HarmonyOS 鸿蒙Next中为什么我的animateTo动画不执行?

HarmonyOS 鸿蒙Next中为什么我的animateTo动画不执行? 我希望在页面刚显示的时候给pagview加个从屏幕底部滑出的动画,但这个动画怎么都不执行,我还写了个demo,demo是正常的,就我的不执行,求助大佬。

代码:

import { PAGView } from "[@tencent](/user/tencent)/libpag";
import * as pag from '[@tencent](/user/tencent)/libpag';
import { AppUtil, DisplayUtil, NetworkUtil } from "[@pura](/user/pura)/harmony-utils";
import { Logger } from "utils";
import curves from "[@ohos](/user/ohos).curves";
import { fetchSleepData, SleepViewModel } from "../viewmodel/SleepViewModel";

const CENTER_PAG_VIEW_WIDTH = 200;
const CENTER_PAG_VIEW_HEIGHT = 200;
const FLOAT_DISTANCE = 6;
const SCREEN_HEIGHT = AppUtil.getUIContext().px2vp(DisplayUtil.getHeight())
const TAG='SleepView'
const STATUS_BAR_HEIGHT= AppUtil.getUIContext().px2vp(AppUtil.getStatusBarHeight())

[@ComponentV2](/user/ComponentV2)
export struct SleepView {
   viewModel: SleepViewModel = SleepViewModel.getInstance();
  bubbleCount=0;
  [@Local](/user/Local) centerPagViewController: pag.PAGViewController = new pag.PAGViewController();
  [@Local](/user/Local) leftPagViewController: pag.PAGViewController = new pag.PAGViewController();
  [@Local](/user/Local) rightPagViewController: pag.PAGViewController = new pag.PAGViewController();
  [@Local](/user/Local) topPagViewController: pag.PAGViewController = new pag.PAGViewController();

  // "你好"和打招呼语
  [@Local](/user/Local) helloDayTranslateY:number=DisplayUtil.getHeight() - 100//100 是随便设的 ;
  [@Local](/user/Local) helloDayOpacity:number=0;
  [@Local](/user/Local) helloDayScaleX: number=0;
  [@Local](/user/Local) helloDayScaleY: number =0;
  [@Local](/user/Local) dayTextOpacity:number=0;
  // 刷新时间
  [@Local](/user/Local) refreshTimeOpacity:number=1;

  // 左下角气泡
  [@Local](/user/Local) leftBubbleTranslateX:number=DisplayUtil.getWidth() / 2;
  [@Local](/user/Local) leftBubbleTranslateY:number=(DisplayUtil.getHeight() - 100)/2;//100 是随便设的 ;
  [@Local](/user/Local) leftBubbleScaleX:number=0;
  [@Local](/user/Local) leftBubbleScaleY:number=0;

  // 右下角动画
  [@Local](/user/Local) rightBubbleTranslateX:number=DisplayUtil.getWidth() / 2;
  [@Local](/user/Local) rightBubbleTranslateY:number=(DisplayUtil.getHeight() - 100)/2;//100 是随便设的 ;
  [@Local](/user/Local) rightBubbleScaleX:number=0;
  [@Local](/user/Local) rightBubbleScaleY:number=0;

  // 右上角动画
  [@Local](/user/Local) topBubbleTranslateX:number= -100;
  [@Local](/user/Local) topBubbleTranslateY:number=(200 - 100)/2;//200是小舒动画高度 ;
  [@Local](/user/Local) topBubbleScaleX:number=0;
  [@Local](/user/Local) topBubbleScaleY:number=0;

  // 左上角小气泡
  [@Local](/user/Local) bubble1TranslateX:number=0;
  [@Local](/user/Local) bubble1TranslateY:number=0;//200 是中央小舒动画的高度 ;
  [@Local](/user/Local) bubble1ScaleX:number=0;
  [@Local](/user/Local) bubble1ScaleY:number=0;

  // 右下角小气泡
  [@Local](/user/Local) bubble2TranslateX:number=DisplayUtil.getWidth() / 2;
  [@Local](/user/Local) bubble2TranslateY:number=200*0.1;//200 是中央小舒动画的高度 ;
  [@Local](/user/Local) bubble2ScaleX:number=0;
  [@Local](/user/Local) bubble2ScaleY:number=0;

