Преобразувайте d3 диаграма (svg) в изображение и я покажете

Опитвам се да конвертирам d3 диаграма в png изображение. Прочетох много въпроси за SO, които обясняват как да го направя.

Заснемане на HTML Canvas като gif/jpg/png/pdf?

По принцип опитвам това, което е споменато във връзката, но не мога да го накарам да работи.

<div class="chart" id="chart" style="width: 50%; margin: 0 auto;"></div>

<script src="/js/d3.js"></script>
<script src="/js/canvg.js"></script>
<script src="/js/jquery-2.2.0.js"></script>

<!-- d3 code -->    
<script type="text/javascript">
   ...
   var svg = d3.select(".chart").append("svg").attr("id", "mysvg").attr("width",
                width + margin.left + margin.right).attr("height",
                height + margin.top + margin.bottom);
   ...
</script>

<canvas id="canvas" width="200" height="200"></canvas>
<script type="text/javascript">
    canvg(document.getElementById('canvas'), x);
    var canvas = document.getElementById('canvas');
    var img = canvas.toDataURL('image/png');
    document.write('<img src="'+img+'"/>');
</script>

Както можете да видите, има x като втори аргумент. За x опитах следното:

1) <svg>'+$('#chart').html()+'</svg>
2) $('svg').html()
3) document.getElementById('chart').firstChild
4) document.getElementById('mysvg')
5) $('#chart').html()  (Answer by Mark, working on his example, but not on my example)

Проверих изхода и за случай 1) всъщност има обект платно и обект изображение, но и двата са празни. В останалите случаи има някои изключения, особено нулеви указатели за 3) 4).

Нямам опит с JS, така че може би това е някаква много основна грешка.. Може ли някой да помогне?

редактиране: моят пълен d3-код:

