如何将 DOM 图像拖放到画布中
在这个演示中,我们将展示如何将放置在画布外部的 DOM 元素拖放到舞台中。
第一张图像是一个 DOM 图像。我们可以使用 HTML 拖放 使其可拖动。
如果您需要在触摸设备上为 DOM 元素启用拖放,则需要一些额外的步骤。您可以在 这里 阅读更多信息。
说明: 将尤达拖放到画布中。
- Vanilla
- React
- Vue
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
// 添加 DOM 元素以在容器外部渲染
document.getElementById('container').insertAdjacentHTML(
'beforebegin',
`
<p>将尤达拖放到灰色区域。</p>
<div id="drag-items">
<img src="https://konvajs.org/assets/yoda.jpg" draggable="true" style="height: 100px; margin: 5px;" />
<img src="https://konvajs.org/assets/darth-vader.jpg" draggable="true" style="height: 100px; margin: 5px;" />
</div>
`
);
// 用灰色背景样式化容器
document.getElementById('container').style.backgroundColor = 'rgba(0, 0, 0, 0.1)';
// 创建舞台和图层
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height - 150, // 留出空间给 DOM 元素
});
const layer = new Konva.Layer();
stage.add(layer);
// 跟踪拖动元素的 URL
let itemURL = '';
document
.getElementById('drag-items')
.addEventListener('dragstart', function (e) {
itemURL = e.target.src;
});
// 处理容器上的 dragover
const container = stage.container();
container.addEventListener('dragover', function (e) {
e.preventDefault(); // 重要 - 必须阻止默认行为
});
// 处理容器上的 drop
container.addEventListener('drop', function (e) {
e.preventDefault();
// 手动注册指针位置,因为这是一个 DOM 事件
stage.setPointersPositions(e);
// 加载图像并将其添加到图层
Konva.Image.fromURL(itemURL, function (image) {
// 根据图像尺寸计算合适的大小
const img = image.image();
const maxDimension = 100;
let width = img.width;
let height = img.height;
if (width > height) {
height = (height / width) * maxDimension;
width = maxDimension;
} else {
width = (width / height) * maxDimension;
height = maxDimension;
}
image.size({
width: width,
height: height
});
layer.add(image);
image.position(stage.getPointerPosition());
image.draggable(true);
});
});
import { useState, useRef, useEffect } from 'react';
import { Stage, Layer, Image } from 'react-konva';
import { useImage } from 'react-konva-utils';
const DragItem = ({ src, onDragStart, onDragEnd }) => {
return (
<img
src={src}
draggable={true}
style={{ height: '100px', margin: '5px' }}
onDragStart={() => onDragStart(src)}
onDragEnd={onDragEnd}
/>
);
};
const App = () => {
const [images, setImages] = useState([]);
const stageRef = useRef(null);
const dragImageSrc = useRef('');
const nextImageId = useRef(1);
const handleDragStart = (src) => {
dragImageSrc.current = src;
};
const handleDragSourceEnd = () => {
dragImageSrc.current = '';
};
const handleDragOver = (e) => {
e.preventDefault(); // 阻止默认行为
};
const handleDrop = (e) => {
e.preventDefault();
const src = dragImageSrc.current;
dragImageSrc.current = '';
if (!src || !stageRef.current) return;
// 获取舞台和指针位置
const stage = stageRef.current;
// 手动注册指针位置,因为这是一个 DOM 事件
stage.setPointersPositions(e);
const position = stage.getPointerPosition();
const id = `image-${nextImageId.current++}`;
// Add new image to the list
setImages(currentImages => [
...currentImages,
{
src,
x: position.x,
y: position.y,
id
}
]);
};
const handleImageDragEnd = (id, e) => {
const { x, y } = e.target.position();
setImages(currentImages => currentImages.map(img =>
img.id === id ? { ...img, x, y } : img
));
};
return (
<div>
<p>将尤达拖放到灰色区域。</p>
<div style={{ marginBottom: '10px' }}>
<DragItem
src="https://konvajs.org/assets/yoda.jpg"
onDragStart={handleDragStart}
onDragEnd={handleDragSourceEnd}
/>
<DragItem
src="https://konvajs.org/assets/darth-vader.jpg"
onDragStart={handleDragStart}
onDragEnd={handleDragSourceEnd}
/>
</div>
<div
onDragOver={handleDragOver}
onDrop={handleDrop}
style={{ backgroundColor: 'rgba(0, 0, 0, 0.1)' }}
>
<Stage
width={window.innerWidth}
height={window.innerHeight - 150}
ref={stageRef}
>
<Layer>
{images.map((img) => (
<KonvaImage
key={img.id}
src={img.src}
x={img.x}
y={img.y}
draggable
onDragEnd={(e) => handleImageDragEnd(img.id, e)}
/>
))}
</Layer>
</Stage>
</div>
</div>
);
};
// Separate component for Konva Image with proper loading
const KonvaImage = ({ src, x, y, draggable, onDragEnd }) => {
const [image] = useImage(src);
if (!image) return null;
// 计算合适的大小
const maxDimension = 100;
let width = image.width;
let height = image.height;
if (width > height) {
height = (height / width) * maxDimension;
width = maxDimension;
} else {
width = (width / height) * maxDimension;
height = maxDimension;
}
return (
<Image
image={image}
x={x}
y={y}
width={width}
height={height}
draggable={draggable}
onDragEnd={onDragEnd}
/>
);
};
export default App;
<template>
<div>
<p>将尤达拖放到灰色区域。</p>
<div style="margin-bottom: 10px;">
<img
src="https://konvajs.org/assets/yoda.jpg"
draggable="true"
style="height: 100px; margin: 5px;"
@dragstart="handleDragStart('https://konvajs.org/assets/yoda.jpg')"
/>
<img
src="https://konvajs.org/assets/darth-vader.jpg"
draggable="true"
style="height: 100px; margin: 5px;"
@dragstart="handleDragStart('https://konvajs.org/assets/darth-vader.jpg')"
/>
</div>
<div
@dragover.prevent
@drop="handleDrop"
style="background-color: rgba(0, 0, 0, 0.1);"
>
<v-stage
ref="stageRef"
:config="stageConfig"
>
<v-layer>
<v-image
v-for="img in images"
:key="img.id"
:config="getImageConfig(img)"
/>
</v-layer>
</v-stage>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, computed } from 'vue';
const stageConfig = {
width: window.innerWidth,
height: window.innerHeight - 150
};
const stageRef = ref(null);
const images = ref([]);
const dragImageSrc = ref('');
const loadedImages = ref({});
// 处理拖动开始,以跟踪正在拖动的图像
const handleDragStart = (src) => {
dragImageSrc.value = src;
};
// 处理 drop 事件,将图像添加到舞台
const handleDrop = (e) => {
e.preventDefault();
if (!dragImageSrc.value || !stageRef.value) return;
// 获取舞台引用并注册指针位置
const stage = stageRef.value.getNode();
stage.setPointersPositions(e);
const position = stage.getPointerPosition();
// 如果尚未加载图像,则加载图像
if (!loadedImages.value[dragImageSrc.value]) {
const img = new Image();
img.onload = () => {
loadedImages.value = {
...loadedImages.value,
[dragImageSrc.value]: img
};
// 将图像添加到舞台
addImageToStage(dragImageSrc.value, position);
};
img.src = dragImageSrc.value;
} else {
// 图像已加载,添加到舞台
addImageToStage(dragImageSrc.value, position);
}
};
// 辅助函数将图像添加到舞台
const addImageToStage = (src, position) => {
images.value.push({
src: src,
x: position.x,
y: position.y,
id: Date.now().toString()
});
};
// 配置图像属性
const getImageConfig = (img) => {
const image = loadedImages.value[img.src];
if (!image) return { x: img.x, y: img.y, draggable: true };
// 计算合适的大小
const maxDimension = 100;
let width = image.width;
let height = image.height;
if (width > height) {
height = (height / width) * maxDimension;
width = maxDimension;
} else {
width = (width / height) * maxDimension;
height = maxDimension;
}
return {
image: image,
x: img.x,
y: img.y,
width: width,
height: height,
draggable: true
};
};
</script>