  // 绑定两人时右下角无内容气泡
  [@Local](/user/Local) bubble3TranslateY:number=0;

  [@Local](/user/Local) waitContentOpacity:number=1;

  // 中央小舒动画
  [@Local](/user/Local) centerPagViewOpacity:number=1;
  [@Local](/user/Local) centerPagViewScaleX:number=0;
  [@Local](/user/Local) centerPagViewScaleY:number=0;
  [@Local](/user/Local) centerPagViewTranslateX:number=0;
  [@Local](/user/Local) centerPagViewTranslateY:number=0;

  // 整个下半部分
  [@Local](/user/Local) moreVisibility: Visibility = Visibility.Hidden;
  [@Local](/user/Local) moreTranslateY:number=0;


  // 背景光点图片
  [@Local](/user/Local) bgImageOpacity:number=1;
  [@Local](/user/Local) bgImageTranslateY:number=0;
  [@Local](/user/Local) bgImageTranslateX:number=0;
  bgImageWidth:number=0;
  bgImageHeight:number=0;

  // 对话文案和引导
  [@Local](/user/Local)  conversationContentTranslateY: number=0;


  private async showWaitView() {
    let centerPagFile =  AppUtil.getContext().resourceDir + "/AISoftide.pag";
    let file = await pag.PAGFile.LoadFromPathAsync(centerPagFile)
    this.centerPagViewController.setComposition(file);
    this.centerPagViewController.setRepeatCount(0);
    this.centerPagViewController.play();

    let leftWaitPagFile =  AppUtil.getContext().resourceDir + "/loading.pag";
    let leftFile = await pag.PAGFile.LoadFromPathAsync(leftWaitPagFile)
    this.leftPagViewController.setComposition(leftFile);
    this.leftPagViewController.setRepeatCount(0);
    this.leftPagViewController.play();

    let rightWaitPagFile =  AppUtil.getContext().resourceDir + "/loading.pag";
    let rightFile = await pag.PAGFile.LoadFromPathAsync(rightWaitPagFile)
    this.rightPagViewController.setComposition(rightFile);
    this.rightPagViewController.setRepeatCount(0);
    this.rightPagViewController.play();

    let topWaitPagFile =  AppUtil.getContext().resourceDir + "/loading.pag";
    let topFile = await pag.PAGFile.LoadFromPathAsync(topWaitPagFile)
    this.topPagViewController.setComposition(topFile);
    this.topPagViewController.setRepeatCount(0);
    this.topPagViewController.play();

    // this.refreshLoading();

    // this.startWaitAnimateView();
    if (NetworkUtil.isNetworkAvailable()){
      // if (DateUtils.isTodayFirstEnter()) { // 首次进入
        if (true){
        // if (this.hasBindBed()) {
          if (true) {
          this.hasBedFirstEnter()
        } else {
          // this.noBedFirstEnter()
        }
      }else { // 非首次进入
        // if (this.hasBindBed()) {
        //   this.hasBedEnter()
        // } else {
        //   this.noBedEnter()
        // }
      }
    }else{
      // this.showNoNetConnectView();
    }
  }

  // 当天首次进入
  private hasBedFirstEnter(){
    this.setPvAnimator()           // PAG动画 (AI圆形动画)
    this.setDayAndHelloAnimator()  // 日期和问候语动画
    this.setTimeAnimator()         // 时间动画
    this.setLeftBubble()           // 左侧气泡设置
    this.setRightBubble()          // 右侧气泡设置
    this.setTopBubble()            // 顶部气泡设置
    this.setBubble1Animator()      // 气泡1动画
    this.setBubble2Animator()      // 气泡2动画
  }

  // 气泡扩散+底部弹出
  private startWaitAnimateView(){
    // 小舒底部弹出
    this.setPvAnimator()
    // 打招呼语底部弹出
    this.setDayAndHelloAnimator();
    // 刷新时间渐显
    this.setTimeAnimator()
    // 气泡扩散
    this.setLeftBubble();
    this.setRightBubble();
    this.setTopBubble();
    this.setBubble1Animator();
    this.setBubble2Animator();
    // 气泡漂浮
    this.setFloatAnimator();
    // 等候语渐显
    this.setShowNextAnimator();
  }

