跳到主要内容

包含 20,000 个节点的交互式散点图

本实验通过渲染 20,000 个圆形,展示 Konva 能够处理的大量节点。每个圆形都能响应 mouseover 事件并支持拖放。本实验还展示了事件委托:舞台上只有一个事件处理器,由它处理所有圆形的事件。

操作说明: 将鼠标移到节点上查看详细信息,然后在舞台中拖放它们。

import Konva from 'konva';

// create stage
const width = window.innerWidth;
const height = window.innerHeight;

const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});

// function to add a node to layer
function addNode(obj, layer) {
const node = new Konva.Circle({
x: obj.x,
y: obj.y,
radius: 4,
fill: obj.color,
id: obj.id,
});

layer.add(node);
}

// Create a single layer for all circles
const circlesLayer = new Konva.Layer();
const tooltipLayer = new Konva.Layer();
const dragLayer = new Konva.Layer();

// create tooltip
const tooltip = new Konva.Label({
opacity: 0.75,
visible: false,
listening: false,
});

tooltip.add(
new Konva.Tag({
fill: 'black',
pointerDirection: 'down',
pointerWidth: 10,
pointerHeight: 10,
lineJoin: 'round',
shadowColor: 'black',
shadowBlur: 10,
shadowOffsetX: 10,
shadowOffsetY: 10,
shadowOpacity: 0.2,
})
);

tooltip.add(
new Konva.Text({
text: '',
fontFamily: 'Calibri',
fontSize: 18,
padding: 5,
fill: 'white',
})
);

tooltipLayer.add(tooltip);

// build data
const data = [];
const colors = ['red', 'orange', 'cyan', 'green', 'blue', 'purple'];

for (let n = 0; n < 20000; n++) {
const x = Math.random() * width;
const y = height + Math.random() * 200 - 100 + (height / width) * -1 * x;
data.push({
x: x,
y: y,
id: n.toString(),
color: colors[Math.round(Math.random() * 5)],
});
}

// Add all nodes to a single layer
for (let n = 0; n < data.length; n++) {
addNode(data[n], circlesLayer);
}

// Add all layers to stage
stage.add(circlesLayer);
stage.add(dragLayer);
stage.add(tooltipLayer);

// handle events
let originalLayer;

stage.on('mouseover mousemove dragmove', function (evt) {
const node = evt.target;
if (node === stage) {
return;
}
if (node) {
// update tooltip
const mousePos = node.getStage().getPointerPosition();
tooltip.position({
x: mousePos.x,
y: mousePos.y - 5,
});
tooltip
.getText()
.text('node: ' + node.id() + ', color: ' + node.fill());
tooltip.show();
}
});

stage.on('mouseout', function (evt) {
tooltip.hide();
});

stage.on('mousedown', function (evt) {
const shape = evt.target;
if (shape) {
originalLayer = shape.getLayer();
shape.moveTo(dragLayer);
// manually trigger drag and drop
shape.startDrag();
}
});

stage.on('mouseup', function (evt) {
const shape = evt.target;
if (shape) {
shape.moveTo(originalLayer);
}
});