关于canvas的缩放和位移实战的一点点总结

1,884 阅读2分钟

关于canvas的缩放和位移,这里做一些总结。

变量定义

defuaultScale:canvas画布的精度,也是缩放的初始值

offsetX:x方向的偏移量

offsetY:y方向的偏移量

scale:画布缩放比例,默认值:defaultScale

realScale:真实缩放比例,默认值为1,公式为realScale = scale / defaultScale

centerX:缩放中心点x坐标

centerY:缩放中心点y坐标

lastX:记录最后移动canvas的点的x坐标

lastY:记录最后移动canvas的点的y坐标

lastD:记录两指间最后的距离

使用

初始化:

如果canvas处于隐藏状态将获取不到宽高

设置画布的精度,将影响画布的清晰程度。

canvas.width = canvas.offsetWidth * defaultScale;
canvas.height = canvas.offsetHeight * defaultScale;
context = canvas.getContext('2d');

通过context.save - context.restore来重置每次的位移和缩放属性,从而达到不影响其他数据的目的。

当保留缩放中心点时,真实偏移量需要将中心点考虑进来。

function draw(){
    context.save();
    clear(); // 清除上次的绘制
    
    // 真实偏移量
    const offsetX0 = offsetX - centerX + centerX / realScale;
    const offsetY0 = offsetY - centerY + centerY / realScale;

    context.scale(scale, scale);
    context.translate(offsetX0, offsetY0);
    
    drawOther(); // 绘制其他物体
    context.restore();
    drawDebug(); // 绘制debug面板
}

缩放和位移的步长计算

开始触屏:

开始缩放时,中心点位置可能发生变化,需要补齐其偏差。

public handleTouchStart(e: TouchEvent) {
    const { clientX, clientY } = e.touches[0];
    
    // 记录起始点
    lastX = clientX;
    lastY = clientY;
    
     // 双指视为缩放
     if (e.touches.length === 2) {
        // 记录两指间的距离
        lastD = Math.sqrt(
          (clientX - e.touches[1].clientX) * (clientX - e.touches[1].clientX) +
            (clientY - e.touches[1].clientY) * (clientY - e.touches[1].clientY),
        );
     
        const _centerX = (clientX + e.touches[1].clientX) / 2;
        const _centerY = (clientY + e.touches[1].clientY) / 2;
        offsetX +=
          ((1 - realScale) * (centerX - _centerX)) / realScale;
        offsetY +=
          ((1 - realScale) * (centerY - _centerY)) / realScale;

        centerX = _centerX;
        centerY = _centerY;
     }
}

触屏移动:

偏移步长 = 位移量 / 真实缩放

缩放步长 = 两指间距位移量 * 精度,(精度用于调节缩放幅度)

public handleTouchMove(e: TouchEvent) {
    const { clientX, clientY } = e.touches[0];
    
    if (e.touches.length === 1) {
        offsetX += (clientX - lastX) / realScale;
        offsetY += (clientY - lastY) / realScale;
    }else{
        const distance = Math.sqrt(
          (clientX - e.touches[1].clientX) * (clientX - e.touches[1].clientX) +
            (clientY - e.touches[1].clientY) * (clientY - e.touches[1].clientY),
        );
        // 0.01为每次缩放的幅度,可以视情况进行调节
        const tmpScale = scale + (distance - lastD) * 0.01;
        if (tmpScale <= 0) { // 防止缩放造成物体反向
          return;
        }
        if (lastD) {
          scale = tmpScale;
        }
        // 保留最后两指间的距离
        lastD = distance;
    }
    
    // 保留最后移动的点
    lastX = clientX;
    lastY = clientY;
}

坐标点映射

有时候点击canvas,需要将点击坐标映射到绘制的物体上,这里给出坐标的映射关系:

映射坐标 = 真实坐标 / 真实缩放值 - 真实偏移量

 public transorm(
    x: number,
    y: number,
    reverse = false,
  ) {
    const offsetX0 = offsetX - centerX + centerX / realScale;
    const offsetY0 = offsetY - centerY + centerY / realScale;

    if (reverse) {
      // 逆映射
      return {
        x: (x + offsetX0) * realScale,
        y: (y + offsetY0) * realScale,
      };
    }
    return {
      x: x / realScale - offsetX0,
      y: y / realScale - offsetY0,
    };
}