uniquejava / blog

My notes regarding the vibrating frontend :boom and the plain old java :rofl.
Creative Commons Zero v1.0 Universal
11 stars 5 forks source link

d3.js #74

Open uniquejava opened 7 years ago

uniquejava commented 7 years ago

book: D3.js in Action

Three Little Circles Let’s Make a Bar Chart Thinking with Joins Setting Scales Domains and Ranges in d3.js

Fun with Circles in D3

https://leanpub.com/D3-Tips-and-Tricks/read#leanpub-auto-d3-javascript

http://www.ourd3js.com/wordpress/865

  1. 英文资料

(1)官方 API

https://github.com/mbostock/d3/wiki/API-Reference

阅读 API 学习是一个不坏的方法。虽然有不少人说 D3 的 API 写得太学术性了,不好懂,但是真要想得心应手地使用 D3,此 API 是避不开的。

(2)Mike Bostock 的博客和作品展示板

http://bost.ocks.org/mike/

http://bl.ocks.org/mbostock/

这是由 D3 的作者制作的,Mike Bostock 是纽约时报的工程师,纽约时报目前的可视化技术是使用 D3 的。但是,这个博客和作品展示板里,文字说明不多,大多数只有代码,理解起来挺困难。

(3)Dashing D3.js

https://www.dashingd3js.com/table-of-contents

非常简单易懂的教程,文字解释、图片都十分清晰。此站开设的目的就是为了让人迅速而高效地掌握 D3。

(4)Custom Visualizations with D3.js

http://jsdatav.is/chap07.html

一本书的其中一章,但是内容很丰富,图形也很简洁精美。另外,该站都是关于数据可视化的文章,绝对值得一看。

(5)INTRO TO D3.JS

http://square.github.io/intro-to-d3/

排版很有意思,简单易懂的一个学习站。

(6)D3.js Gallery

http://christopheviau.com/d3list/gallery.html

汇集了网络上 2490 个 D3.js 的例子,是一个巨大的可视化宝库。

  1. 中文资料

    (1)很早的一个站

http://www.pkuwwt.tk/d3-tutorial-cn/about.html

更新时间显示的是2012年,可以说非常非常早,要知道 D3 是2011年诞生,2012年12月版本才升级到3.0(现在常用的)。

(2)张天旭的博客

http://blog.csdn.net/tianxuzhang?viewmode=contents

张天旭的 CSDN 博客,其 D3 的系列教程有超过 10 万的访问量,人气相当高。

(3)阮一峰的博客

http://javascript.ruanyifeng.com/library/d3.html

阮一峰的写作风格是简单易懂。但是可惜,关于 D3 的只有一篇。

(4)楚狂人的博客

http://www.cnblogs.com/winleisure/tag/D3.js/

楚狂人的博客,翻译的 Dashing D3.js,翻译的质量很好。

https://schoolofdata.org/2013/10/01/pie-and-donut-charts-in-d3-js/ http://ddgreat.com/d3-svg-line-function-problem/

d3-tip demo: http://bl.ocks.org/Caged/6476579

http://nesterko.com/blog/2012/01/30/measuring-homophily-in-network-data-and-how-to-export-from-d3-js-to-pdf/

uniquejava commented 7 years ago

pdf

http://stackoverflow.com/questions/11567668/svg-to-canvas-with-d3-js

http://blog.csdn.net/wyaspnet/article/details/52095157

https://yq.aliyun.com/wenzhang/show_38978

https://github.com/niklasvh/html2canvas/issues/95

v1

