D3 中的自定义形状、路径及可视化导出
1. D3 支持的符号
D3 提供了一系列标准符号,可用于可视化。查看 d3.shape API 能了解所有不同的标准形状,以下是 D3 自带的标准符号列表:
| 符号 | 描述 |
| — | — |
| d3.symbolCross | 十字符号或加号(+)符号 |
| d3.symbolCircle | 简单的圆形 |
| d3.symbolDiamond | 扑克牌中的菱形符号 |
| d3.symbolSquare | 简单的正方形 |
| d3.symbolStar | 五角星 |
| d3.symbolTriangle | 简单的三角形,顶点朝上 |
| d3.symbolWye | Y 符号 |
每个符号都有一个大小函数,可用于设置符号大小,默认大小为 64。以下是渲染这些符号的 JavaScript 代码:
var symbols = [
{name: 'Cross', symbol: d3.symbolCross},
{name: 'Circle', symbol: d3.symbolCircle},
{name: 'Diamond', symbol: d3.symbolDiamond},
{name: 'Square', symbol: d3.symbolSquare},
{name: 'Star', symbol: d3.symbolStar},
{name: 'Triangle', symbol: d3.symbolTriangle},
{name: 'Wye', symbol: d3.symbolWye}
];
var color = d3.scaleOrdinal()
.domain(symbols.map(function(s) {return s.name}))
.range(d3.schemeCategory10);
var xBand = d3.scaleBand()
.domain(symbols.map(function(s) {return s.name}))
.range([0, width])
.paddingInner(0.1);
var symbolGroups = svg.selectAll(".symbol").data(symbols)
.enter()
.append("g")
.attr("class","symbol")
.attr("transform", function(d) {
return "translate(" + xBand(d.name) + " 40)"
});
symbolGroups.append("path")
.attr("fill", function(d) {return color(d.name)})
.attr("d", function(d) {
return d3.symbol()
.size(2500) // specifies area
.type(d.symbol)();
});
需要注意的是,符号的大小不是以像素为单位,而是指定符号占用的面积。在上述代码中,大小约为 50x50。
由于这些是基本的 SVG 形状,还可以使用过渡效果对其进行动画处理。以下是旋转符号的代码:
redraw();
function redraw() {
// select currently drawn symbols
var symbolGroups = svg.selectAll(".symbol").data(symbols);
// for newly added elements add the symbol and
var newSymbols = symbolGroups.enter()
.append("g")
.attr("class","symbol")
.attr("transform", function(d) {
return "translate(" + xBand(d.name)
+ " 40) rotate(0)"
});
// add the symbol to the group
newSymbols.append("path")
.attr("fill", function(d) {return color(d.name)})
.attr("d", function(d) {
return d3.symbol()
.size(2400) // specifies area
.type(d.symbol)();
})
// merge with existing ones, and set up a transition.
symbolGroups.merge(newSymbols)
.transition()
.on('end', function(d) {
if (d.name === 'Wye' ) redraw(); // only redraw once
})
.duration(2000)
.ease(d3.easeLinear)
.attrTween("transform", function(d, i) {
var interpolate = d3.interpolate(0, 360);
return function(t) {
return "translate(" + xBand(d.name) + " 40) rotate(" + interpolate(t) + ")";
};
})
}
2. 使用 d3.path 绘制路径
2.1 使用路径 API 绘制
直接使用 SVG 创建路径相当复杂,例如:
<path d="
M 213.1,6.7
c -32.4-14.4-73.7,0-88.1,30.6
C 110.6,4.9,67.5-9.5,36.9,6.7
C 2.8,22.9-13.4,62.4,13.5,110.9
C 33.3,145.1,67.5,170.3,125,217
c 59.3-46.7,93.5-71.9,111.5-106.1
C 263.4,64.2,247.2,22.9,213.1,6.7
z" />
D3 通过路径生成器隐藏了大部分复杂性。通过 d3.path 函数,可以直接定义路径。首先需要创建一个 d3.path 实例:
var path = d3.path();
然后可以使用以下函数绘制路径:
| 函数 | 描述 |
| — | — |
| moveTo(x,y) | 移动到由 x 和 y 坐标指定的特定点 |
| closePath() | 通过绘制一条线到第一个指定点来关闭路径 |
| lineTo(x,y) | 从当前位置绘制一条线到由 x 和 y 坐标指定的位置 |
| quadraticCurveTo(cpx, cpy, x, y) | 使用由 cpx 和 cpy 指定的控制点绘制二次曲线到指定的 x 和 y 坐标 |
| bezierCurveTo(cpx1, cpy1, cxp2, cpx2, x, y) | 使用由 cpx1、cpy1、cxp2 和 cpx2 坐标指定的控制点绘制贝塞尔曲线到指定的 x 和 y 坐标 |
| arcTo(x1, y1, x2, y2, radius) | 按照 HTML5 画布规范的 arcTo 函数绘制圆弧段 |
| arc(x, y, r start, end, anticlockwise) | 按照 HTML5 画布规范的 arc 函数绘制圆弧段 |
| rect(x, y, w, h) | 使用 x、y、w 和 h 变量绘制矩形 |
| toString() | 将当前路径转换为字符串表示形式,可分配给 SVG 路径的 d 属性 |
以下是一个简单的 D3 示例,展示了这些函数的使用:
var cp1 = {x: 230, y: 200};
var cp2 = {x: 290, y: 10};
var cp3 = {x: 300, y: 200};
function getPath() {
var path = d3.path();
path.moveTo(100, 20);
path.lineTo(200, 160);
path.quadraticCurveTo(cp1.x, cp1.y, 250, 120);
path.bezierCurveTo(cp2.x, cp2.y, cp3.x, cp3.y, 400, 150);
path.lineTo(500, 90);
return path.toString();
}
function drawPath() {
// draw the circles and add a drag listener
var circles = svg.selectAll("circle").data([cp1, cp2, cp3])
circles.enter().append("circle")
.attr("r", 5)
.attr("fill", "blue")
.attr("class","cp1")
.call(d3.drag().on("drag", dragcp))
.merge(circles)
.attr("cx", function(d) {return d.x})
.attr("cy", function(d) {return d.y});
var line = svg.selectAll(".line").data([0])
line.enter().append("path")
.attr("class","line").merge(line)
.attr("d", getPath)
.attr("stroke", "black")
.attr("fill", "none");
};
function dragcp() {
var ev = d3.event;
ev.subject.x = ev.x;
ev.subject.y = ev.y;
drawPath();
}
在这个示例中,可以拖动蓝色圆圈(即两条曲线的控制点),观察其对路径的影响。
2.2 沿线路径动画
创建路径的一个有趣用途是用于动画。一旦有了路径,就可以很容易地让 SVG 对象沿着该路径动画。以下是实现蓝色圆圈沿路径移动动画的代码:
// set the startingpoint
var startingPoint = {x: 300, y: 200};
// generate and draw the curve
var curve = generateCurve(startingPoint);
var drawnPath = drawPath(curve);
// draw a circle, which will be moved
var circle = svg.append("circle");
circle.attr("r", 7)
.attr("fill", "steelblue")
.attr("transform", "translate(" + startingPoint.x + " " + startingPoint.y + ")");
function generateCurve(startingPoint) {
var path = d3.path();
var center = startingPoint;
var curve = {w: 300, h: 300}
path.moveTo(center.x, center.y);
path.bezierCurveTo(center.x + curve.w, center.y - curve.h, center.x + curve.w, center.y + curve.h, center.x, center.y);
path.bezierCurveTo(center.x - curve.w, center.y - curve.h, center.x - curve.w, center.y + curve.h, center.x, center.y);
path.closePath();
return path.toString();
}
function drawPath(curve) {
return svg.append("path")
.attr("class","line")
.attr("d", function(d) {return curve})
.attr("stroke", "black")
.attr("fill", "none");
};
// kickoff the animation
move();
function move() {
circle.transition()
.duration(7500)
.ease(d3.easeLinear)
.attrTween("transform", animateOnPath(drawnPath.node()))
.on("end", move);// infinite loop
}
function animateOnPath(path) {
var l = path.getTotalLength();
return function(i) {
return function(t) {
var p = path.getPointAtLength(t * l);
return "translate(" + p.x + "," + p.y + ")";//Move marker
}
}
}
这个动画的关键在于 animateOnPath 插值器,它使用了标准的 SVG 函数 getPointAtLength ,该函数返回路径上对应特定长度的点。
3. 可视化导出
3.1 导出为 PNG
将 SVG 元素导出为 PNG 需要以下步骤:
1. 创建一个单独的 CSS 文件,定义要应用的样式。
2. 将 SVG 元素转换为字符串。
3. 使用画布获取 PNG 文件。
4. 将画布内容保存为文件。
以下是一个具体示例:
function exportAsPng() {
// first load the external css which will be applied to the image. For
// instance we can set the color to black instead of white.
d3.text('./css/D08-03-export.css', function(externalStyles) {
// first convert the SVG node to a string.
var svgString = nodeToSVGString(d3.select('svg').node(), externalStyles);
// Clean up some inconsistencies in browser implementations
svgString = svgString.replace(/(\w+)?:?xlink=/g, 'xmlns:xlink=');
svgString = svgString.replace(/NS\d+:href/g, 'xlink:href');
// next save
svgString2Image(svgString, width * 2, height, function(datablob) {
saveAs(datablob,'elections.png');
// FileSaver.js function
});
});
}
function nodeToSVGString( svgNode, cssText ) {
appendCSS( cssText, svgNode )
return new XMLSerializer().serializeToString(svgNode);
/**
* Append the external css to the element, so it is correctly exported
*/
function appendCSS( cssText, element ) {
var styleElement = document.createElement("style");
styleElement.setAttribute("type","text/css");
styleElement.innerHTML = cssText;
element.hasChildNodes()
? element.insertBefore( styleElement, element.children[0])
: element.insertBefore( styleElement, null );
}
}
function svgString2Image( svgString, width, height, callback ) {
// create a dataURL object
var blob = new Blob([svgString], { type: 'image/svg+xml;charset=utf-8'});
var imgsrc = window.URL.createObjectURL(blob);
// create a canvas to load the image to.
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
// create the image, and define callback
var image = new Image;
image.onload = function() {
// when loaded, draw the image, export it to a blob
// and call the specified callback.
context.clearRect ( 0, 0, width, height );
context.drawImage(image, 0, 0, width, height);
canvas.toBlob( function(blob) {
var filesize = Math.round( blob.length/1024 ) + ' KB';
if ( callback ) callback( blob, filesize );
});
};
// set the dataurl as image source.
image.src = imgsrc;
}
需要注意的是, canvas.toBlob 函数在大多数现代浏览器中受支持,但为确保在更多设备上正常工作,可以添加 JavaScript-Canvas-to-Blob 填充 。
3.2 导出为 SVG 并导入外部程序
导出 SVG 为 .svg 文件有两种方法。一种是在 Chrome 中使用 Bookmarklet ,另一种方法与导出为 PNG 类似,步骤如下:
1. 定义应包含在导出 SVG 文件中的特定 CSS 样式。
2. 将 SVG 和 CSS 文件合并为单个 SVG 字符串。
3. 将 SVG 字符串保存为文件。
以下是示例代码:
function exportAsSVG() {
d3.text('./css/D08-04.css', function(externalStyles) {
// first convert the SVG node to a string.
var svgString = nodeToSVGString(d3.select('svg').node(), externalStyles);
svgString2SVG(svgString);
});
}
function svgString2SVG( svgString, width, height, callback ) {
// create a dataURL object
var blob = new Blob([svgString], { type: 'image/svg+xml;charset=utf-8' });
saveAs(blob, "radial-tree.svg");
}
导出的 SVG 文件可以使用 Adobe Illustrator、Boxy SVG 或 Inkscape 等外部程序进行编辑。
流程图
graph LR
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
A(开始):::process --> B(选择可视化类型):::process
B --> C{是否使用符号}:::process
C -- 是 --> D(选择符号类型):::process
D --> E(设置符号大小和样式):::process
E --> F(渲染符号):::process
C -- 否 --> G{是否绘制路径}:::process
G -- 是 --> H(使用d3.path定义路径):::process
H --> I(绘制路径):::process
I --> J(可选择添加动画):::process
G -- 否 --> K(无操作):::process
F --> L{是否导出可视化}:::process
J --> L
L -- 是 --> M{导出为PNG还是SVG}:::process
M -- PNG --> N(创建CSS样式):::process
N --> O(将SVG转换为字符串):::process
O --> P(使用画布生成PNG):::process
P --> Q(保存PNG文件):::process
M -- SVG --> R(定义CSS样式):::process
R --> S(合并SVG和CSS为字符串):::process
S --> T(保存为SVG文件):::process
L -- 否 --> U(结束):::process
Q --> U
T --> U
综上所述,通过 D3 可以方便地创建自定义形状和路径,并实现各种动画效果。同时,还可以将可视化结果导出为 PNG 或 SVG 格式,满足不同的需求。无论是符号的使用、路径的绘制,还是可视化的导出,D3 都提供了丰富的功能和便捷的方法。希望本文能帮助你更好地掌握 D3 在这些方面的应用。
4. 总结与拓展
4.1 关键知识点回顾
- D3 符号 :D3 提供了多种标准符号,如十字、圆形、菱形等。每个符号都有大小函数,大小以面积指定。可以使用过渡效果对符号进行动画处理。
- d3.path 绘制路径 :通过
d3.path函数可以直接定义路径,提供了moveTo、lineTo等多个函数。可以创建自定义路径,并实现路径动画。 - 可视化导出 :可以将可视化结果导出为 PNG 或 SVG 格式。导出为 PNG 需要创建 CSS 样式、将 SVG 转换为字符串、使用画布生成 PNG 并保存;导出为 SVG 则需要定义 CSS 样式、合并 SVG 和 CSS 为字符串并保存。
4.2 实际应用场景
- 数据可视化展示 :在制作图表时,可以使用 D3 符号来替代传统的圆形等标记,使图表更加丰富多样。例如,在股票走势图中,使用不同的符号表示不同类型的交易信号。
- 动画效果制作 :利用路径动画可以创建出各种有趣的动画效果,如粒子沿着特定路径运动、元素沿着曲线轨迹移动等。
- 设计与编辑 :将可视化结果导出为 SVG 后,可以使用专业的 SVG 编辑工具进行进一步的设计和修改,满足不同的设计需求。
4.3 拓展建议
- 深入学习 D3 API :D3 提供了丰富的 API,可以进一步学习其他功能,如数据绑定、比例尺、交互事件等,以创建更加复杂和强大的可视化应用。
- 结合其他技术 :可以将 D3 与其他前端技术,如 React、Vue 等结合使用,提高开发效率和代码可维护性。
- 探索更多动画效果 :除了本文介绍的路径动画,还可以探索其他类型的动画效果,如淡入淡出、缩放、旋转等,为可视化增添更多的动态效果。
表格:D3 功能与应用场景总结
| 功能 | 描述 | 应用场景 |
|---|---|---|
| D3 符号 | 提供多种标准符号,可设置大小和动画 | 数据可视化标记、图表美化 |
| d3.path 绘制路径 | 直接定义路径,提供多个绘制函数 | 自定义图形绘制、路径动画 |
| 可视化导出 | 可导出为 PNG 或 SVG 格式 | 分享、打印、进一步设计 |
流程图:D3 学习与应用拓展
graph LR
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
A(学习 D3 基础):::process --> B(掌握符号和路径绘制):::process
B --> C(实现可视化导出):::process
C --> D{深入学习 D3 API}:::process
D -- 是 --> E(学习数据绑定、比例尺等):::process
E --> F(结合其他前端技术):::process
F --> G(创建复杂可视化应用):::process
D -- 否 --> H(探索更多动画效果):::process
H --> I(应用于实际项目):::process
G --> I
I --> J(持续优化和创新):::process
列表:学习资源推荐
- 官方文档 :D3 官方文档是学习 D3 的重要资源,提供了详细的 API 文档和示例代码。
- 在线教程 :网上有许多关于 D3 的在线教程,如 MDN Web Docs、W3Schools 等,可以帮助快速入门。
- 开源项目 :可以参考一些优秀的 D3 开源项目,学习他人的代码和设计思路。
通过不断学习和实践,相信你可以在 D3 的世界中创造出更加精彩的可视化作品。无论是简单的图表展示,还是复杂的交互应用,D3 都能为你提供强大的支持。希望本文能为你在 D3 的学习和应用中提供一些帮助和启发。
D3自定义形状与路径动画
超级会员免费看
870

被折叠的 条评论
为什么被折叠?



