HTML5 Canvas 放置事件
Konva 不支持放置事件,但你可以自行编写放置事件检测逻辑。 如需检测作为放置目标的图形,必须把正在拖动的对象移到另一个图层。
此示例展示 drop、dragenter、dragleave 和 dragover 事件的实现。
操作说明: 将一个图形拖到另一个图形上方。也可以将一个图形拖放到 另一个图形中。
- Vanilla
- React
- Vue
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
const tempLayer = new Konva.Layer();
stage.add(layer);
stage.add(tempLayer);
const text = new Konva.Text({
fill: 'black',
});
layer.add(text);
let previousShape;
// create multiple stars
for (let i = 0; i < 10; i++) {
const star = new Konva.Star({
x: stage.width() * Math.random(),
y: stage.height() * Math.random(),
fill: 'blue',
numPoints: 10,
innerRadius: 20,
outerRadius: 25,
draggable: true,
name: 'star ' + i,
shadowOffsetX: 5,
shadowOffsetY: 5,
});
star.on('dragstart', () => {
star.moveTo(tempLayer);
text.text('Moving ' + star.name());
});
star.on('dragmove', (e) => {
const pos = stage.getPointerPosition();
const shape = layer.getIntersection(pos);
if (previousShape && shape) {
if (previousShape !== shape) {
// leave from old target
previousShape.fire('dragleave', { evt: e.evt }, true);
// enter new target
shape.fire('dragenter', { evt: e.evt }, true);
previousShape = shape;
} else {
previousShape.fire('dragover', { evt: e.evt }, true);
}
} else if (!previousShape && shape) {
previousShape = shape;
shape.fire('dragenter', { evt: e.evt }, true);
} else if (previousShape && !shape) {
previousShape.fire('dragleave', { evt: e.evt }, true);
previousShape = undefined;
}
});
star.on('dragend', (e) => {
const pos = stage.getPointerPosition();
const shape = layer.getIntersection(pos);
if (previousShape && previousShape !== shape) {
previousShape.fire('dragleave', { evt: e.evt }, true);
}
if (shape) {
shape.fire('drop', { evt: e.evt }, true);
}
previousShape = undefined;
star.moveTo(layer);
});
star.on('dragenter', () => {
star.fill('green');
text.text('dragenter ' + star.name());
});
star.on('dragleave', () => {
star.fill('blue');
text.text('dragleave ' + star.name());
});
star.on('dragover', () => {
text.text('dragover ' + star.name());
});
star.on('drop', () => {
star.fill('red');
text.text('drop ' + star.name());
});
layer.add(star);
}
import { Stage, Layer, Text, Star } from 'react-konva';
import { useState, useRef } from 'react';
const App = () => {
const [stars, setStars] = useState(() =>
Array.from({ length: 10 }, (_, i) => ({
id: i,
x: window.innerWidth * Math.random(),
y: window.innerHeight * Math.random(),
fill: 'blue',
name: `star ${i}`,
}))
);
const [message, setMessage] = useState('');
const previousShapeRef = useRef(null);
const mainLayerRef = useRef(null);
const tempLayerRef = useRef(null);
const updateStar = (id, attrs) => {
setStars((currentStars) =>
currentStars.map((star) =>
star.id === id ? { ...star, ...attrs } : star
)
);
};
const handleDragStart = (id, e) => {
const shape = e.target;
updateStar(id, { x: shape.x(), y: shape.y() });
shape.moveTo(tempLayerRef.current);
setMessage('Moving ' + shape.name());
};
const handleDragMove = (id, e) => {
const stage = e.target.getStage();
const pos = stage.getPointerPosition();
const shape = mainLayerRef.current.getIntersection(pos);
updateStar(id, { x: e.target.x(), y: e.target.y() });
if (previousShapeRef.current && shape) {
if (previousShapeRef.current !== shape) {
// leave from old target
previousShapeRef.current.fire('dragleave', { evt: e.evt }, true);
// enter new target
shape.fire('dragenter', { evt: e.evt }, true);
previousShapeRef.current = shape;
} else {
previousShapeRef.current.fire('dragover', { evt: e.evt }, true);
}
} else if (!previousShapeRef.current && shape) {
previousShapeRef.current = shape;
shape.fire('dragenter', { evt: e.evt }, true);
} else if (previousShapeRef.current && !shape) {
previousShapeRef.current.fire('dragleave', { evt: e.evt }, true);
previousShapeRef.current = undefined;
}
};
const handleDragEnd = (id, e) => {
const shape = e.target;
const stage = e.target.getStage();
const pos = stage.getPointerPosition();
const dropShape = mainLayerRef.current.getIntersection(pos);
if (
previousShapeRef.current &&
previousShapeRef.current !== dropShape
) {
previousShapeRef.current.fire('dragleave', { evt: e.evt }, true);
}
if (dropShape) {
dropShape.fire('drop', { evt: e.evt }, true);
}
updateStar(id, { x: shape.x(), y: shape.y() });
shape.moveTo(mainLayerRef.current);
previousShapeRef.current = undefined;
};
const handleDragEnter = (id, e) => {
updateStar(id, { fill: 'green' });
setMessage('dragenter ' + e.target.name());
};
const handleDragLeave = (id, e) => {
updateStar(id, { fill: 'blue' });
setMessage('dragleave ' + e.target.name());
};
const handleDragOver = (e) => {
setMessage('dragover ' + e.target.name());
};
const handleDrop = (id, e) => {
updateStar(id, { fill: 'red' });
setMessage('drop ' + e.target.name());
};
return (
<Stage width={window.innerWidth} height={window.innerHeight}>
<Layer ref={mainLayerRef}>
<Text text={message} fill="black" />
{stars.map((star) => (
<Star
key={star.id}
name={star.name}
x={star.x}
y={star.y}
numPoints={10}
innerRadius={20}
outerRadius={25}
fill={star.fill}
shadowOffsetX={5}
shadowOffsetY={5}
draggable
onDragStart={(e) => handleDragStart(star.id, e)}
onDragMove={(e) => handleDragMove(star.id, e)}
onDragEnd={(e) => handleDragEnd(star.id, e)}
onDragEnter={(e) => handleDragEnter(star.id, e)}
onDragLeave={(e) => handleDragLeave(star.id, e)}
onDragOver={handleDragOver}
onDrop={(e) => handleDrop(star.id, e)}
/>
))}
</Layer>
<Layer ref={tempLayerRef} />
</Stage>
);
};
export default App;
<template>
<v-stage :config="stageSize">
<v-layer ref="mainLayer">
<v-text :config="textConfig" />
<v-star
v-for="star in stars"
:key="star.id"
:config="getStarConfig(star)"
@dragstart="handleDragStart"
@dragmove="handleDragMove"
@dragend="handleDragEnd"
@dragenter="handleDragEnter"
@dragleave="handleDragLeave"
@dragover="handleDragOver"
@drop="handleDrop"
/>
</v-layer>
<v-layer ref="tempLayer" />
</v-stage>
</template>
<script setup>
import { ref, computed } from 'vue';
const stageSize = {
width: window.innerWidth,
height: window.innerHeight
};
const message = ref('');
const mainLayer = ref(null);
const tempLayer = ref(null);
const previousShape = ref(null);
const stars = ref(Array.from({ length: 10 }, (_, i) => ({
id: i,
x: window.innerWidth * Math.random(),
y: window.innerHeight * Math.random(),
fill: 'blue',
name: `star ${i}`
})));
const textConfig = computed(() => ({
text: message.value,
fill: 'black'
}));
const getStarConfig = (star) => ({
id: star.id,
name: star.name,
x: star.x,
y: star.y,
numPoints: 10,
innerRadius: 20,
outerRadius: 25,
fill: star.fill,
shadowOffsetX: 5,
shadowOffsetY: 5,
draggable: true
});
const handleDragStart = (e) => {
const shape = e.target;
shape.moveTo(tempLayer.value.getNode());
message.value = 'Moving ' + shape.attrs.name;
};
const handleDragMove = (e) => {
const stage = e.target.getStage();
const pos = stage.getPointerPosition();
const shape = mainLayer.value.getNode().getIntersection(pos);
if (previousShape.value && shape) {
if (previousShape.value !== shape) {
// leave from old target
previousShape.value.fire('dragleave', { evt: e.evt }, true);
// enter new target
shape.fire('dragenter', { evt: e.evt }, true);
previousShape.value = shape;
} else {
previousShape.value.fire('dragover', { evt: e.evt }, true);
}
} else if (!previousShape.value && shape) {
previousShape.value = shape;
shape.fire('dragenter', { evt: e.evt }, true);
} else if (previousShape.value && !shape) {
previousShape.value.fire('dragleave', { evt: e.evt }, true);
previousShape.value = null;
}
};
const handleDragEnd = (e) => {
const shape = e.target;
const stage = e.target.getStage();
const pos = stage.getPointerPosition();
const dropShape = mainLayer.value.getNode().getIntersection(pos);
if (previousShape.value && previousShape.value !== dropShape) {
previousShape.value.fire('dragleave', { evt: e.evt }, true);
}
if (dropShape) {
dropShape.fire('drop', { evt: e.evt }, true);
}
shape.moveTo(mainLayer.value.getNode());
previousShape.value = null;
};
const handleDragEnter = (e) => {
const shape = e.target;
shape.fill('green');
message.value = 'dragenter ' + shape.attrs.name;
};
const handleDragLeave = (e) => {
const shape = e.target;
shape.fill('blue');
message.value = 'dragleave ' + shape.attrs.name;
};
const handleDragOver = (e) => {
message.value = 'dragover ' + e.target.attrs.name;
};
const handleDrop = (e) => {
const shape = e.target;
shape.fill('red');
message.value = 'drop ' + shape.attrs.name;
};
</script>