$scope.exportAsPDF = function () {

        var svgs = document.querySelectorAll('svg');
        for (var i = 0; i < svgs.length; i++) {
            var $svg = $(svgs[i]);
            var $svgp = $svg.parent();
            var serializer = new XMLSerializer();
            var svgString = serializer.serializeToString($svg.get(0));

            var myCanvas = document.createElement('canvas');
            myCanvas.width = $svgp.width();
            myCanvas.height = $svgp.height();
            $svgp.append(myCanvas);
            canvg(myCanvas, svgString);
            $svg.hide();
        }

        html2canvas(document.getElementById('analytics-content'), {
            onrendered: function (canvas) {

                var data = canvas.toDataURL();
                var docDefinition = {
                    content: [{
                        image: data,
                        width: 500,
                    }]
                };
                pdfMake.createPdf(docDefinition).download("canvs.pdf");
                $("canvas").remove();
                $("svg").show();
            }
        });

    };
uniquejava commented 7 years ago

http://codepen.io/wadeharrell/pen/iebdw

v2

var svgs = document.getElementsByTagName('svg');
        for (var i = 0; i < svgs.length; i++) {
            var $svg = $(svgs[i]);
            var $svgp = $svg.parent();
            var serializer = new XMLSerializer();
            var svgString = serializer.serializeToString($svg.get(0));

            /*
            var myCanvas = document.createElement('canvas');
            myCanvas.width = $svgp.width();
            myCanvas.height = $svgp.height();
            $svgp.append(myCanvas);
            canvg(myCanvas, svgString);
            $svg.hide();
            */
            var canvasId = "c" + i;
            var myCanvas = document.createElement('canvas');
            myCanvas.width = $svgp.width();
            myCanvas.height = $svgp.height();
            myCanvas.id = canvasId;
            $svgp.append(myCanvas);

            var f = new fabric.Canvas(canvasId);
            var path = fabric.loadSVGFromString(svgString,function(objects, options) {
                var obj = fabric.util.groupSVGElements(objects, options);
                f.add(obj).renderAll();
            });

            $svg.hide();
        }
uniquejava commented 7 years ago

v3

