canvas动态线条

  function handleExecute(ctx, keyPoints) {
    // 设置线条样式
    ctx.lineWidth = 1;
    ctx.strokeStyle = '#24B7B3';
    // 顺序定义折线上各个转折点的坐标

    let prevX = keyPoints[0].x;
    let prevY = keyPoints[0].y;
    let nextX;
    let nextY;
    // 第一帧执行的时间
    let startTime;
    // 期望动画持续的时间
    const duration = 900;

    // 动画被切分成若干段,每一段所占总进度的比例
    const partProportion = 1 / (keyPoints.length - 1);
    // 缓存绘制第n段线段的n值,为了在进行下一段绘制前把这一段线段的末尾补齐
    let lineIndexCache = 1;

    /*
     * 动画帧绘制方法.
     * currentTime是requestAnimation执行回调方法step时会传入的一个执行时的时间(由performance.now()获得).
     * */
    const step = currentTime => {
      // 第一帧绘制时记录下开始的时间
      !startTime && (startTime = currentTime);
      // 已经过去的时间(ms)
      const timeElapsed = currentTime - startTime;
      // 动画执行的进度 {0,1}
      let progress = Math.min(timeElapsed / duration, 1);

      // 描述当前所绘制的是第几段线段
      const lineIndex = Math.min(
        Math.floor(progress / partProportion) + 1,
        keyPoints.length - 1,
      );

      //  当前线段的进度 {0,1}
      const partProgress =
        (progress - (lineIndex - 1) * partProportion) / partProportion;

      // 绘制方法
      const draw = () => {
        ctx.beginPath();
        ctx.moveTo(prevX, prevY);
        // 当绘制下一段线段前,把上一段末尾缺失的部分补齐
        if (lineIndex !== lineIndexCache) {
          ctx.lineTo(keyPoints[lineIndex - 1].x, keyPoints[lineIndex - 1].y);
          lineIndexCache = lineIndex;
        }
        prevX = nextX = ~~(
          keyPoints[lineIndex - 1].x +
          (keyPoints[lineIndex].x - keyPoints[lineIndex - 1].x) * partProgress
        );
        prevY = nextY = ~~(
          keyPoints[lineIndex - 1].y +
          (keyPoints[lineIndex].y - keyPoints[lineIndex - 1].y) * partProgress
        );
        ctx.lineTo(nextX, nextY);
        ctx.stroke();
      };
      draw();

      if (progress < 1) {
        requestAnimationFrame(step);
      } else {
        console.log('动画执行完毕');
      }
    };

    requestAnimationFrame(step);
  }
 
 useEffect(() => {
    const canvas = canvasRef.current;
    if (canvas) {
      const ctx = canvas.getContext('2d');
      // 先清除画布(一次性清除画布)
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      // 获取每个 catalog 元素的位置
      catalogRefs.current.forEach((catalogRef, index) => {
        if (catalogRef) {
          const rect = catalogRef.getBoundingClientRect();
          console.log(
            `坐标${index}:`,
            rect.left,
            rect.top,
            rect.width,
            rect.height,
          );
        }
      });
      // 线
      drawLine(160, 113, 160, 267, 113, 2, ctx); // 开始绘制动画
      // 圆
      drawCircle(ctx, 277, 113, 10);
      // 线
      drawLine(391, 113, 391, 347, 113, 2, ctx); // 开始绘制动画
      // 圆
      drawCircle(ctx, 342, 113, 10);

      const Three = [
        {x: 160, y: 377.5},
        {x: 195, y: 377.5},
        {x: 195, y: 284},
        {x: 270, y: 284},
      ];
      handleExecute(ctx, Three);
      drawCircle(ctx, 280, 284, 10);
  }, [canvasRef.current]);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值