HarmonyOS canvas动画如何实现逐帧动画

HarmonyOS
2025-01-09 17:45:16
浏览
收藏 0
回答 1
回答 1
按赞同
/
按时间
superinsect

参考示例如下:

@Entry
@Component
export default struct ProMode {
  // 创建渲染上下文设置和画布渲染上下文
  private settings: RenderingContextSettings = new RenderingContextSettings(true) // 初始化渲染上下文设置
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings) // 创建画布渲染上下文
  // 定义画布宽度和高度
  private canvasWidth: number = 0; // 画布宽度
  private canvasHeight: number = 0; // 画布高度
  private markBottom: number = 90; // 底部基线位置
  private longMarkSize: number = 50; // 中间刻度线的长度
  private midMarkSize: number = 28; // 较长的刻度线长度
  private shortMarkSize: number = 8; // 较短的刻度线长度
  private greenLineColor: string = '#6EC967'; // 绿色线的颜色
  private redLineColor: string = '#EF9D9D'; // 红色线的颜色
  private pitchDifferenceColor: string = 'rgba(245, 138, 138, 0.16)'; // 红色透明区域的颜色
  @State offsetText: string = '智能识别中' // 刻度上方文字
  @State offsetNum: number = 0 // 刻度偏移量
  @State isOffsetOk: boolean = false // 偏移量是否正常
  @State redX: number = 50;
  @State redXNew: number = 50;
  @State tickSpacing: number = 0;
  @State timeId: number = -1;
  @State isRunning: boolean = false;

  build() {
    Column() {
      // 弦刻度
      Column() {
        Text(this.offsetText)
          .fontSize('30fp')
          .fontColor(this.isOffsetOk ? '#78c371' : '#fff')
          .margin({ top: '70vp' })
      }

      // 刻度
      Column() {
        // 在这里使用 MeterView 组件
        Flex({
          direction: FlexDirection.Column,
          alignItems: ItemAlign.Center,
          justifyContent: FlexAlign.Center
        }) { // 创建Flex布局
          // 创建画布
          Canvas(this.context)
            .width('96%')// 设置画布宽度为100%
            .height('100%')// 设置画布高度为100%
            .backgroundColor('#transparent')// 设置画布背景颜色为白色
            .onReady(() => {
              this.canvasWidth = this.context.width; // 获取画布宽度
              this.canvasHeight = this.context.height; // 获取画布高度
              this.onDraw(); // 调用绘制函数
            })
            .onClick((event) => {
              console.log('event.x', event.x);
              this.redXNew = Math.floor((event.x - this.canvasWidth / 2) / this.tickSpacing) * 5;
              if (!this.isRunning) {
                clearInterval(this.timeId);
                this.isRunning = true;
                setTimeout(() => {
                  console.log('------redx =' + this.redXNew, 'this.redX=' + this.redX);
                  this.isRunning = false;
                  if (this.redXNew > this.redX) {
                    this.timeId = setInterval(() => {
                      if (this.redXNew > this.redX) {
                        this.redX++;
                        this.onDraw();
                      } else {
                        console.log('this.redXNew > this.redX')

                        this.redX = this.redXNew;
                        this.onDraw();
                        clearInterval(this.timeId)
                      }
                    }, 50)
                  }
                  if (this.redX > this.redXNew) {
                    this.timeId = setInterval(() => {
                      if (this.redXNew < this.redX) {
                        this.redX--;
                        this.onDraw();
                      } else {
                        console.log('this.redX > this.redXNew')
                        this.redX = this.redXNew;
                        this.onDraw();
                        clearInterval(this.timeId)
                      }
                    }, 50)
                  }
                }, 500)
              }
            })
            .borderColor('#5c000000')// 设置画布边框颜色
            .borderStyle(BorderStyle.Solid) // 设置画布边框样式为实线
        }
        .width('100%') // 设置Flex布局宽度为100%
        .height('100%') // 设置Flex布局高度为100%
      }
      .height('120vp')
      .width('100%')

      //浅灰色条框
      Column() {
        Row() {
          Text('-50b')
            .fontSize('16fp')
            .fontColor('#ffb5acac')
            .width('33%')
            .textAlign(TextAlign.Start);

          Text('0')
            .fontSize('16fp')
            .fontColor('#ffb5acac')
            .width('33%')
            .textAlign(TextAlign.Center);

          Text('#50')
            .fontSize('16fp')
            .fontColor('#ffb5acac')
            .width('33%')
            .textAlign(TextAlign.End);
        }
        .backgroundColor('#535353') // 浅灰色背景
        .height('20vp')
        .padding({ left: '10vp', right: '10vp' });
      }
      .height('50vp')
      .width('100%')
      .margin({ top: 0 });
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#494949')
  }

  // canvas绘制
  onDraw() {
    const context = this.context; // 获取渲染上下文
    context.clearRect(0, 0, this.canvasWidth, this.canvasHeight); // 清除画布
    context.save(); // 保存当前绘图状态
    context.translate(this.canvasWidth / 2, this.markBottom); // 平移到指定位置
    context.strokeStyle = '#79d0cece'; // 设置刻度线的笔触颜色
    context.lineWidth = 3; // 设置刻度线的笔触宽度
    context.lineCap = 'round'; // 设置刻度线末端为圆形
    // 计算刻度线的间隔
    const totalTicks = 21; // 刻度线的总数
    this.tickSpacing = (this.canvasWidth - 5) / (totalTicks - 1); // 每份刻度的宽度
    for (let i = 0; i < totalTicks; i++) { // 循环绘制21条刻度线
      const x = (i - Math.floor(totalTicks / 2)) * this.tickSpacing; // 计算刻度线位置
      context.beginPath(); // 开始新的路径
      if (i === Math.floor(totalTicks / 2)) { // 中间刻度线
        context.moveTo(x, 0); // 移动到起点
        context.lineTo(x, -this.longMarkSize); // 绘制中间最长刻度线
      } else if (i === 0 || i === 5 || i === 15 || i === 20) { // 较长的刻度线
        context.moveTo(x, 0); // 移动到起点
        context.lineTo(x, -this.midMarkSize); // 绘制较长的刻度线
      } else { // 较短的刻度线
        context.moveTo(x, 0); // 移动到起点
        context.lineTo(x, -this.shortMarkSize); // 绘制较短的刻度线
      }
      context.stroke(); // 描边路径
    }
    // 绘制绿色线(固定在中间)
    context.strokeStyle = this.greenLineColor;
    context.lineWidth = 3; // 设置绿色线的宽度
    context.beginPath();
    const greenX = 0; // 绿色线固定在中间位置
    const greenLineLength = this.canvasHeight * 0.6; // 绿色线的长度(根据画布高度的60%)
    context.moveTo(greenX, -greenLineLength / 1.4); // 从绿色线的顶部开始
    context.lineTo(greenX, greenLineLength / 7.0); // 画到绿色线的底部
    if (this.offsetNum !== 0) {
      context.stroke(); // 描边绿色线
    }
    // 计算每个刻度线的宽度
    const totalTicksInRange = 100; // 刻度总数(分成100份)
    const tickSpacingInRange = (this.canvasWidth - 5) / totalTicksInRange; // 每个刻度的宽度
    // 假设 redX 是你已经获得的值
    // const redX = 50; // 你给定的值
    animateTo({
      duration: 40000,
      curve: Curve.Ease,
      delay: 0,
      iterations: 1,
      playMode: PlayMode.Normal
    }, () => {
      // const redX = this.offsetNum; // 你给定的值
      // const redX = 40; // 你给定的值
      // 反推 redTickIndex
      const redTickIndex = Math.round(((this.redX + 50) / 100) * totalTicksInRange);
      // 确保 redTickIndex 在有效范围内
      const validRedTickIndex = Math.max(0, Math.min(redTickIndex, totalTicksInRange));
      // 计算 redLineLength(假设你需要这个变量)
      const redLineLength = this.canvasHeight * 0.6; // 红色线的长度(根据画布高度的60%)
      // 重新计算 redX(如果需要验证)
      const recalculatedRedX = (validRedTickIndex - (totalTicksInRange / 2)) * tickSpacingInRange;
      // 只有当 redTickIndex 不等于 50 时才绘制红色线
      if (validRedTickIndex !== 50) {
        // 绘制红色线
        context.strokeStyle = this.redLineColor;
        context.lineWidth = 3; // 设置红色线的宽度
        context.beginPath();
        context.moveTo(recalculatedRedX, -redLineLength / 1.4); // 从红色线的顶部开始
        context.lineTo(recalculatedRedX, redLineLength / 7); // 画到红色线的底部
        context.stroke(); // 描边红色线
        // 绘制红色透明区域
        const leftX = Math.max(-this.canvasWidth / 2, Math.min(greenX, recalculatedRedX)); // 计算左边界
        const rightX = Math.min(this.canvasWidth / 2, Math.max(greenX, recalculatedRedX)); // 计算右边界
        context.fillStyle = this.pitchDifferenceColor;
        context.beginPath();
        context.moveTo(leftX, -redLineLength / 1.4);
        context.lineTo(rightX, -redLineLength / 1.4);
        context.lineTo(rightX, redLineLength / 7);
        context.lineTo(leftX, redLineLength / 7);
        context.closePath();
        context.fill(); // 填充透明区域
      }
    })
    context.restore(); // 恢复绘图状态
  }
}
  • 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.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.
  • 186.
  • 187.
  • 188.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.
  • 214.
  • 215.
  • 216.
  • 217.
  • 218.
  • 219.
  • 220.
