#鸿蒙通关秘籍#如何利用属性动画实现一镜到底动效,实现组件A到组件B的过渡动画?

HarmonyOS
20h前
浏览
收藏 0
回答 1
待解决
回答 1
按赞同
/
按时间
系统小鬼才

属性动画赋予了开发者更高的自由度,适合用来在组件A到组件B之间实现一镜到底动效。获取两个组件的位置信息后,通过属性动画实现位置过渡。

import { curves } from '@kit.ArkUI';

class RectJson {
  $rect: Array<number> = []
}

class RectInfo {
  left: number = 0;
  top: number = 0;
  right: number = 0;
  bottom: number = 0;
}

const LARGE_IMAGE_SIZE = 240;
const SMALL_IMAGE_SIZE = 40;
const LARGE_IMAGE_RADIUS = 30;
const SMALL_IMAGE_RADIUS = 15;

@Entry
@Component
struct Index {
  @State showImage: Resource = $r('app.media.flower');
  @State isTempImageShow: boolean = false;
  @State clickedImage: Resource = $r('app.media.flower');
  @State translateX: number = 0;
  @State translateY: number = 0;
  @State positionX: number = 0;
  @State positionY: number = 0;
  @State showImageAlphaValue: number = 1;
  @State tempImageSize: number = SMALL_IMAGE_SIZE;
  @State imageList: Resource[] = [$r('app.media.island'), $r('app.media.sky'),
    $r('app.media.spring'), $r('app.media.tree')];
  @State radius: number = SMALL_IMAGE_RADIUS;
  @State isEnabled: boolean = true;

  private calculatedTranslateX: number = 0;
  private calculatedTranslateY: number = 0;
  private animationCount: number = 0;
  private formerImageSource: Resource = $r('app.media.flower');

  private calculateData(key: string): void {
    let clickedImageInfo = this.getRectInfoById(key);
    let showImageInfo = this.getRectInfoById('showImage');
    let rootStackInfo = this.getRectInfoById('rootStack');
    this.positionX = px2vp(clickedImageInfo.left - rootStackInfo.left);
    this.positionY = px2vp(clickedImageInfo.top - rootStackInfo.top);
    this.calculatedTranslateX = px2vp(showImageInfo.left - clickedImageInfo.left);
    this.calculatedTranslateY = px2vp(showImageInfo.top - clickedImageInfo.top);
  }

  private getRectInfoById(key: string): RectInfo {
    try {
      let strJson: string = getInspectorByKey(key);
      let rect: RectJson = JSON.parse(strJson);
      let rectInfo: Array<object> = JSON.parse('[' + rect.$rect + ']');
      let rect_left: number = Number(JSON.parse('[' + rectInfo[0] + ']')[0]);
      let rect_top: number = Number(JSON.parse('[' + rectInfo[0] + ']')[1]);
      let rect_right: number = Number(JSON.parse('[' + rectInfo[1] + ']')[0]);
      let rect_bottom: number = Number(JSON.parse('[' + rectInfo[1] + ']')[1]);
      return { left: rect_left, top: rect_top, right: rect_right, bottom: rect_bottom };
    } catch (err) {
      console.log(`getPositionById error ${err}`);
    }
    return { left: 0, top: 0, right: 0, bottom: 0 };
  }

  private onTouchDown(index: number): void {
    this.radius = SMALL_IMAGE_RADIUS;
    this.clickedImage = this.imageList[index];
    this.calculateData(index.toString());
    this.isTempImageShow = true;
    this.formerImageSource = this.imageList[index];
  }

  private onTouchUp(index: number): void {
    this.imageList[index] = this.showImage;
    this.animationCount++;
    this.doOneShotAnimation();
    this.isEnabled = false;
  }

  private doOneShotAnimation(): void {
    animateTo({
      curve: curves.springMotion(),
      onFinish: () => {
        if (this.animationCount === 1) {
          this.onAnimationFinish();
        }
      }
    }, () => {
      this.tempImageSize = LARGE_IMAGE_SIZE;
      this.translateX = this.calculatedTranslateX;
      this.translateY = this.calculatedTranslateY;
      this.showImageAlphaValue = 0;
      this.radius = LARGE_IMAGE_RADIUS;
    });
  }

  private onAnimationFinish(): void {
    this.showImage = this.formerImageSource;
    this.showImageAlphaValue = 1.0;
    this.isTempImageShow = false;
    this.translateX = 0;
    this.translateY = 0;
    this.positionX = 0;
    this.positionY = 0;
    this.tempImageSize = SMALL_IMAGE_SIZE;
    this.animationCount--;
    this.isEnabled = true;
  }

  build() {
    Stack() {
      Column() {
        Column({ space: 40 }) {
          Image(this.showImage)
            .size({ width: 240, height: 240 })
            .syncLoad(true)
            .opacity(this.showImageAlphaValue)
            .key('showImage')
            .borderRadius(30)

          Row() {
            Image($r('app.media.back'))
              .size({ width: 20, height: 20 })

            Image($r('app.media.play'))
              .size({ width: 20, height: 20 })

            Image($r('app.media.forward'))
              .size({ width: 20, height: 20 })
          }
          .justifyContent(FlexAlign.SpaceEvenly)
          .width(240)
        }
        .margin({ top: 20 })
        .padding({ top: 20 })
        .clip(true)
        .borderRadius(30)
        .backgroundColor('#40808080')
        .size({ width: 320, height: 360 })

        Column({ space: 20 }) {
          ForEach(this.imageList, (imageResource: Resource, index: number) => {
            Row({ space: 20 }) {
              Image(imageResource)
                .size({ width: SMALL_IMAGE_SIZE, height: SMALL_IMAGE_SIZE })
                .key(index.toString())
                .margin({ left: 10 })
                .borderRadius(15)

              Text('专辑' + (index + 1))
                .size({ width: 200 })
            }
            .size({ width: '90%', height: 60 })
            .borderRadius(20)
            .backgroundColor('#40808080')
            .enabled(this.isEnabled)
            .onTouch((event: TouchEvent) => {
              if (event.type === TouchType.Down) {
                this.onTouchDown(index);
              } else if (event.type === TouchType.Up) {
                this.onTouchUp(index);
              }
            })
          })
        }
        .width('100%')
        .justifyContent(FlexAlign.Center)
        .margin({ top: 40 })
      }
      .size({ width: '100%', height: '100%' })

      if (this.isTempImageShow) {
        Image(this.clickedImage)
          .size({ width: this.tempImageSize, height: this.tempImageSize })
          .translate({ x: this.translateX, y: this.translateY })
          .position({ x: this.positionX, y: this.positionY })
          .borderRadius(this.radius)
      }
    }
    .size({ width: '100%', height: '100%' })
    .key('rootStack')
  }
}
分享
微博
QQ
微信
回复
17h前
相关问题
如何应用属性动画实现宽高动画
346浏览 • 1回复 待解决
属性动画如何实现宽高动画效果
1980浏览 • 1回复 待解决