【canvas】在Vue3+ts中实现 canva内的矩形拖动操作。

编程入门 行业动态 更新时间:2024-10-13 14:28:09

【canvas】在Vue3+ts中实现 canva内的<a href=https://www.elefans.com/category/jswz/34/1759498.html style=矩形拖动操作。"/>

【canvas】在Vue3+ts中实现 canva内的矩形拖动操作。

前言

canvas内的显示内容如何拖动?
这里提供一个 canvas内矩形移动的解决思路。

描述

如何选中canvas里的某部分矩形内容,然后进行拖动?
我的解决思路:

  1. **画布搭建。**用一个div将canvas元素包裹,设置宽高,div设置成相对定位(relative),canvas设置绝对定位(absolute)。
  2. 在往canvas内添加内容时,请保存添加内容的相关属性,长宽、位置、样式等,以此确定这部分内容的初始状态。例如:往canvas加一个矩形 ,就要先保存一下它的宽高和原点。
  3. 确定选中的内容。并与第一步保存的相关内容数据匹配。 由于canvas内添加的内容无法进行事件绑定,我们需要靠给canvas绑定点击事件,并根据点击位置确定哪部分内容被选中了。
  4. 生成可操作盒子。通过选中内容的数据,生成一个新的Dom元素盒子,并清除canvas内当前选中内容部分。给dom盒子绑定移动事件(mouse模拟拖动)。
  5. 拖动结束后,更新选中内容数据,在结束区域,canvas重新绘制

实现

1.画布搭建

   <div class="content" ref="canvasContent"><canvas id="canvas" ref="canvas" @click="canvasClickFn"></canvas></div>
.content {position: relative;width: 800px;height: 600px;}#canvas {position: absolute;width: 800px;height: 600px;border: 1px solid #000;background-color: #fafafa;}

这一步要保证外层盒子和canvas大小一致。

2. 初始化canvas内容

在往canvas内添加内容时,请保存添加内容的相关属性,长宽、位置、样式等,以此确定这部分内容的初始状态。

  import { onMounted, reactive, ref, type Ref } from 'vue';interface DivStyle {boder?: string;backgroundColor?: string;width?: string;height?: string;}interface DiagramObj {id: string | number;path: Float32Array;origin: Array<number>;type: string;width?: number;height?: number;r?: number;style?: DivStyle;}let ctx: CanvasRenderingContext2D | null | undefined;const canvasContent: Ref<HTMLElement | null> = ref(null);const canvas: Ref<HTMLCanvasElement | null> = ref(null);const diagramObjArr: Array<DiagramObj> = reactive([]);const initCanvas = () => {if (canvas.value) {ctx = canvas.value?.getContext('2d');canvas.value.width = 800;canvas.value.height = 600;}};onMounted(() => {initCanvas();if (ctx) {let rect1 = new Float32Array([1, 1, 50, 1, 50, 30, 1, 30]);let rectObj = {id: 'rect1',path: rect1,origin: [1, 1],width: 50,height: 30,type: 'rect',style: {boder: '1px solid #000',backgroundColor: '#fff',width: '50px',height: '30px',},children: [],};drawRect(ctx, rect1);diagramObjArr.push(rectObj);}});//  绘制图形function drawRect(ctx: CanvasRenderingContext2D, array: Float32Array) {if (array.length % 2 !== 0) {console.error('drwaRect函数Float32Array参数长度需要偶数位');return;}ctx.beginPath();for (let i = 0; i < array.length; i += 2) {let x = array[i];let y = array[i + 1];if (i === 0) {ctx.moveTo(x, y);} else {ctx.lineTo(x, y);}}ctx.closePath();ctx.stroke();}

rectObj是一个原点1,1;宽50,高30的盒子,然后 根据canvas路径api绘制图形。

3. 选中内容

