使用d3js-wordcloud Angular Directive将SVG导出为PNG

时间:2017-02-09 13:59:12

标签: javascript angularjs d3.js svg

我想实现一个简单的下载/导出功能,它将在我的Angular应用程序中将渲染的svg词云转换并保存为png。

我正在使用由Jason Davies完成的d3字云生成器和Julien Renaux的简化脚本。

我正在尝试添加一个简单的导出功能,使用iweczek的save-svg-as-an-image export function,但某处我错过了一些东西。

这是我的Angular WordCloud指令,包括底部的导出功能(scope.exportToPNG):

'use strict';

/**
 * @ngdoc: function
 * @name: portalDashboardApp.directive:ogWordCloud
 * @description: Word Cloud Generator based on the d3 word cloud generator done by Jason Davies and a simplified script by Julien Renaux
 * Directive of the portalDashboardApp
 */

angular.module('portalDashboardApp')
  .directive('ogWordCloud', function () {
      return {
          restrict: 'E',
          replace: true,
          templateUrl: './socialMedia.module/socialMedia.templates/WordCloudTemplate.html',
          scope: {
              words: '='
          },
          link: function (scope) {

              var fill = d3.scale.category20b();

              var w = window.innerWidth - 238,
                h = 400;

              var max,
                fontSize;

              var layout = d3.layout.cloud()
                .timeInterval(Infinity)
                .size([w, h])
                .fontSize(function (d) {
                    return fontSize(+d.value);
                })
                .text(function (d) {
                    return d.key;
                })
                .on("end", draw);

              var svg = d3.select("#wordCloudVisualisation").append("svg")
                .attr("width", w)
                .attr("height", h)
                .attr("xmlns", 'http://www.w3.org/2000/svg')
                .attr("xmlns:xlink", 'http://www.w3.org/1999/xlink')
                .attr("version", '1.1')
                .attr("id", "wordCloudSVG");

              var wordCloudVisualisation = svg.append("g").attr("transform", "translate(" + [w >> 1, h >> 1] + ")");

              update();

              window.onresize = function (event) {
                  update();
              };

              var tags = [];

              scope.$watch('words', function () {
                  tags = scope.words;
              }, true);

              function draw(data, bounds) {
                  var w = window.innerWidth - 238,
                    h = 400;

                  svg.attr("width", w).attr("height", h);

                  var scale = bounds ? Math.min(
                    w / Math.abs(bounds[1].x - w / 2),
                    w / Math.abs(bounds[0].x - w / 2),
                    h / Math.abs(bounds[1].y - h / 2),
                    h / Math.abs(bounds[0].y - h / 2)) / 2 : 1;

                  var text = wordCloudVisualisation.selectAll("text")
                    .data(data, function (d) {
                        return d.text.toLowerCase();
                    });
                  text.transition()
                    .duration(1000)
                    .attr("transform", function (d) {
                        return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
                    })
                    .style("font-size", function (d) {
                        return d.size + "px";
                    });
                  text.enter().append("text")
                    .attr("text-anchor", "middle")
                    .attr("transform", function (d) {
                        return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
                    })
                    .style("font-size", function (d) {
                        return d.size + "px";
                    })
                    .style("opacity", 1e-6)
                    .transition()
                    .duration(1000)
                    .style("opacity", 1);
                  text.style("font-family", function (d) {
                      return d.font;
                  })
                    .style("fill", function (d) {
                        return fill(d.text.toLowerCase());
                    })
                    .text(function (d) {
                        return d.text;
                    });

                  wordCloudVisualisation.transition().attr("transform", "translate(" + [w >> 1, h >> 1] + ")scale(" + scale + ")");
              }

              function update() {
                  layout.font('impact').spiral('archimedean');
                  fontSize = d3.scale['sqrt']().range([10, 100]);
                  if (scope.words.length) {
                      fontSize.domain([+scope.words[scope.words.length - 1].value || 1, +scope.words[0].value]);
                  }
                  layout.stop().words(scope.words).start();
              }

//////////////////////////////////////////////////////////////////

              scope.exportToPNG = function () {

                  var html = d3.select("svg") //svg
                        .attr("version", 1.1)
                        .attr("xmlns", "http://www.w3.org/2000/svg")
                        .node().parentNode.innerHTML;

                  var imgsrc = 'data:image/svg+xml;base64,' + btoa(html);
                  var img = '<img src="' + imgsrc + '">';
                  d3.select("#svgdataurl").html(img);


                  var canvas = document.querySelector("canvas"),
                      context = canvas.getContext("2d");

                  var image = new Image;
                  image.src = imgsrc;
                  image.onload = function () {
                      context.drawImage(image, 0, 0);

                      var canvasdata = canvas.toDataURL("image/png");

                      var pngimg = '<img src="' + canvasdata + '">';
                      d3.select("#pngdataurl").html(pngimg);

                      var a = document.createElement("a");
                      a.download = "sample.png";
                      a.href = canvasdata;
                      a.click();
                  };
              }
          }
      };
  });

这是我的指示模板:

<div id="wordCloud">
    <button class="basicButton" ng-click="exportToPNG()">Export to .PNG</button>
    <div id="wordCloudVisualisation"></div>
    <canvas id="canvas"></canvas>
    <h2>svgdataurl</h2>
    <div id="svgdataurl"></div>
    <h2>pngdataurl</h2>
    <div id="pngdataurl"></div>
</div>

按原样,代码会在Chrome中生成损坏的图像(例如,当网站上的占位符中缺少图像时)。在IE中,它在我的“svgdataurl”中创建了一个图像,但却炸毁了。我认为它与IE不兼容。

2 个答案:

答案 0 :(得分:2)