  private setPvAnimator(){
    // 小舒动画底部弹出
    this.centerPagViewTranslateY = SCREEN_HEIGHT - CENTER_PAG_VIEW_HEIGHT;

    // 修复animateTo与V2的刷新机制不兼容
    animateToImmediately({
      duration: 0
    }, () => {
    })
      AppUtil.getUIContext().animateTo({
        duration:1500,
        curve:curves.springMotion()
      },()=>{
        this.centerPagViewTranslateY = 0;
      })
  }

  private setDayAndHelloAnimator(){
    //时间与问候语动画
    this.helloDayOpacity=0;
    this.helloDayTranslateY=SCREEN_HEIGHT - CENTER_PAG_VIEW_HEIGHT;
    this.dayTextOpacity=0;

    // 修复animateTo与V2的刷新机制不兼容
    animateToImmediately({
      duration: 0
    }, () => {
    })

      AppUtil.getUIContext().animateTo({
        duration:1500,
        curve:curves.springMotion()
      },()=>{
        this.helloDayTranslateY=0;
      })

      AppUtil.getUIContext().animateTo({
        duration:500,
        delay:283,
        curve:curves.cubicBezierCurve(0.33, 0, 0.67, 1.0)
      },()=>{
        this.helloDayOpacity=1;
      })

      AppUtil.getUIContext().animateTo({
        duration:332,
        delay:1667,
        curve:curves.cubicBezierCurve(0.33, 0, 0.67, 1.0)
      },()=>{
        this.dayTextOpacity=1;
      })


  }

  private setTimeAnimator(){
    this.refreshTimeOpacity=0;
    //刷新时间动画
    setTimeout( ()=>{
      AppUtil.getUIContext().animateTo({
        duration:167,
        delay:1500,
        curve:Curve.Linear
      },()=>{
        this.refreshTimeOpacity=1;
      })
    },1)

  }

  private setLeftBubble(){
    //左下角气泡动画
    this.leftBubbleScaleX=0.2;
    this.leftBubbleScaleY=0.2;
    this.leftBubbleTranslateX=50;
    this.leftBubbleTranslateY=-45;
    animateToImmediately({
      duration: 0
    }, () => {
    })

    AppUtil.getUIContext().animateTo({
      duration: 500,
      delay: 1500
    },()=>{
      this.leftBubbleTranslateX=1;
      this.leftBubbleTranslateY=1;
      this.leftBubbleScaleX=1;
      this.leftBubbleScaleY=1;
    })
  }

  private setRightBubble(){
    if (this.bubbleCount===2){

    }else{
    //右下角气泡动画
    this.rightBubbleScaleX=0.2;
    this.rightBubbleScaleY=0.2;
    this.rightBubbleTranslateX=-(CENTER_PAG_VIEW_WIDTH)/2;
    this.rightBubbleTranslateY=-(CENTER_PAG_VIEW_HEIGHT-32)/2;
    animateToImmediately({
      duration: 0
    }, () => {
    })

    AppUtil.getUIContext().animateTo({
      duration: 500,
      delay: 1500
    },()=>{
      this.rightBubbleTranslateX=1;
      this.rightBubbleTranslateY=1;
      this.rightBubbleScaleX=1;
      this.rightBubbleScaleY=1;
    })
    }
  }

  private setTopBubble(){

    this.topBubbleScaleX=0.2;
    this.topBubbleScaleY=0.2;
    animateToImmediately({
      duration: 0
    }, () => {
    })
    AppUtil.getUIContext().animateTo({
      duration: 500,
      delay: 1500
    },()=>{
      this.topBubbleTranslateX=0;
      this.topBubbleTranslateY=0;
      this.topBubbleScaleX=1;
      this.topBubbleScaleY=1;
    })
  }

