canvas 可视化操作-拖拽&缩放&移动
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));
})