Skip to main content

使用 Konva 进行调整大小压力测试

这是一个用于同时选择和调整许多形状的压力测试演示。

该演示使用两个核心 Konva 功能来提升性能:

1. 图层

调整大小的形状被移动到另一个图层(另一个画布元素)。因此,当您调整所选形状的大小时,我们不需要重绘其他形状。

2. 缓存

select 时,我将所有选定的形状移动到一个组中并缓存该组。缓存操作会将组转换为位图图像。在屏幕上重绘这样的组要快得多。

说明:尝试选择几个形状并调整它们的大小/旋转它们。

import Konva from 'konva';

// 首先我们需要创建一个舞台
var width = window.innerWidth;
var height = window.innerHeight;

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

// 用于所有形状的图层
var layer = new Konva.Layer();
stage.add(layer);
for (var i = 0; i < 10000; i++) {
var shape = new Konva.Circle({
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight,
radius: 10,
name: 'shape',
fill: Konva.Util.getRandomColor(),
});
layer.add(shape);
}

// 用于变换组的顶层图层
var topLayer = new Konva.Layer();
stage.add(topLayer);

var group = new Konva.Group({
draggable: true,
});
topLayer.add(group);

var tr = new Konva.Transformer();
topLayer.add(tr);

// 添加一个新功能,让我们添加绘制选择矩形的能力
var selectionRectangle = new Konva.Rect({
fill: 'rgba(0,0,255,0.5)',
visible: false,
});
topLayer.add(selectionRectangle);

var x1, y1, x2, y2;
stage.on('mousedown touchstart', (e) => {
// 如果在变换器上按下鼠标,则不执行任何操作
if (e.target.getParent() === tr) {
return;
}
// 如果在组上按下鼠标,则不执行任何操作
if (e.target.parent === group) {
return;
}
x1 = stage.getPointerPosition().x;
y1 = stage.getPointerPosition().y;
x2 = stage.getPointerPosition().x;
y2 = stage.getPointerPosition().y;

selectionRectangle.setAttrs({
x: x1,
y: y1,
width: 0,
height: 0,
visible: true,
});

// 将旧选区移回原始图层
group.children.slice().forEach((shape) => {
const transform = shape.getAbsoluteTransform();
shape.moveTo(layer);
shape.setAttrs(transform.decompose());
});
// 重置组变换
group.setAttrs({
x: 0,
y: 0,
scaleX: 1,
scaleY: 1,
rotation: 0,
});
group.clearCache();
});

stage.on('mousemove touchmove', () => {
// 如果没有开始选择,则不执行任何操作
if (!selectionRectangle.visible()) {
return;
}
x2 = stage.getPointerPosition().x;
y2 = stage.getPointerPosition().y;

selectionRectangle.setAttrs({
x: Math.min(x1, x2),
y: Math.min(y1, y2),
width: Math.abs(x2 - x1),
height: Math.abs(y2 - y1),
});
});

stage.on('mouseup touchend', () => {
// 如果没有开始选择,则不执行任何操作
if (!selectionRectangle.visible()) {
return;
}
// 在超时中更新可见性,以便我们可以在点击事件中检查它
setTimeout(() => {
selectionRectangle.visible(false);
});

var shapes = stage.find('.shape');
var box = selectionRectangle.getClientRect();

// 移除所有子项以获得更好的性能
layer.removeChildren();

// 然后检查交集并将所有形状添加到正确的容器中
shapes.forEach((shape) => {
var intersected = Konva.Util.haveIntersection(
box,
shape.getClientRect()
);
if (intersected) {
group.add(shape);
shape.stroke('blue');
} else {
layer.add(shape);
shape.stroke(null);
}
});

if (group.children.length) {
tr.nodes([group]);
group.cache();
} else {
tr.nodes([]);
group.clearCache();
}
});

// 点击应选择/取消选择形状
stage.on('click tap', function (e) {
// 如果我们正在使用矩形选择,则不执行任何操作
if (selectionRectangle.visible()) {
return;
}

// 如果点击空白区域 - 移除所有选择
if (e.target === stage) {
tr.nodes([]);
return;
}
});