canvas拖拽+缩放的实现
/* canvas 可视化操作-拖拽&缩放&移动 */ const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); const stautsConfig = { //拖拽开始 IDLE: 0, //拖拽中 DRAG_START: 1, //拖拽结束 DRAGGING: 2 } //画布信息 const canvasInfo = { status: stautsConfig.IDLE, //拖拽状态 dragTarget: null, //拖拽对象 lastEvtPos: { x: null, y: null }, //计算偏移量坐标 offsetEvtPos: { x: null, y: null }, //偏移事件位置 offset: { x: 0, y: 0 }, //缩放偏移 scale: 1, //缩放比例 scaleStep: 0.1, //每次缩放产生的变化量 maxScale: 2, //最大缩放倍数 minScale: 0.5 //最小缩放倍数 } const cirlces = []; //画圆 const drawCircle = (ctx, cx, cy, r) => { ctx.save(); ctx.beginPath(); ctx.strokeStyle = 'blue'; ctx.arc(cx, cy, r, 0, Math.PI*2); ctx.stroke(); ctx.closePath(); ctx.restore(); } // ctx.translate(100, 0); //视图层绘制 drawCircle(ctx, 100, 100, 20); //数据层记录 cirlces.push({ x: 100, y: 100, r: 20 }) drawCircle(ctx, 200, 200, 30); cirlces.push({ x: 200, y: 200, r: 30 }) /*————————————————拖拽———————————————————*/ //画布位置 const getCanvasPosition = e => { return { x: e.offsetX, y: e.offsetY } } //获取距离 const getDistance = (p1, p2) => { return Math.sqrt((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2); } //判断是否在圆内 const ifInCirlce = (pos) => { for (let i = 0; i < cirlces.length; i++) { //如果两个距离小于半径就返回 if (getDistance(cirlces[i], pos) < cirlces[i].r) { return cirlces[i] } } return false; } //鼠标按下 canvas.addEventListener('mousedown', e => { const canvasPosition = getCanvasPosition(e); const cirlceRef = ifInCirlce(canvasPosition); //如果拖拽对象条件成立,系统进入拖拽状态 if (cirlceRef) { //记录拖拽目标、状态、偏移量位置、偏移事件位置 canvasInfo.dragTarget = cirlceRef; canvasInfo.status = stautsConfig.DRAG_START; canvasInfo.lastEvtPos = canvasPosition; canvasInfo.offsetEvtPos = canvasPosition; } }) //鼠标移动 canvas.addEventListener('mousemove', e => { const canvasPosition = getCanvasPosition(e); //如果在某个圆内,修改拖动中的鼠标样式 if (ifInCirlce(canvasPosition)) { canvas.style.cursor = 'all-scroll'; } else { canvas.style.cursor = '' } //如果第一次距离和第二次之间大于5,代表真正的拖动(防止抖动,一按下就移动的问题) if (canvasInfo.status === stautsConfig.DRAG_START && getDistance(canvasPosition, canvasInfo.lastEvtPos) > 5) { console.log('try'); canvasInfo.status = stautsConfig.DRAGGING; //更新偏移事件位置 canvasInfo.offsetEvtPos = canvasPosition; } else if (canvasInfo.status === stautsConfig.DRAGGING){ console.log('拖拽中'); const { dragTarget } = canvasInfo; dragTarget.x += (canvasPosition.x - canvasInfo.offsetEvtPos.x); dragTarget.y += (canvasPosition.y - canvasInfo.offsetEvtPos.y); //拖拽时候清空并重绘圆 ctx.clearRect(0, 0, canvas.width, canvas.height); cirlces.forEach(item => drawCircle(ctx, item.x, item.y, item.r)); canvasInfo.offsetEvtPos = canvasPosition; } }) //鼠标抬起 canvas.addEventListener('mouseup', e => { if (canvasInfo.status === stautsConfig.DRAGGING) canvasInfo.status = stautsConfig.IDLE; }) /*————————————————滚轮缩放———————————————————*/ canvas.addEventListener('wheel', e => { e.preventDefault(); const canvasPosition = getCanvasPosition(e); //计算出鼠标在画布的坐标位置 const realCanvasPosition = { x: canvasPosition.x - canvasInfo.offset.x, y: canvasPosition.y - canvasInfo.offset.y } //变化偏移量 const { scaleStep } = canvasInfo; const deltaX = realCanvasPosition.x / canvasInfo.scale * scaleStep; const deltaY = realCanvasPosition.y / canvasInfo.scale * scaleStep; //上下滚轮分别赋值 if (e.wheelDelta > 0) { canvasInfo.offset.x -= deltaX; canvasInfo.offset.y -= deltaY; canvasInfo.scale += scaleStep; } else { canvasInfo.offset.x += deltaX; canvasInfo.offset.y += deltaY; canvasInfo.scale -= scaleStep; } //通过矩阵变换重置当前的坐标系 ctx.setTransform(canvasInfo.scale, 0, 0, canvasInfo.scale, canvasInfo.offset.x, canvasInfo.offset.y); ctx.clearRect(0, 0, canvas.width, canvas.height); cirlces.forEach(item => drawCircle(ctx, item.x, item.y, item.r)); })