  private setBubble1Animator() {
    if (this.bubbleCount>3) {

    }else {
      this.bubble1ScaleX=0.2;
      this.bubble1ScaleY=0.2;
      this.bubble1TranslateX=100;
      this.bubble1TranslateY=(200-32)/2//200是小舒动画高度,32是bubble1高度;
      animateToImmediately({
        duration: 0
      }, () => {
      })
      AppUtil.getUIContext().animateTo({
        duration: 500,
        delay: 1500
      },()=>{
        this.bubble1TranslateX=0;
        this.bubble1TranslateY=0;
        this.bubble1ScaleX=1;
        this.bubble1ScaleY=1;
      })
    }
  }

  private setBubble2Animator() {
    if (this.bubbleCount===2) {
      return
    }
    this.bubble2ScaleX=0.2;
    this.bubble2ScaleY=0.2;
    animateToImmediately({
      duration: 0
    }, () => {
    })

    AppUtil.getUIContext().animateTo({
      duration: 500,
      delay: 1500
    },()=>{
      this.bubble2TranslateX=1;
      this.bubble2TranslateY=1;
      this.bubble2ScaleX=1;
      this.bubble2ScaleY=1;
    })
  }

  private setFloatAnimator(){
    //浮动动画
   AppUtil.getUIContext().animateTo( {
     duration:2000,
     delay:2500,
     playMode: PlayMode.AlternateReverse,
     iterations:-1,
   }, () => {
     this.topBubbleTranslateY=FLOAT_DISTANCE;
     this.leftBubbleTranslateY=-FLOAT_DISTANCE;
     this.rightBubbleTranslateY=FLOAT_DISTANCE;
   })
  }

  private setShowNextAnimator(){
    this.waitContentOpacity=0;
    animateToImmediately({
      duration: 0
    }, () => {
    })
    AppUtil.getUIContext().animateTo({
      duration: 300,
      delay: 3625
    },()=>{
      this.waitContentOpacity=1;
    })
  }

  /**
   * 每次请求完数据执行这个方法
   */
  private firstStep(){
    this.moreVisibility=Visibility.Hidden;
    this.resetBindView();
    this.setNextAnimator()
  }

  // 重置多个组件状态
  private resetBindView(){
    // 重置气泡1的视图状态
    this.bubble1TranslateX=0;
    this.bubble1TranslateY=0;
    this.bubble1ScaleX=1;
    this.bubble1ScaleY=1;
    // 重置气泡2的视图状态
    this.bubble2TranslateX=0;
    this.bubble2TranslateY=0;
    this.bubble2ScaleX=1;
    this.bubble2ScaleY=1;

    // 重置顶部气泡布局的视图状态
    this.topBubbleTranslateX=0;
    this.topBubbleTranslateY=0;
    this.topBubbleScaleX=1;
    this.topBubbleScaleY=1;
    // 重置左侧气泡布局的视图状态
    this.leftBubbleTranslateX=0;
    this.leftBubbleTranslateY=0;
    this.leftBubbleScaleX=1;
    this.leftBubbleScaleY=1;
    // 重置右侧气泡布局的视图状态
    this.rightBubbleTranslateX=0;
    this.rightBubbleTranslateY=0;
    this.rightBubbleScaleX=1;
    this.rightBubbleScaleY=1;
    // // 重置更多气泡的视图状态
    // resetViewState(binding.ivBubbleMore)
    // 重置页面视图容器的视图状态
    // resetViewState(binding.clPagview)
    // resetViewState(binding.clContent)
    // // 重置摘要文本的视图状态
    // resetViewState(binding.tvSummary)
    // // 重置分割线的视图状态
    // resetViewState(binding.vSpilt)
    // // 重置建议布局的视图状态
    // resetViewState(binding.llAdvice)
    // // 重置更多内容布局的视图状态
    // resetViewState(binding.clMore)
    // // 重置展开按钮的视图状态
    // resetViewState(binding.ivExpand)
    // // 重置"你好的一天"布局的视图状态
    // resetViewState(binding.llHelloDay)

  }

  private setNextAnimator(isBind:boolean=true){
    if (isBind) {
      this.showNextAnimator()
    } else {
      this.showUnBindNextAnimator()
    }
  }

  private showNextAnimator(){
    this.setNextBgAnimator()

    this.setNextDayAndHelloAnimator()

    this.setUpNextAnimator();

    this.setNextFloatAnimator();
  }

