d3 + geojson in node 1 d3+jsdom实现后端渲染svg 2. 按需分模块导入d3 子module 3 一次为selection添加多个attr 4 绑定geojson格式数据 5 d3选择集上消失的update()方法,改用merge()
d3.js本来主要是用于用“数据驱动dom”,在浏览器端,接收后端数据,数据绑定,渲染出svg。
即使是在ng中用,也是会由框架打包,供客户端下载。
那么,如果用所谓后端渲染,发布静态的svg,那就要在node里用d3。
几个遇到的点:
node和前端的区别,就是没有全局window.document 这个对象。d3选择器无从选起。
1 创建jsdom对象
const { JSDOM } = require("jsdom"); const my_jsdom = new JSDOM( ` <html> <head> <link rel="stylesheet" type="text/css" href="style.css"> </head> <body> </body> </html> `, { resources: 'usable' } );
反引号中是一个空白网页模板,用过flask的jinja2 和ng的 templates的不会陌生,模拟浏览器里的window.document全局对象。
2 导入d3模块,并让d3获得这个模拟的网页。
//按需导入d3 var d3 = Object.assign({}, require("d3-selection"), require("d3-selection-multi"), require("d3-geo")); //导入模拟的geojson数据 import {default as gdf} from './mock_polygon_gdf.json'; //让d3绑定到模拟的网页 const d3n = d3.select(my_jsdom.window.document); const svg = d3n.select('body') .insert('svg','records') .attr('width', 2400) .attr('height', 3000); .append('g') .selectAll('path') .data(gdf['features']) .enter() .append('path') .attr("d", d3.geoPath()
写这么长,是想说明,注意绑定当前网页后的对象命名为d3n, 不要和开始导入的d3库混淆。
在浏览器里,d3是通过html里写另外一个<script>引入d3.min.js 直接都导入好了,而且是直接d3.select('body')的。
在node这里加了一步,而且后面主要是用d3n来做后面的工作。
前面导入的d3库对象,也是有用的。比如后面的d3.geoPath()方法
经过这样的d3一顿绑定,svg画好了。还差最后一步:
3把网页输出成string,保存到.html文件
const content = my_jsdom.serialize(); fs.writeFile('${fpath}/${fname}.html', content, { encoding: "utf8", flag: "w" }, (err) =>{
if (err) {
throw err;
}
});
2. 按需分模块导入d3 子module
d3发展到v4 v5之后,功能膨胀,体积变大,除了集中发布的d3 还分离成若干独立子组件,可以按需加载。
而且,有些子组件并没有打包到d3中,必须自己手动安装,按需加载
根据d3官网,node中按需加载是这样的:https://github.com/d3/d3/blob/master/README.md
In Node:
var d3 = require("d3");
You can also require individual modules and combine them into a
d3
object using Object.assign:
var d3 = Object.assign({}, require("d3-format"), require("d3-geo"), require("d3-geo-projection"));
用Object.assign() 打包成一个d3对象
3 一次为selection添加多个attr
特别是不知道attr具体名字,但attr的{name:value}都在需要绑定的数据中的时候
要用到独立于d3的子组件d3-selection-multi
按上面的方式,分组导入(我只用到3个,d3-selection必选,d3-geo是为了绑定geojson)
var d3 = Object.assign({}, require("d3-selection"), require("d3-selection-multi"), require("d3-geo"));
这样就可以:
const enter = g.selectAll('path') .data(gjson['features']) .enter() .append('path') .attrs( (d)=> Object.assign({id: (d) => d['id']}, d['properties']) );
4 绑定geojson格式数据
const enter = g.selectAll('path') .data(gjson['features']) .enter() .append('path') .attr("d", d3.geoPath());
通过数据绑定,enter新增数据,添加path,直接一句搞定。
5 d3选择集上消失的update()方法,改用merge()
给出的答案是用merge,确实更优雅了
把enter和update合并处理了
// Perform the data join and obtain the update selection. const option = d3.select("#mySelect") .selectAll("option") .data(optionsData); // Append the entering nodes, and obtain the enter selection. const enter = option.enter().append("option"); // Merge entering and updating nodes to apply some operations to both. enter.merge(option) .property("value", d => d.value) .text(d => d.label); // Remove the exiting nodes. option.exit().remove();