$scope.generatePDF = function () {

      // get styles from all required stylesheets
      // http://www.coffeegnome.net/converting-svg-to-png-with-canvg/
      var style = "\n";
      var requiredSheets = ['chart.css', 'lineChart.css', 'donutChart.css', 'application.css', 'material-icons.css']; // list of required CSS
      for (var i = 0; i < document.styleSheets.length; i++) {
        var sheet = document.styleSheets[i];
        if (sheet.href) {
          var sheetName = sheet.href.split('/').pop();
          if (requiredSheets.indexOf(sheetName) != -1) {
            var rules = sheet.rules || sheet.cssRules;
            if (rules) {
              console.log("append new rules:", rules.length, sheetName);
              for (var j = 0; j < rules.length; j++) {
                var css = rules[j].cssText;
                if (css.startsWith('@font-face')) {
                  if (css.indexOf("url(\"MaterialIcons") != -1) {
                    var url = sheet.href;
                    console.log(url);
                    url = url.substring(0, url.lastIndexOf('/') + 1);
                    css = css.replace(/url\("/gi, "url(\"" + url);
                    console.log(css);
                  }

                }
                style += (css + '\n');
              }
            }
          }
        }
      }

      d3.selectAll('svg').each(function () {
        var node = this,
          svg = d3.select(node),
          serializer = new XMLSerializer(),
          width = node.getBBox().width,
          height = node.getBBox().height;

        // prepend style to svg
        svg.insert('defs', ":first-child");
        svg.select("defs")
          .append('style')
          .attr('type', 'text/css')
          .html(style);
        var svgStr = serializer.serializeToString(node);

        var myCanvas = document.createElement('canvas');
        myCanvas.width = width;
        myCanvas.height = height;
        $(node).parent().append(myCanvas);
        canvg(myCanvas, svgStr);
        $(node).hide();
      });

      html2canvas(document.getElementById('analytics-content'), {
        onrendered: function (canvas) {

          var data = canvas.toDataURL();
          var docDefinition = {
            content: [{
              image: data,
              width: 500,
            }]
          };
          pdfMake.createPdf(docDefinition).download("output.pdf");
          $("canvas").remove();
          $("svg").show();
        }
      });
    };
uniquejava commented 7 years ago

v4

$scope.generatePDF = function () {

  // get required stylesheets for svg and embed the style into svg itself.
  // see http://www.coffeegnome.net/converting-svg-to-png-with-canvg/
  var style = "\n";
  var requiredSheets = ['application.css', 'chart.css', 'lineChart.css', 'donutChart.css', 'material-icons.css']; // list of required CSS
  for (var i = 0; i < document.styleSheets.length; i++) {
    var sheet = document.styleSheets[i];
    if (sheet.href) {
      var sheetName = sheet.href.split('/').pop();
      if (requiredSheets.indexOf(sheetName) != -1) {
        var rules = sheet.rules || sheet.cssRules;
        if (rules) {
          for (var j = 0; j < rules.length; j++) {
            var css = rules[j].cssText;
            if (css.startsWith('@font-face')) {
              if (css.indexOf("url(\"MaterialIcons") != -1) {
                var url = sheet.href;
                console.log(url);
                url = url.substring(0, url.lastIndexOf('/') + 1);
                css = css.replace(/url\("/gi, "url(\"" + url);
                console.log(css);
              }

            }
            style += (css + '\n');
          }
        }
      }
    }
  }

  // style += '.tick line{stroke: lightgray !important}\n';

  d3.selectAll('svg').each(function () {
    var node = this,
      svg = d3.select(node),
      serializer = new XMLSerializer(),
      width = node.getBBox().width,
      height = node.getBBox().height;

    var pClasses = $(node).parent().attr('class').split(' ');
    _.each(pClasses, function (clz) {
      if (clz !== 'chart') {
        $(node).toggleClass(clz, true);
      }
    });

    // prepend style to svg
    svg.insert('defs', ":first-child");
    svg.select("defs")
      .append('style')
      .attr('type', 'text/css')
      .html(style);

    var myCanvas = document.createElement('canvas');
    myCanvas.width = width;
    myCanvas.height = height;
    $(node).parent().append(myCanvas);
    canvg(myCanvas, serializer.serializeToString(node));

    var pClasses = $(node).parent().attr('class').split(' ');
    _.each(pClasses, function (clz) {
      $(node).toggleClass(clz, false);
    });

    $(node).hide();
  });

  Html5Page2Canvas();
};

function Html5Page2Canvas() {
  html2canvas(document.getElementById('analytics-content'), {
    onrendered: function (canvas) {
      var data = canvas.toDataURL();
      var docDefinition = {
        content: [{
          image: data,
          width: 500
        }]
      };
      pdfMake.createPdf(docDefinition).download("output.pdf");
      $("canvas").remove();
      $("svg").show();
    }
  });
}
uniquejava commented 7 years ago

v5

function getRequiredStyles4Svg() {
  // get required stylesheets for svg and embed the style into svg itself.
  // see http://www.coffeegnome.net/converting-svg-to-png-with-canvg/
  var style = "\n";
  // list of required CSS
  var requiredSheets = ['application.css', 'chart.css', 'lineChart.css', 'donutChart.css'];
  for (var i = 0; i < document.styleSheets.length; i++) {
    var sheet = document.styleSheets[i];
    if (sheet.href) {
      var sheetName = sheet.href.split('/').pop();
      if (requiredSheets.indexOf(sheetName) != -1) {
        var rules = sheet.rules || sheet.cssRules;
        if (rules) {
          for (var j = 0; j < rules.length; j++) {
            var css = rules[j].cssText;
            style += (css + '\n');
          }
        }
      }
    }
  }

  //fix grid line styles
  style += '.axis .tick line, .x .tick line, .y .tick line{strike: lightgray !important; stroke-width: 1px !important}\n';
  return style;
}

$scope.generatePDF = function () {

  var style = getRequiredStyles4Svg();

  d3.selectAll('svg').each(function () {
    var node = this,
      svg = d3.select(node),
      serializer = new XMLSerializer(),
      width = node.getBBox().width,
      height = node.getBBox().height;

    // fix stylesheet context issue: copy svg parent's classes onto svg itself
    var pClasses = $(node).parent().attr('class').split(' ');
    _.each(pClasses, function (clz) {
      if (clz !== 'chart') { // grid line will be disappeared if `chart` is added
        $(node).toggleClass(clz, true);
      }
    });

    // add required styles to svg
    svg.insert('defs', ":first-child");
    svg.select("defs")
      .append('style')
      .attr('type', 'text/css')
      .html(style);

    // convert svg to canvas
    var myCanvas = document.createElement('canvas');
    myCanvas.width = width;
    myCanvas.height = height;
    $(node).parent().append(myCanvas);
    canvg(myCanvas, serializer.serializeToString(node));

    // hide svg temporarily
    $(node).hide();
  });

  // convert whole html5 page to canvas and export canvas as pdf.
  Html5Page2Canvas('analytics-content', document.title + '.pdf');
};

function Html5Page2Canvas(root, fileName) {

  html2canvas(document.getElementById(root), {
    onrendered: function (canvas) {
      var data = canvas.toDataURL();
      var docDefinition = {
        content: [{
          image: data,
          width: 500
        }]
      };
      pdfMake.createPdf(docDefinition).download(fileName);

      // clean up
      $("canvas").remove();
      $("svg defs").remove();
      $("svg").attr('class', '');
      $("svg").show();
    }
  });
}
uniquejava commented 6 years ago

d3 and vue.js

How to implement D3 for Vue.js

Composing D3 Visualizations With Vue.js

video

https://www.safaribooksonline.com/videos/creating-data-visualization/9781789344820/9781789344820-video1_5

book

https://www.safaribooksonline.com/library/view/d3js-4x-data/9781787120358/

svg gotchas

You can write SVG markup and embed it directly in a web page (provided you use <!DOCTYPE html>). You can inspect SVG elements in your browser’s developer tools. And SVG elements can be styled with CSS, albeit using different property names like fill instead of background-color. However, unlike HTML, SVG elements must be positioned relative to the top-left corner of the container; SVG does not support flow layout or even text wrapping.

<!DOCTYPE html>
<style>

.chart rect {
  fill: steelblue;
}

.chart text {
  fill: white;
  font: 10px sans-serif;
  text-anchor: end;
}

</style>
<svg class="chart" width="420" height="120">
  <g transform="translate(0,0)">
    <rect width="40" height="19"></rect>
    <text x="37" y="9.5" dy=".35em">4</text>
  </g>
  <g transform="translate(0,20)">
    <rect width="80" height="19"></rect>
    <text x="77" y="9.5" dy=".35em">8</text>
  </g>

SVG requires text to be placed explicitly in text elements. Since text elements do not support padding or margins, the text position must be offset by three pixels from the end of the bar, while the dy offset is used to center the text vertically.

svg styling properties

http://www.w3.org/TR/SVG/styling.html

uniquejava commented 6 years ago

selector

select/selectAll/attr/style/property

data join

data(..)返回的是需要update的selection enter(..)是没有element的data(OR 需要新建的element) exit(..)是没有data的element(OR 需要移除的element)

套路: selectAll + data + enter + append, 示例:

var circle = svg.selectAll("circle")
    .data([32, 57, 293], function(d) { return d; });

circle.enter().append("circle")
    .attr("cy", 60)
    .attr("cx", function(d, i) { return i * 100 + 30; })
    .attr("r", function(d) { return Math.sqrt(d); });

circle.exit().remove();

需要注意的是append比较特殊返回的是新selection.

d3.selectAll("section")
    .attr("class", "special")
  .append("div")
    .html("Hello, world!");

var section = d3.selectAll("section");

section.append("div")
    .html("First!");

section.append("div")
    .html("Second.");

scale

var x = d3.scale.linear()
    .domain([0, d3.max(data)])
    .range([0, 420]);
d3.select(".chart")
  .selectAll("div")
    .data(data)
  .enter().append("div")
    .style("width", function(d) { return x(d) + "px"; })
    .text(function(d) { return d; });