var margin = {
        top : 20,
        right : 20,
        bottom : 30,
        left : 50
    }, width = 960 - margin.left - margin.right, height = 500 - margin.top
            - margin.bottom;


    d3.json("data/measure.json", function(error, data) {
        console.log(data);
        if (error)
            throw error;

        /* defining scales axis */
        var x = d3.scale.linear().domain([ 0, d3.max(data[0].route, function(d) {return d.time; })])
                .range([ 0, width ]);

        var y = d3.scale.linear().domain([ d3.min(data[0].route, function(d) { return d.consume;}), d3.max(data[0].route, function(d) {return d.consume; })])
                .range([ height, 0 ]);

        var xAxis = d3.svg.axis().scale(x).orient("bottom")
                        .tickPadding(10)
                        .innerTickSize(-height)
                        .outerTickSize(0)
                        .ticks(data[0].route.length);


        var yAxis = d3.svg.axis().scale(y).orient("left")
                        .tickPadding(10)
                        .innerTickSize(-width)
                        .outerTickSize(0);

        // painting curves                  
        var line = d3.svg.line().x(function(d,i) { console.log(d.time); return x(d.time);})
                                .y(function(d,i) { return y(d.consume); });

        var foreignCurve = d3.svg.line().x(function(d,i) { return x(d.time); })
                                        .y(function(d,i) { return y(d.consume); })


        //main chart component    
        var svg = d3.select(".chart").append("svg").attr("width",
                width + margin.left + margin.right).attr("height",
                height + margin.top + margin.bottom).append("g").attr(
                "transform",
                "translate(" + margin.left + "," + margin.top + ")");

        svg.append("g").attr("class", "x axis").call(xAxis).attr(
                "transform", "translate(0," + height + ")").append("text")
                .style("text-anchor", "end").attr("x", width).attr(
                        "y", -10).text("time (s)").attr("font-weight", "bold");

        var axisG = svg.append("g");

        axisG.attr("class", "y axis").attr("dx", ".5em").call(yAxis)
                .append("text").attr("transform", "rotate(-90)").attr("y",
                        6).attr("dy", ".71em").style("text-anchor", "end")
                .text("Bandwidth (Kbps)").attr("font-weight","bold");


        svg.append("path").datum(data[0].route).attr("class", "line").attr(
                "d", line);

        svg.append("path").datum(data[0].foreign).attr("class","line2").attr("d", foreignCurve);

        var group = svg.append("g");

        group.append("line").attr("class", "mean-line").attr({
            x1 : x(0),
            y1 : y(data[0].threshold),
            x2 : x(d3.max(data[0].route, function(d) {
                return d.time;
            })),
            y2 : y(data[0].threshold)
        });

        group.append("line").attr("class", "mean-line").attr({
            x1 : x(0),
            y1 : y(data[0].threshold*1.05),
            x2 : x(d3.max(data[0].route, function(d) {
                return d.time;
            })),
            y2 : y(data[0].threshold*1.05)
        });

        group.append("text").style("text-anchor", "end").attr("y",
                y(data[0].threshold*1.05) - 5).attr("x", x(d3.max(data[0].route, function(d) {
            return d.time;
        }))).attr("fill", "gray").text("buffered limit");

        group.append("text").style("text-anchor", "end").attr("y",
                y(data[0].threshold) + 10).attr("x", x(d3.max(data[0].route, function(d) {
            return d.time;
        }))).attr("fill", "gray").text("limit");

html изход (само img таг):

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAAsUlEQVR4nO3BAQEAAACCIP+vbkhAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8GXHmAAFMgHIEAAAAAElFTkSuQmCC">

person lunatikz    schedule 09.04.2016    source източник


Отговори (2)


Имате нужда от html съдържанието на вашата диаграма div включително таговете svg. Така че използвайте:

$('#chart').html()

Ето един пример:

<html>

<head>
  <script data-require="[email protected]" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
  <script data-require="[email protected]" data-semver="2.2.0" src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
  <script src="https://rawgit.com/gabelerner/canvg/master/canvg.js"></script>
</head>

<body>
  <div class="chart" id="chart" style="width: 50%; margin: 0 auto;"></div>
  <!-- d3 code -->
  <script type="text/javascript">
    var svg = d3.select(".chart").append("svg")
    .attr("id", "mysvg")
    .attr("width", 500)
    .attr("height", 500);
    
    svg.selectAll('.bar')
      .data([1,2,3,4])
      .enter()
      .append('rect')
      .attr('y', function(d,i){
        return d * 50;
      })
      .attr('height', function(d,i){
        return 500 - (d * 50);
      })
      .attr('width', 50)
      .attr('x', function(d,i){
        return i * 100;
      })
      .style('fill', 'steelblue');
    
  </script>
  <canvas id="canvas" width="200" height="200"></canvas>
  <script type="text/javascript">
    canvg(document.getElementById('canvas'), $('#chart').html());
    var canvas = document.getElementById('canvas');
    var img = canvas.toDataURL('image/png');
    document.write('<img src="' + img + '"/>');
  </script>
</body>

</html>

person Mark    schedule 09.04.2016
comment
Опитах това, което споменахте, но все още не работи в моя пример. Копирах допълнително целия ви пример в моя проект за webapp.. Работи добре.. Освен това, когато заменя моето d3 съдържание с вашето, то работи.. Ще предоставя своя d3 код във въпроса си. Може би проблемът е свързан с моя d3-код? - person lunatikz; 09.04.2016
comment
Отстраних малко грешки и се оказа, че в моя пример $('#chart').html() връща празен низ?! Във вашия пример той работи както се очаква.. - person lunatikz; 09.04.2016
comment
@lunatikz, ако вашият canvg код във вашето обратно извикване на AJAX? Моето предположение е не и кодът canvg се изпълнява преди вашето обратно извикване на AJAX. - person Mark; 09.04.2016
comment
Добре, благодаря, вече работи. Не знаех за асинхронните функции на JS. Поне получавам изображение, което може да се запази. Но самото изображение е някак развалено. Но това ще е друг въпрос. Прието - person lunatikz; 09.04.2016

този процес се нарича растеризация, така че конвертирането на векторна графика в растерна графика, т.е. svg (векторна графика) в png (растерна графика) https://en.wikipedia.org/wiki/Rasterisation

Това е по-лесен начин да направите това (но изисква използването на npm, така че npm, за да работи, изисква Node.js): трябва да инсталирате gulp-svg2png и да го добавите към вашия gulpfile.js

Първо инсталирайте gulp-svg2png като зависимост за разработка:

npm install --save-dev gulp-svg2png

След това го добавете към вашия gulpfile.js:

var svg2png = require('gulp-svg2png');

gulp.task('svg2png', function () {
    gulp.src('./specs/assets/**/*.svg')
        .pipe(svg2png())
        .pipe(gulp.dest('./build'));
});

затова опитайте този плъгин https://www.npmjs.com/package/gulp-svg2png

person grzesiekmq    schedule 09.04.2016