绑定canvas点击事件,却定点击位置和点击位置下的内容。

  const canvasClickFn = (e: MouseEvent) => {let point = [e.offsetX, e.offsetY];let res = isGraphIstersection(point, diagramObjArr[0]);if (res && ctx) {console.log('在内部::', res.width);//  在图形正上方创建可操作图形createElementFn(canvasContent.value, res);//  清除该区域clearRect(ctx, [...res.origin, res.width, res.height]);}};function clearRect(ctx: CanvasRenderingContext2D, array: Array<number | undefined>) {const [x, y, width, height] = array as Array<number>;//  把1px 的边框算上ctx.clearRect(x - 1, y - 1, width + 2, height + 2);}// 圆点 和 多边形相交检测function isGraphIstersection(point: Array<number>, target: DiagramObj) {const { origin, width, height, r } = target;let apogee = [0, 0];//  求两矩形形中心点距离switch (target.type) {case 'rect'://  矩形  坐标轴法,不考虑矩形旋转if (!width || !height) return false;//  最远点apogee = [origin[0] + width, origin[1] + height];if (point[0] >= origin[0] &&point[0] <= apogee[0] &&point[1] >= origin[1] &&point[1] <= apogee[1]) {return target;}return false;case 'circle':if (!r) return false;if (Math.pow(Math.abs(point[0] - origin[0]), 2) +Math.pow(Math.abs(point[1] - origin[0]), 2) <r * r) {return target;}return false;case 'polygon':return false;}}

圆点 和 多边形相交检测 这个函数我只简单实现了矩形和圆形的检测(不考虑旋转)。如果想多检测其他的形状,需要自行实现。

4. 生成可操作盒子

根据选中的数据生成可操作盒子,盒子绑定事件,实现拖动
 function createElementFn(source: HTMLElement | null, obj: DiagramObj) {const { width, height, origin, style } = obj;if (!source || !width || !height) return;const div = document.createElement('div');div.setAttribute('style',`position:absolute;top:${origin[1]}px;left:${origin[0]}px;width:${style?.width};height:${style?.height};border:${style?.boder};background-color:${style?.backgroundColor};box-shadow: 0px 0px 3px skyblue;`,);let divClickLeft = 0,divClickTop = 0; //  元素点击时本身偏移量let isStart = false;let finallyLeft = origin[0],finallyTop = origin[1]; // 最终偏移量div.onmousedown = (e: MouseEvent) => {divClickLeft = e.offsetX as number;divClickTop = e.offsetY as number;isStart = true;};div.onmousemove = (e: MouseEvent): void => {if (!isStart) return;const parentV = source.getBoundingClientRect();const [left, top] = [e.pageX - parentV.left - divClickLeft,e.pageY - parentV.top - divClickTop,];if (left < 0 ||top < 0 ||left > parentV.width - (width as number) ||top > parentV.height - (height as number))return;e.target.style.top = top + 'px';e.target.style.left = left + 'px';finallyLeft = left;finallyTop = top;};div.onmouseup = div.onmouseleave = (e: MouseEvent) => {if (!isStart) return;isStart = false;//  拖动好后在新区域重新绘画let newRectObj: DiagramObj = obj;let pw = finallyLeft + width;let ph = finallyTop + height;Object.assign(newRectObj, {path: new Float32Array([finallyLeft, finallyTop, pw, finallyTop, pw, ph, finallyLeft, ph]),origin: [finallyLeft, finallyTop],} as DiagramObj);if (ctx) {drawRect(ctx, newRectObj.path);let index = diagramObjArr.findIndex((item) => item.id === newRectObj.id);diagramObjArr.splice(index, 1, newRectObj);source.removeChild(div);}};

5.拖动完成后重新绘制图形

拖动完成后,在新的位置重新绘制图形。需要在生成盒子的鼠标抬起和鼠标移出实现。

div.onmouseup = div.onmouseleave = (e: MouseEvent) => {if (!isStart) return;isStart = false;//  拖动好后在新区域重新绘画let newRectObj: DiagramObj = obj;let pw = finallyLeft + width;let ph = finallyTop + height;Object.assign(newRectObj, {path: new Float32Array([finallyLeft, finallyTop, pw, finallyTop, pw, ph, finallyLeft, ph]),origin: [finallyLeft, finallyTop],} as DiagramObj);if (ctx) {drawRect(ctx, newRectObj.path);let index = diagramObjArr.findIndex((item) => item.id === newRectObj.id);diagramObjArr.splice(index, 1, newRectObj);source.removeChild(div);}};

效果

canvas移动


效果地址:

由于是模拟的拖动,不能拖动过快,下次想办法优化下,下次一定。

结语

结束了。 这个canvas拖动如果封装好的话,感觉是很有用的。

更多推荐

【canvas】在Vue3+ts中实现 canva内的矩形拖动操作。

本文发布于:2023-11-15 10:58:45,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1598507.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:矩形   拖动   操作   canvas   canva

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!