• canvas 简单直线轨迹运动与线性插值计算


    canvas 简单直线轨迹运动与线性插值计算

    一、canvas 直线轨迹运行

    添加 canvas 语法提示

    通过/** @type {HTMLCanvasElement} */代码块 来添加canvas的语法提示

    <body>
      <canvas id="canvas">canvas>
    body>
    <script>
      /** @type {HTMLCanvasElement} */ 
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    获取 canvas,模拟demo数据
    /** @type {HTMLCanvasElement} */
    const canvas = document.getElementById("canvas");
    // 建议设置宽高的方式
    canvas.width = 600;
    canvas.height = 600;
    const coords = [
      [100, 100],
      [200, 200],
      [400, 200],
      [500, 300],
      [500, 500],
    ];
    const img = new Image();
    img.src = "img/arrow2.png";
    let t = 0; // 图片移动步进
    let index = 0; // 图片轨迹分段索引
    const imgW = 242 / 10;
    const imgH = 166 / 10;
    // 图片运动轨迹分段
    const animationCoords = [
      [coords[0], coords[1]],
      [coords[1], coords[2]],
      [coords[2], coords[3]],
      [coords[3], coords[4]],
    ];
    
    // 获取canvas 的上下文
    const ctx = canvas.getContext("2d");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    根据模拟点位数据 coords,画轨迹
    // 轨迹
    function drawLine() {
      ctx.beginPath();
      ctx.moveTo(coords[0][0], coords[0][1]);
      ctx.lineTo(coords[1][0], coords[1][1]);
      ctx.lineTo(coords[2][0], coords[2][1]);
      ctx.lineTo(coords[3][0], coords[3][1]);
      ctx.lineTo(coords[4][0], coords[4][1]);
      ctx.strokeStyle = "gray";
      ctx.stroke();
    }
    drawLine();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    运动中计算图片当前所在位置坐标与角度
    /**
     * @desc 根据t切割line坐标,计算当前点的角度值,用于动画
     * @parmas start 轨迹线段开始坐标
     * @parmas end 轨迹线段结束坐标
     */
    function computed(start, end, t) {
      const dx = end[0] - start[0];
      const dy = end[1] - start[1];
      const angle = Math.atan2(dy, dx);
    
      if (t > 1) return {};
    
      const x = start[0] + (end[0] - start[0]) * t;
      const y = start[1] + (end[1] - start[1]) * t;
      return {
        x,
        y,
        angle,
      };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    绘制图片
    function drawImage() {
      if (t >= 1) {
        t = 0; // 重置
        index++; // 进行下一分段的轨迹运动
        if (index === animationCoords.length) {
          index = 0; // 反复运动
        }
      }
      t += 0.01; // 控制动画速度
      const start = animationCoords[index][0];
      const end = animationCoords[index][1];
      const { x, y, angle } = computed(start, end, t);
      if (x === undefined) return;
      ctx.beginPath();
      ctx.translate(x, y); // 改变画布原点
      ctx.rotate(angle); // 旋转画布
      ctx.drawImage(img, -imgW / 2, -imgH / 2, imgW, imgH);
      // setTransform设置默认矩阵 恢复画布原点 避免后续画图坐标偏离。也可以通过save和restore的方式来恢复画布原点,save可以保存变换矩阵,在translate之前调用save
      ctx.setTransform(1, 0, 0, 1, 0, 0); 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    编写动画函数
    function animation() {
      // 清空画布 动画每一帧之前都需要清空画布,除非动画下一帧是全屏覆盖
      ctx.clearRect(0, 0, 600, 600); 
      requestAnimationFrame(animation); // 浏览器下一次渲染时执行动画函数
      drawImage();
      drawLine();
    }
    animation()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    二、canvas 中的线性插值

    已知 A,B 两点坐标,计算已知 x 或 y 的 C 点坐标,且 C 点在 AB 连线上

    线性插值计算方法
    /**
     * @desc 线性插值
     * @params coord1
     * @params coord2
     */
    function TDLI(coord1, coord2) {
      const scale = (coord1[0] - coord2[0]) / (coord1[1] - coord2[1]);
    
      return {
        getY: (x) => (x - coord1[0]) / scale + coord1[1],
        getX: (y) => (y - coord1[1]) * scale + coord1[0],
      };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    demo
    const A = [90, 100];
    const B = [205, 110];
    const tdli = TDLI(A, B);
    
    // 计算AB连线上 x坐标为 320的点
    const Cx = 320;
    const Cy = tdli.getY(Cx);
    
    ctx.beginPath();
    ctx.moveTo(A[0], A[1]);
    ctx.lineTo(B[0], B[1]);
    ctx.stroke();
    ctx.beginPath();
    ctx.moveTo(B[0], B[1]);
    ctx.lineTo(x, y);
    ctx.setLineDash([4, 16]);
    ctx.strokeStyle = "red";
    ctx.stroke();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述

  • 相关阅读:
    插件_创蓝图文滑动验证码
    信息系统项目管理师---第十四章 信息文档管理与配置管理历年考题
    容器内需要编辑文件,但无法下载和使用ed 、vi、vim、nano编辑器
    基于Python Scrapy爬虫改进KNN算法的网站分类系统
    Faiss原理和使用总结
    Python零基础入门-7 输入与输出
    ios的info.plist 配置
    V100 GPU服务器安装CUDA教程
    创建.NET MAUI程序
    动态规划 台阶问题四(爱思创)
  • 原文地址:https://blog.csdn.net/weixin_42508580/article/details/134332257