分享
微博
QQ
微信
回复
2025-01-09 19:24:58
相关问题
HarmonyOS Canvas 实现动画
822浏览 • 1回复 待解决
HarmonyOS 动画用什么实现
702浏览 • 1回复 待解决
HarmonyOS Canvas动画实现
324浏览 • 1回复 待解决
HarmonyOS 动画demo
895浏览 • 1回复 待解决
HarmonyOS 动画如何主动停止
602浏览 • 1回复 待解决
HarmonyOS 有没有动画组件
847浏览 • 1回复 待解决
HarmonyOS 属性动画怎么监听回调?
856浏览 • 1回复 待解决
属性动画如何实现宽高动画效果
2532浏览 • 1回复 待解决
有谁知道是否支持动画
3129浏览 • 1回复 待解决
HarmonyOS 组合动画如何实现
467浏览 • 1回复 待解决
HarmonyOS 如何实现WaveView动画
706浏览 • 1回复 待解决
HarmonyOS 如何实现RippleView动画
713浏览 • 1回复 待解决
HarmonyOS 动画实现
701浏览 • 1回复 待解决
如何应用属性动画实现宽高的动画
842浏览 • 1回复 待解决
HarmonyOS 如何实现呼吸灯动画
593浏览 • 1回复 待解决
HarmonyOS 如何实现音频声浪动画
1293浏览 • 1回复 待解决
HarmonyOS 如何实现旋转动画
899浏览 • 1回复 待解决
HarmonyOS如何实现动态缩放动画
865浏览 • 1回复 待解决