文章目录
- 📚d3
- 📚桑基图应用
- 🐇html
- 🐇css
- 🐇js
📚d3
d3.js是一个用于创建数据可视化的JavaScript库,它提供了丰富的API和工具来操作文档对象模型(DOM),使得创建可交互的、动态的数据可视化变得更加容易。
-
选择元素:使用d3.select()函数可以选择一个元素,使用d3.selectAll()函数可以选择多个元素。选择函数可以接收CSS选择器、DOM元素或函数作为参数。
var svg = d3.select("#chart"); // 选择id为"chart"的元素 var circles = d3.selectAll("circle"); // 选择所有的circle元素
-
绑定数据:使用
.data()
函数可以将数据绑定到选择的元素上。数据可以是数组、对象或函数的返回值。var data = [1, 2, 3, 4, 5]; var circles = svg.selectAll("circle").data(data);
-
创建元素:使用
.enter()
函数可以创建新的元素,这些元素对应于未绑定数据的选择集。可以使用.append()
、.insert()
等方法来创建元素。var circles = svg.selectAll("circle").data(data).enter().append("circle");
-
设置元素属性:使用
.attr()
函数可以设置元素的属性,可以传入属性名和对应的值,也可以传入一个函数来动态计算属性值。circles.attr("cx", function(d, i) {return i * 50; // 根据索引动态设置x坐标}).attr("cy", 50).attr("r", 20);
-
设置元素样式:使用
.style()
函数可以设置元素的样式,可以传入属性名和对应的值,也可以传入一个函数来动态计算样式值。circles.style("fill", "blue").style("opacity", 0.5);
-
缓动过渡:使用
.transition()
可以为元素添加过渡效果,可以设置过渡时间、缓动函数等。circles.transition().duration(1000).attr("cx", function(d, i) {return i * 100;});
-
事件处理:可以使用
.on()
函数为元素绑定事件处理函数,可以处理鼠标事件(如click、mouseover等)和其他事件(如transitionend等)。circles.on("click", function(d) {console.log("Clicked:", d); });
📚桑基图应用
🐇html
- 放两个div,一个放图,一个用于悬浮框。
<div id="chart"></div> <div id="tooltip"></div>
🐇css
#tooltip
: 定义了一个绝对定位的工具提示框样式,包括居中对齐、背景颜色、文本颜色和字体样式,以及圆角边框。pointer-events: none;
禁用了鼠标事件,确保工具提示不会干扰用户操作。svg
: 设置了SVG元素的全局字体、显示为块级元素以及水平居中。.node rect
: 定义了节点(node)矩形的样式,包括移动光标时显示为移动样式、填充不透明度以及形状呈现方式。.node text
: 设置了节点文本的样式,包括禁用指针事件、设置不透明度以及字体样式。.link
: 设置了链接(link)的样式,包括不填充任何颜色、设置描边颜色以及描边不透明度。.link:hover
: 当鼠标悬停在链接上时,修改了描边的不透明度,以增强交互体验。body{background: url(./assects/images/background.jpg) center; } #tooltip {position: absolute;text-align: center;width: auto;height: auto;padding: 2px;background: #13393E;color: #F2CCA6;font-size: 14px;font-family: KaiTi, serif;border: 0px;border-radius: 8px;pointer-events: none; } svg {font: 10px sans-serif;display: block;margin:0 auto; } .node rect {cursor: move;fill-opacity: .9;shape-rendering: crispEdges; } .node text {pointer-events: none;opacity: .5;font-weight: bold;font-family: KaiTi, serif; } .link {fill: none;stroke: #000;stroke-opacity: .06;} .link:hover {stroke-opacity: .18; }
🐇js
-
引入js外部库:d3.js本地下载
<script src="./assects/js/d3/d3.v3.min.js"></script> <!-- D3.js的作者之一Mike Bostock提供的Sankey图相关的Javascript库 --> <script src="https://bost.ocks.org/mike/sankey/sankey.js"></script>
以下是自定义部分⭐️
- 数据定义:定义节点nodes和链接links。links里的起始按索引设置。
var json = {"nodes": [{"name": "洛阳"},{"name": "陇西"}...],"links": [{"source": 0,"target": 55,"value": 99},{"source": 1,"target": 56,"value": 47}... ]};
- 基础设定
// 定义边距、宽度和高度 var margin = {top: 3, right: 1, bottom: 3, left: 1}, // 定义边距的对象width = window.innerWidth * 0.5 - margin.left - margin.right, // 计算宽度height = window.innerHeight * 0.95 - margin.top - margin.bottom; // 计算高度// 格式化和颜色比例尺 var formatNumber = d3.format(",.0f"), // 格式化数字的函数format = function(d) { return formatNumber(d); }, // 定义格式化函数color = d3.scale.category20(); // 创建颜色比例尺// 创建一个SVG元素并将其附加到图表div中 var svg = d3.select("#chart").append("svg") // 选择图表元素,并添加SVG元素.attr("width", width + margin.left + margin.right) // 设置SVG宽度.attr("height", height + margin.top + margin.bottom) // 设置SVG高度 .append("g") // 添加一个分组元素.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // 设置图表的偏移量
- 初始化Sankey图布局
// 初始化Sankey图布局 var sankey = d3.sankey() // 创建Sankey布局.nodeWidth(30) // 设置节点宽度.nodePadding(5) // 设置节点间距.size([width, height]); // 设置布局大小var path = sankey.link(); // 为链接定义路径生成器// 在Sankey图中设置节点、链接和布局 sankey.nodes(json.nodes) // 设置节点数据.links(json.links) // 设置链接数据.layout(32); // 执行布局计算
- 链接
links
绑定// 创建链接并绑定数据 var link = svg.append("g").selectAll(".link") // 创建链接的选择集.data(json.links) // 绑定数据 .enter().append("path") // 进入更新.attr("class", "link") // 添加class.attr("d", path) // 设置路径.style("stroke-width", function(d) { return Math.max(1, d.dy); }) // 设置描边宽度.sort(function(a, b) { return b.dy - a.dy; }); // 根据数据大小排序
- 节点
nodes
设置// 创建节点并绑定数据 var node = svg.append("g").selectAll(".node") // 创建节点的选择集.data(json.nodes) // 绑定数据.enter().append("g") // 进入更新.attr("class", "node") // 添加class.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }) // 设置节点位置.call(d3.behavior.drag() // 添加拖动行为.origin(function(d) { return d; }) // 定义起始位置.on("dragstart", function() { this.parentNode.appendChild(this); }) // 开始拖动时将节点置于顶层.on("drag", dragmove)); // 拖动时的处理函数// 节点颜色数组var colorArray = ["#00577f", "#439083", "#aa5723", /* ... */ ]; // 节点颜色数组// 为节点创建矩形node.append("rect") // 添加矩形元素.attr("height", function(d) { return d.dy; }) // 设置高度.attr("width", sankey.nodeWidth()) // 设置宽度.style("fill", function(d, i) { return colorArray[i % colorArray.length]; }) // 设置填充颜色.style("stroke", function(d, i) { return d3.rgb(colorArray[i % colorArray.length]).darker(0); }) // 设置描边颜色.append("title"); // 添加标题元素// 为节点添加标签node.append("text") // 添加文本元素.attr("x", -6) // 设置x坐标.attr("y", function(d) { return d.dy / 2; }) // 设置y坐标.attr("dy", ".35em") // 设置dy属性.attr("text-anchor", "end") // 设置文本锚点.attr("transform", null) // 重置变换.text(function(d) { return d.name; }) // 设置文本内容.filter(function(d) { return d.x < width / 2; }) // 过滤条件.attr("x", 6 + sankey.nodeWidth()) // 设置x坐标.attr("text-anchor", "start"); // 设置文本锚点
- 悬浮框动作触发设置
// 当鼠标悬停在节点上时显示悬浮框 node.on("mouseover", function(d) {d3.select("#tooltip").style("left", (d3.event.pageX) + "px").style("top", (d3.event.pageY - 28) + "px").style("opacity", 0.9).html(d.name + ": " + format(d.value));});// 鼠标移出节点的时候隐藏自定义悬浮框 node.on("mouseout", function(d) {d3.select("#tooltip").style("opacity", 0); });// 在鼠标悬浮在链接上的时候显示自定义悬浮框 link.on("mouseover", function(d) {d3.select("#tooltip").style("left", (d3.event.pageX) + "px").style("top", (d3.event.pageY - 28) + "px").style("opacity", 0.9).html(d.source.name + " → " + d.target.name + ": " + format(d.value)); });// 鼠标移出链接的时候隐藏自定义悬浮框 link.on("mouseout", function(d) {d3.select("#tooltip").style("opacity", 0); });
- 节点拖动功能实现
// 通过拖动行为移动节点 function dragmove(d) {d3.select(this).attr("transform", "translate(" + d.x + "," + (d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))) + ")");sankey.relayout();link.attr("d", path); }