由于以下帖子,我能够解决我的问题:

Save inline SVG as JPEG/PNG/SVG

https://gist.github.com/gustavohenke/9073132

以下是我的完整解决方案:

我的Angular指令:

'use strict';

/**
 * @ngdoc: function
 * @name: portalDashboardApp.directive:ogWordCloud
 * @description: Word Cloud Generator based on the d3 word cloud generator done by Jason Davies and a simplified script by Julien Renaux
 * Directive of the portalDashboardApp
 */

angular.module('portalDashboardApp')
  .directive('ogWordCloud', function () {
      return {
          restrict: 'E',
          replace: true,
          templateUrl: './socialMedia.module/socialMedia.templates/WordCloudTemplate.html',
          scope: {
              words: '='
          },
          link: function (scope) {

              var fill = d3.scale.category20b();

              var w = window.innerWidth - 238,
                h = 400;

              var max,
                fontSize;

              var layout = d3.layout.cloud()
                .timeInterval(Infinity)
                .size([w, h])
                .fontSize(function (d) {
                    return fontSize(+d.value);
                })
                .text(function (d) {
                    return d.key;
                })
                .on("end", draw);

              var svg = d3.select("#wordCloudVisualisation").append("svg")
                .attr("width", w)
                .attr("height", h)
                .attr("xmlns", 'http://www.w3.org/2000/svg')
                .attr("xmlns:xlink", 'http://www.w3.org/1999/xlink')
                .attr("version", '1.1')
                .attr("id", "wordCloudSVG");

              var wordCloudVisualisation = svg.append("g").attr("transform", "translate(" + [w >> 1, h >> 1] + ")");

              update();

              window.onresize = function (event) {
                  update();
              };

              var tags = [];

              scope.$watch('words', function () {
                  tags = scope.words;
              }, true);

              function draw(data, bounds) {
                  var w = window.innerWidth - 238,
                    h = 400;

                  svg.attr("width", w).attr("height", h);

                  var scale = bounds ? Math.min(
                    w / Math.abs(bounds[1].x - w / 2),
                    w / Math.abs(bounds[0].x - w / 2),
                    h / Math.abs(bounds[1].y - h / 2),
                    h / Math.abs(bounds[0].y - h / 2)) / 2 : 1;

                  var text = wordCloudVisualisation.selectAll("text")
                    .data(data, function (d) {
                        return d.text.toLowerCase();
                    });
                  text.transition()
                    .duration(1000)
                    .attr("transform", function (d) {
                        return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
                    })
                    .style("font-size", function (d) {
                        return d.size + "px";
                    });
                  text.enter().append("text")
                    .attr("text-anchor", "middle")
                    .attr("transform", function (d) {
                        return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
                    })
                    .style("font-size", function (d) {
                        return d.size + "px";
                    })
                    .style("opacity", 1e-6)
                    .transition()
                    .duration(1000)
                    .style("opacity", 1);
                  text.style("font-family", function (d) {
                      return d.font;
                  })
                    .style("fill", function (d) {
                        return fill(d.text.toLowerCase());
                    })
                    .text(function (d) {
                        return d.text;
                    });

                  wordCloudVisualisation.transition().attr("transform", "translate(" + [w >> 1, h >> 1] + ")scale(" + scale + ")");
              }

              function update() {
                  layout.font('impact').spiral('archimedean');
                  fontSize = d3.scale['sqrt']().range([10, 100]);
                  if (scope.words.length) {
                      fontSize.domain([+scope.words[scope.words.length - 1].value || 1, +scope.words[0].value]);
                  }
                  layout.stop().words(scope.words).start();
              }

              scope.exportToPNG2 = function () {

                  var svg = document.querySelector('#wordCloudSVG'); //svg
                  var canvas = document.createElement("canvas");

                  var svgSize = svg.getBoundingClientRect();
                  canvas.width = svgSize.width;
                  canvas.height = svgSize.height;

                  var ctx = canvas.getContext('2d');
                  var data = new XMLSerializer().serializeToString(svg);

                  var DOMURL = window.URL || window.webkitURL || window;

                  var img = new Image();
                  var svgBlob = new Blob([data], { type: 'image/svg+xml;charset=utf-8' });
                  var url = DOMURL.createObjectURL(svgBlob);

                  img.onload = function () {
                      ctx.drawImage(img, 0, 0);
                      DOMURL.revokeObjectURL(url);

                      var imgURI = canvas
                          .toDataURL('image/png')
                          .replace('image/png', 'image/octet-stream');

                      triggerDownload(imgURI);
                  };

                  img.src = url;
              }

              function triggerDownload(imgURI) {
                  var evt = new MouseEvent('click', {
                      view: window,
                      bubbles: false,
                      cancelable: true
                  });

                  var a = document.createElement('a');
                  a.setAttribute('download', 'MY_COOL_IMAGE.png');
                  a.setAttribute('href', imgURI);
                  a.setAttribute('target', '_blank');

                  a.dispatchEvent(evt);
              }

          }
      };
  });

我的指示模板:

 <div id="wordCloud">
    <button class="basicButton" ng-click="exportToPNG2()">Export to .PNG</button>
    <div id="wordCloudVisualisation"></div>
    <canvas id="WordCloudCanvas"></canvas>
</div>

我希望这有助于其他人!

答案 1 :(得分:0)

你的指令外是否存在div #svgdataurl#pngdataurl?如果没有,您应该将它们包含在指令模板中。在您关注的示例中,它们已经出现在页面上,而不是由click函数创建。

您还可以查看刚刚发布的类似工具Mike Bostock,该工具使用canvas.toBlob()https://github.com/mbostock/svjimmy/blob/master/index.js