效果,可以单独点击控制大小,也可框选控制
代码:
<template>
<div class="rect">
<div id="canvas"></div> <!-- 画布容器 -->
</div>
</template>
<script setup lang="ts">
import { onMounted } from 'vue'; // 导入 Vue 的 onMounted 钩子
import Konva from 'konva'; // 导入 Konva 图形库
// 声明 stage、layer 和 transformer
let stage: Konva.Stage | null = null; // 初始化 stage 为 null
const layer: Konva.Layer = new Konva.Layer(); // 创建一个新的图层
const tr: Konva.Transformer = new Konva.Transformer(); // 创建一个 Transformer 用于选中和调整形状
// 在组件挂载后初始化
onMounted(() => {
init(); // 调用初始化函数
});
const init = () => {
const el = document.getElementById("canvas"); // 获取画布 DOM 元素
if (!el) {
return; // 如果没有找到画布元素,结束函数
}
const { clientWidth, clientHeight } = el; // 获取画布的宽度和高度
// 创建一个 Konva Stage 平台
stage = new Konva.Stage({
container: 'canvas', // 指定画布容器
width: clientWidth, // 设置宽度
height: clientHeight, // 设置高度
});
stage.add(layer); // 将层添加到舞台
// 创建第一个矩形
const rect1 = new Konva.Rect({
name: "rect", // 给图形添加名称
x: clientWidth / 2 - 100, // 设置 x 坐标
y: clientHeight / 2, // 设置 y 坐标
width: 200, // 矩形的宽度
height: 100, // 矩形的高度
fill: "#ff8800", // 填充颜色
stroke: 'black', // 边框颜色
strokeWidth: 1, // 边框宽度
draggable: true, // 设置可拖拽
});
layer.add(rect1); // 将第一个矩形添加到图层
// 创建第二个矩形
const rect2 = new Konva.Rect({
name: "rect", // 同样的名称,可能导致选择问题
x: clientWidth / 2 + 100, // 设置 x 坐标
y: clientHeight / 2, // 设置 y 坐标
width: 200, // 矩形的宽度
height: 100, // 矩形的高度
fill: "#ff00ff", // 填充颜色
stroke: 'black', // 边框颜色
strokeWidth: 1, // 边框宽度
draggable: true, // 设置可拖拽
});
layer.add(rect2); // 将第二个矩形添加到图层
// 新增一个矩形用于框选功能
const selectionRect = new Konva.Rect({
fill: "rgba(0,0,255,0.1)", // 填充颜色
visible: false, // 初始时隐藏
stroke: "rgba(0,0,255,0.5)", // 边框颜色
strokeWidth: 1, // 边框宽度
});
layer.add(selectionRect); // 将选择矩形添加到图层
layer.add(tr); // 将 Transformer 添加到图层
// 点击图形时进行编辑,点击画布则取消编辑
stage.on("click tap", (e) => {
const dom = e.target; // 获取点击的目标
if (dom.getType() === "Shape") { // 判断目标是否为形状
tr.nodes([dom]); // 选中该形状
} else {
tr.nodes([]); // 如果点击的是画布,则清空选中
}
});
let x1 = 0, y1 = 0, x2 = 0, y2 = 0; // 初始化坐标变量
// 鼠标按下时开始框选
stage.on("mousedown touchstart", (e) => {
if (e.target !== stage) { // 如果点击的不是画布
return; // 结束函数
}
e.evt.preventDefault(); // 阻止默认行为
// 获取鼠标位置
const { x, y } = stage.getPointerPosition() as Konva.Vector2d;
x1 = x; // 记录起始点 x 坐标
x2 = x; // 记录起始点 x 坐标
y1 = y; // 记录起始点 y 坐标
y2 = y; // 记录起始点 y 坐标
selectionRect.visible(true); // 显示选择框
selectionRect.width(0); // 初始化宽度为 0
selectionRect.height(0); // 初始化高度为 0
});
// 鼠标移动时更新选择框
stage.on("mousemove touchmove", (e) => {
if (!selectionRect.visible()) { // 如果选择框不可见
return; // 结束函数
}
const { x, y } = stage.getPointerPosition() as Konva.Vector2d; // 获取当前鼠标位置
x2 = x; // 更新结束点 x 坐标
y2 = y; // 更新结束点 y 坐标
// 更新选择矩形的属性
selectionRect.setAttrs({
x: Math.min(x1, x2), // 设置选择框的 x 坐标
y: Math.min(y1, y2), // 设置选择框的 y 坐标
width: Math.abs(x1 - x2), // 设置选择框的宽度
height: Math.abs(y1 - y2), // 设置选择框的高度
});
});
// 鼠标松开时完成选择
stage.on("mouseup touchend", (e) => {
if (!stage || !selectionRect.visible()) { // 如果舞台不存在或选择框不可见
return; // 结束函数
}
// 隐藏选择框
selectionRect.visible(false);
// 获取所有矩形形状并进行选择
const shapes = stage.find(".rect"); // 查找所有矩形
const box = selectionRect.getClientRect(); // 获取选择框的边界
const selected = shapes.filter((shape) =>
Konva.Util.haveIntersection(box, shape.getClientRect()) // 判断是否与选择框相交
);
tr.nodes(selected); // 将选中的形状传给 Transformer
});
}
</script>
<style scoped lang="scss">
.rect {
padding: 20px; // 设置外边距
#canvas {
background-color: #eee; // 设置背景颜色
border: 1px solid #666; // 设置边框
height: calc(100vh - 42px); // 设置画布高度
}
}
</style>
逻辑过程
逻辑步骤说明
- 创建画布容器 - 在模板中定义画布的容器。
- 导入 Vue 和 Konva - 引入所需的库和模块。
- 初始化变量 - 创建舞台、层和变换器的实例。
- 挂载后初始化 - 在组件挂载时调用初始化函数。
- 获取 DOM 元素 - 获取画布的 DOM 元素,准备进行绘制。
- 创建舞台 - 创建 Konva 的舞台实例,并设置画布的尺寸。
- 添加层到舞台 - 将图层添加到舞台,以便在其上绘制图形。
- 创建矩形 - 依次创建第一个和第二个矩形,并设置其属性。
- 添加矩形到层 - 将创建的矩形添加到图层。
- 创建选择矩形 - 创建一个用于框选的矩形,但初始时设置为不可见。
- 处理点击事件 - 为舞台添加点击事件,以便选择图形或清空选择。
- 鼠标事件处理 - 处理鼠标按下、移动和松开事件以实现框选功能。
- 更新选择框 - 在鼠标移动时更新选择框的位置和大小。
- 完成选择 - 在鼠标松开时,计算选择的矩形,并将其传递给变换器。
- 样式设置 - 最后设置样式,定义画布的外观