【问题标题】:Export SVG as PNG using d3js-wordcloud Angular Directive使用 d3js-wordcloud Angular Directive 将 SVG 导出为 PNG
【发布时间】:2017-06-27 13:53:24
【问题描述】:

我想实现一个简单的下载/导出功能,它将渲染的 svg 词云转换并保存为我的 Angular 应用程序中的 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 不兼容。

【问题讨论】:

    标签: javascript angularjs d3.js svg


    【解决方案1】:

    感谢以下帖子,我能够解决我的问题:

    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>
    

    我希望这对其他人有帮助!

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

      • 感谢您的回复!我很感激帮助。你会看到我的指令模板实际上是我的指令的一部分。我没有外部模板文件。 template: '&lt;div id="wordCloud"&gt;&lt;button class="basicButton" ng-click="exportToPNG()"&gt;Export to .PNG&lt;/button&gt;&lt;div id="wordCloudVisualisation"&gt;&lt;/div&gt;&lt;canvas id="canvas"&gt;&lt;/canvas&gt;&lt;/div&gt;'你能给我一些关于添加缺失标签的建议吗?
      • 我为我的指令创建了一个外部模板。请查看更新后的问题。
      猜你喜欢
      • 2023-04-05
      • 1970-01-01
      • 2013-07-01
      • 2015-05-03
      • 1970-01-01
      • 2017-02-28
      • 2022-01-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多