  private showUnBindNextAnimator(){

    this.setNextBgAnimator();


  }


  private setNextBgAnimator(){
    //背景动画
    this.bgImageTranslateY=0;
    this.bgImageTranslateX=0;
    this.bgImageOpacity=1;
    animateToImmediately({
      duration: 0
    }, () => {
    })

    AppUtil.getUIContext().animateTo({
      duration: 300,
      delay: 200,
      curve:curves.cubicBezierCurve(0.33, 0, 0.87, 1.0)
    },()=>{
      this.bgImageTranslateY=(this.bgImageHeight-DisplayUtil.getHeight())/5*3;
      this.bgImageTranslateX=this.bgImageWidth/10;
      this.bgImageOpacity=0.81;
    })

  }

  private setUpAnimator() {
    this.centerPagViewOpacity = 1;
    this.centerPagViewTranslateY = 0;
    animateToImmediately({
      duration: 0
    }, () => {
    })
    AppUtil.getUIContext().animateTo({
      duration: 500,
    }, () => {
      this.centerPagViewTranslateY = -70;
    })
  }

  private setNextDayAndHelloAnimator(){
    //时间与问候语动画
    this.helloDayScaleX=1;
    this.helloDayScaleY=1;
    this.dayTextOpacity=1;

    animateToImmediately({
      duration: 0
    }, () => {
    })

    AppUtil.getUIContext().animateTo({
      duration: 500,
      curve: curves.cubicBezierCur

更多关于HarmonyOS 鸿蒙Next中为什么我的animateTo动画不执行?的实战教程也可以访问 https://www.itying.com/category-93-b0.html

5 回复

【问题定位】

观察问题代码,发现在onAppear方法中存在await操作,尝试把相关代码注释掉,之后动画可以正确展示,故问题出现在await操作上。

【分析结论】 onAppear()方法中执行await操作,导致animateToImmediately无法正确执行。

【修改建议】 把await相关操作移动到aboutToAppear上,修改后代码如下:

import { AppUtil } from '@pura/harmony-utils';
import * as pag from '@tencent/libpag';
import { PAGView } from '@tencent/libpag';
import { curves } from '@kit.ArkUI';

@ComponentV2
@Entry
export struct Index2 {
  @Local centerPagViewController: pag.PAGViewController = new pag.PAGViewController();
  @Local centerPagViewTranslateX: number = 0;
  @Local centerPagViewTranslateY: number = 0;
  @Local centerPagViewOpacity: number = 1;

  async aboutToAppear(): Promise<void> {
    let centerPagFile = AppUtil.getContext().resourceDir + '/AISoftide.pag';
    let file = await pag.PAGFile.LoadFromPathAsync(centerPagFile)
    this.centerPagViewController.setComposition(file);
    this.centerPagViewController.setRepeatCount(0);
    this.centerPagViewController.play();
  }

  build() {
    RelativeContainer() {
      Scroll() {
        RelativeContainer() {
          RelativeContainer() {
            PAGView({ controller: this.centerPagViewController })
              .id('center_pagview')
              .size({ width: 199, height: 199 })
              .opacity(this.centerPagViewOpacity)
              .translate({ x: this.centerPagViewTranslateX, y: this.centerPagViewTranslateY })
              .alignRules({
                middle: { anchor: '__container__', align: HorizontalAlign.Center },
                top: { anchor: '__container__', align: VerticalAlign.Top }
              })

            Image($r('app.media.startIcon'))
              .size({ width: 34, height: 40 })
              .margin({ bottom: 20 })
              .alignRules({
                middle: { anchor: 'center_pagview', align: HorizontalAlign.Center },
                center: { anchor: 'center_pagview', align: VerticalAlign.Center }
              })
          }
          .size({ width: 200, height: 200 })
          .id('rc_center_pagview')
          .margin({ top: 91 })
          .translate({ x: this.centerPagViewTranslateX, y: this.centerPagViewTranslateY })
          .alignRules({
            middle: { anchor: '__container__', align: HorizontalAlign.Center },
            top: { anchor: '__container__', align: VerticalAlign.Top }
          })
        }.padding({ left: 16, right: 16 })
        .width('100%')
        .height('auto')
      }
      .width('100%')
      .height('100%')
      .alignRules({
        top: { anchor: 'fake_statusBar', align: VerticalAlign.Bottom },
      })

      .onAppear(() => {
        // 如果在aboutToAppear中调用动画,自定义组件内的build还未执行,内部组件还未创建,动画时机过早,动画属性没有初值无法对组件产生动画。
        this.showWaitView()
      })
    }
  }

  async showWaitView() {
    this.centerPagViewTranslateY = 600
    animateToImmediately({
      duration: 0,
    }, () => {
      AppUtil.getUIContext().animateTo({
        duration: 1500,
        curve: curves.springMotion()
      }, () => {
        this.centerPagViewTranslateY = 0;
      })
    })
  }
}

更多关于HarmonyOS 鸿蒙Next中为什么我的animateTo动画不执行?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


可以在animateTo的事件中加个log看是否执行了

我简单仿写了一个动画 是可以正常执行的 我使用的

this.getUIContext().animateTo

加了,有日志,但看起来就是没动画,我也很费解,目前找到了个解决办法,就是给animateTo加个setTimeOut,就有动画了,但不理解为什么,

检查动画代码是否在UI线程执行,确保状态变量使用@State装饰器。动画参数需正确设置,duration和curve属性不能缺失。确认组件可见且未被遮挡,父容器尺寸需有效。动画触发条件需满足,避免在页面生命周期错误阶段调用。

根据你的代码,问题很可能出在动画执行时机上。在 onAppear 中调用 showWaitView() 启动动画,但此时组件可能尚未完成首次布局和渲染,导致动画属性变更无法触发有效的过渡效果。

具体分析如下:

  1. 关键问题:onAppear 时机过早 你在 ScrollonAppear 回调中调用 showWaitView()。虽然 onAppear 表示组件已出现,但此时其内部子组件的布局和绘制可能尚未完全就绪。直接修改 @Local 状态变量并启动 animateTo,系统可能无法正确捕获初始状态值,导致动画“跳跃”完成或看似未执行。

  2. 代码中的线索 你已经在 setPvAnimator() 等方法中使用了 animateToImmediately 来尝试规避状态更新问题,这思路正确,但根源在于首次动画触发的时机。

解决方案:

将动画启动时机延迟到组件首次布局完成之后。推荐使用 onAreaChangesetTimeout 进行微延迟。

修改建议:

build() 方法中,找到 Scroll 组件,将其 onAppear 回调修改如下:

.onAppear(() => {
  // 使用setTimeout确保在下一个事件循环中执行,此时布局已完成
  setTimeout(() => {
    this.showWaitView();
  }, 0);
  
  // 或者使用onAreaChange监听布局完成(更推荐)
  // 但注意onAreaChange会多次触发,需要添加标志位控制只执行一次
})

更优方案:使用 onAreaChange 并控制单次执行

// 在组件类中增加一个标志位
@Local isFirstLayout: boolean = true;

// 在build()的Scroll组件中
.onAreaChange((oldArea, newArea) => {
  if (this.isFirstLayout && newArea.width > 0 && newArea.height > 0) {
    this.isFirstLayout = false;
    this.showWaitView();
  }
})

其他注意事项:

  1. animateToImmediately 的使用:你已经在各个动画方法中正确使用了 animateToImmediately({ duration: 0 }, () => {}) 来同步设置初始状态,这很好,请保持。

  2. AppUtil.getUIContext().animateTo:你通过 AppUtil.getUIContext() 获取UI上下文执行动画是正确做法,确保动画在正确的上下文中执行。

  3. 动画属性绑定:检查你的动画属性(如 centerPagViewTranslateY)是否确实绑定到了对应组件的样式上。从代码看,绑定关系是存在的。

总结:将 this.showWaitView() 的调用包裹在 setTimeout(() => {}, 0) 中,或使用 onAreaChange 监听布局完成后再触发,应该能解决动画不执行的问题。这是因为给了组件足够的时间完成初始布局,使动画属性有明确的起始值。

回到顶部