滚动鼠标,可以缩放画布
<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.getRelativePointerPosition() 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
});
/缩放/
const SCALE_BY = 1.1; // 缩放倍数
stage.on("wheel", (e) => {
console.log('wheel event triggered'); // 检查事件触发
e.evt.preventDefault(); // 阻止默认的页面滚动
if (!stage) return; // 如果 stage 没有初始化,则退出
// 获取当前舞台的缩放比例
const oldScale = stage.scaleX();
// 获取鼠标指针在舞台坐标系内的位置
const pointer = stage.getPointerPosition() as Konva.Vector2d;
if (!pointer) return; // 如果鼠标位置无效,退出
// 计算当前鼠标点在缩放前的相对位置
const mousePointTo = {
x: (pointer.x - stage.x()) / oldScale,
y: (pointer.y - stage.y()) / oldScale,
};
// 判断滚轮方向:deltaY > 0 表示缩小,< 0 表示放大
let direction = e.evt.deltaY > 0 ? -1 : 1;
// 支持 ctrl 键反转缩放方向(可选功能)
if (e.evt.ctrlKey) {
direction = -direction;
}
// 根据滚轮方向计算新的缩放比例
const newScale = direction > 0 ? oldScale * SCALE_BY : oldScale / SCALE_BY;
// 更新舞台的缩放比例
stage.scale({ x: newScale, y: newScale });
// 计算缩放后舞台新的位置,以保持鼠标相对位置不变
const newPos = {
x: pointer.x - mousePointTo.x * newScale,
y: pointer.y - mousePointTo.y * newScale,
};
// 设置舞台的新位置
stage.position(newPos);
// 使用 batchDraw 来优化更新频率
stage.batchDraw();
});
}
</script>
<style scoped lang="scss">
.rect {
padding: 20px; // 设置外边距
#canvas {
background-color: #eee; // 设置背景颜色
border: 1px solid #666; // 设置边框
height: calc(100vh - 42px); // 设置画布高度
}
}
</style>