目录

实现跨页面的同步旋转图片动画

问题现象

如何在两个页面中实现Image组件的旋转动画同步,通过共享全局状态触发动画更新?

效果预览

背景知识

解决方案

通过AppStorage全局状态管理实现两个页面中Image组件旋转动画的同步,具体如下:

  1. 通过AppStorage共享startFlag状态:页面1通过AppStorage.set()修改状态;页面2通过AppStorage.get()读取状态。
  2. 使用@StorageProp('startFlag')装饰器绑定startFlag状态,当startFlag变化时,触发change()方法动态更新旋转角度和动画时长,从而控制Image的旋转动画。
    // PageOne
    @Entry
    @Component
    struct Index {
      @StorageProp('startFlag') @Watch('change') startFlag: boolean = false;
      @State rotateValue: number = 0;
      @State ifStop: number = -1;
      @State duration: number = 4000;
    
      change() {
        console.info(`startFlag: ${this.startFlag}`);
        if (this.startFlag) {
          this.duration = 4000;
          this.rotateValue = 360;
        } else {
          this.rotateValue = 0;
        }
      }
    
      build() {
        Column({ space: 30 }) {
          Button('点击')
            .onClick(() => {
              this.getUIContext().getRouter().pushUrl({
                url: 'pages/Index2',
                params: {
                  flag: '1',
                }
              });
            });
    
          // 此处'app.media.startIcon'仅作示例,请开发者自行替换,否则imageSource创建失败会导致后续无法正常执行。
          Image($r('app.media.startIcon'))
            .width(200)
            .aspectRatio(1)
            .borderRadius(100)
            .rotate({
              angle: this.rotateValue
            })
            .animation({
              duration: this.duration,
              iterations: -1, // 动画重复次数
              curve: Curve.Linear,
              delay: 1, // 延迟时间
              playMode: PlayMode.Normal
            });
    
          Button('旋转').onClick(() => {
            this.duration = 4000;
            this.rotateValue = 360;
            AppStorage.set('startFlag', true);
          });
    
          Button('停止').onClick(() => {
            this.duration = 0;
            this.rotateValue = 0;
            AppStorage.set('startFlag', false);
          });
        }
        .justifyContent(FlexAlign.Center)
        .height('100%')
        .width('100%');
      }
    }
    // PageTwo
    @Entry
    @Component
    struct Index2 {
      @State rotateValue: number = 0;
      @State duration: number = 4000;
      @State pauseFlag: boolean = false;
    
      onPageShow(): void {
        const startFlag = AppStorage.get('startFlag') as boolean;
        console.info(`页面二:startFlag: ${startFlag}`);
        if (startFlag) {
          this.duration = 4000;
          this.rotateValue = 360;
        } else {
          this.duration = 0;
          this.rotateValue = 0;
        }
      }
    
      build() {
        Column({ space: 30 }) {
          // 此处'app.media.startIcon'仅作示例,请开发者自行替换,否则imageSource创建失败会导致后续无法正常执行。
          Image($r('app.media.startIcon'))
            .width(200)
            .aspectRatio(1)
            .borderRadius(100)
            .rotate({
              angle: this.rotateValue
            })
            .animation({
              duration: this.duration,
              iterations: -1, // 动画重复次数
              curve: Curve.Linear,
              delay: 1, // 延迟时间
              playMode: PlayMode.Normal
            });
    
          Button('旋转').onClick(() => {
            this.duration = 4000;
            this.rotateValue = 360;
            AppStorage.set('startFlag', true);
          });
    
          Button('停止').onClick(() => {
            this.duration = 0;
            this.rotateValue = 0;
            AppStorage.set('startFlag', false);
          });
    
          Button('返回').onClick(() => {
            this.getUIContext().getRouter().back({
              url: 'pages/Index',
              params: this.pauseFlag
            });
          });
        }
        .justifyContent(FlexAlign.Center)
        .height('100%')
        .width('100%');
      }
    }
    {
      "src": [
        "pages/Index",
        "pages/Index2"
      ]
    }