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)); })