交互(Interaction)
D3 提供了许多模块来帮助您添加交互性,例如缩放、平移和画笔。
-
四叉树(Quadtrees):通过在每次移动鼠标时搜索最接近鼠标指针的项目,可以更轻松地挑选小项目。这可能是一项昂贵的操作,但使用 D3 的四叉树模块可以提高效率。四叉树是一种树数据结构,它递归地将一个区域划分为越来越小的区域,并且可以使搜索项目更有效。
let data = [], width = 600, height = 400, numPoints = 100; let quadtree = d3.quadtree() .x(function(d) {return d.x;}) .y(function(d) {return d.y;}); let hoveredId; function updateData() { data = []; for(let i=0; i<numPoints; i++) { data.push({ id: i, x: Math.random() * width, y: Math.random() * height, r: 1 + Math.random() * 20 }); } } function handleMousemove(e) { let pos = d3.pointer(e, this); let d = quadtree.find(pos[0], pos[1], 20); hoveredId = d ? d.id : undefined; update(); } function initEvents() { d3.select('svg') .on('mousemove', handleMousemove); } function updateQuadtree() { quadtree.addAll(data); } function update() { d3.select('svg') .selectAll('circle') .data(data) .join('circle') .attr('cx', function(d) { return d.x; }) .attr('cy', function(d) { return d.y; }) .attr('r', function(d) { return d.r; }) .style('fill', function(d) { return d.id === hoveredId ? 'red' : null;}); } updateData(); updateQuadtree(); update(); initEvents(); -
德劳内三角形(Delaunay triangles):还可以使用 D3 的 Delaunay 模块来查找最近点。给定一个点数组,Delaunay 三角剖分将所有点与三角形连接起来,从而使碎片最小化。四叉树不同,不支持添加最大距离作为第三个参数。
let data = [], width = 600, height = 400, numPoints = 100; let triangles; let hoveredId; function updateData() { data = []; for(let i=0; i<numPoints; i++) { data.push({ id: i, x: Math.random() * width, y: Math.random() * height, r: 1 + Math.random() * 20 }); } } function handleMousemove(e) { let pos = d3.pointer(e, this); let i = triangles.find(pos[0], pos[1]); hoveredId = data[i].id; update(); } function initEvents() { d3.select('svg') .on('mousemove', handleMousemove); } function updateDelaunay() { triangles = d3.Delaunay .from(data, function(d) {return d.x;}, function(d) {return d.y;} ); } function update() { d3.select('svg') .selectAll('circle') .data(data) .join('circle') .attr('cx', function(d) { return d.x; }) .attr('cy', function(d) { return d.y; }) .attr('r', function(d) { return d.r; }) .style('fill', function(d) { return d.id === hoveredId ? 'red' : null;}); } initEvents(); updateData(); updateDelaunay(); update(); -
拖动(Dragging):D3 有一个用于向元素添加拖动行为的模块。D3 的拖动模块也支持触摸手势。使 HTML/SVG 元素可拖动需要三个步骤:
- 调用
d3.drag()创建拖动行为函数 - 添加一个在拖动事件发生时调用的事件处理程序。事件处理程序接收一个事件对象,您可以使用它来更新拖动元素的位置
- 将拖动行为附加到要使其可拖动的元素
拖动事件对象有几个属性,其中最有用的是:
属性名称 描述 .subject被拖动元素的连接数据 .x&.y被拖动元素的新坐标 .dx&.dy被拖动元素的新坐标,相对于之前的坐标 let data = [], width = 600, height = 400, numPoints = 10; let drag = d3.drag() .on('drag', handleDrag); function handleDrag(e) { e.subject.x = e.x; e.subject.y = e.y; update(); } function initDrag() { d3.select('svg') .selectAll('circle') .call(drag); } function updateData() { data = []; for(let i=0; i<numPoints; i++) { data.push({ id: i, x: Math.random() * width, y: Math.random() * height }); } } function update() { d3.select('svg') .selectAll('circle') .data(data) .join('circle') .attr('cx', function(d) { return d.x; }) .attr('cy', function(d) { return d.y; }) .attr('r', 40); } updateData(); update(); initDrag(); - 调用
-
刷牙(Brushing):允许用户指定一个区域(通过按下鼠标按钮,移动鼠标,然后释放),例如,选择一组元素。将画笔行为添加到 HTML 或 SVG 元素需要三个步骤:
- 调用
d3.brush()创建画笔行为函数 - 添加一个在发生画笔事件时调用的事件处理程序。事件处理程序接收画笔范围,然后可用于选择元素、定义缩放区域等。
- 将画笔行为附加到一个元素(或多个元素)
let brush = d3.brush() .on('start brush', handleBrush); function handleBrush(e) { // Use the brush extent e.selection to compute, for example, which elements to select } function initBrush() { d3.select('svg') .call(brush); } initBrush();事件类型:
'brush'和'start'和'end'。'brush'表示画笔范围已更改。'start'表示刷牙已经开始(例如用户按下了鼠标按钮)。'end'表示刷牙结束(例如用户已松开鼠标按钮)。handleBrush接收单个参数e,该参数是表示画笔事件的对象。画笔事件中最有用的属性.selection是将画笔的范围表示为一个数组[[x0, y0], [x1, y1]],其中x0, y0和x1, y1是画笔的对角。通常handleBrush会计算哪些元素在画笔范围内并相应地更新它们。通过选择元素并将画笔行为传递给
.call方法,可以将画笔行为附加到元素://完整例子 let data = [], width = 600, height = 400, numPoints = 100; let brush = d3.brush() .on('start brush', handleBrush); let brushExtent; function handleBrush(e) { brushExtent = e.selection; update(); } function initBrush() { d3.select('svg g') .call(brush); } function updateData() { data = []; for(let i=0; i<numPoints; i++) { data.push({ id: i, x: Math.random() * width, y: Math.random() * height }); } } function isInBrushExtent(d) { return brushExtent && d.x >= brushExtent[0][0] && d.x <= brushExtent[1][0] && d.y >= brushExtent[0][1] && d.y <= brushExtent[1][1]; } function update() { d3.select('svg') .selectAll('circle') .data(data) .join('circle') .attr('cx', function(d) { return d.x; }) .attr('cy', function(d) { return d.y; }) .attr('r', 4) .style('fill', function(d) { return isInBrushExtent(d) ? 'red' : null; }); } initBrush(); updateData(); update();D3 还提供了画笔
d3.brushX和d3.brushY将画笔限制在一个维度上。画笔行为有两种设置画笔范围
.move和.clear。第一个设置画笔范围,第二个清除画笔。d3.select('svg g') .call(brush.move, [[50, 50], [100, 100]]); - 调用