HarmonyOS开发实战:ArkUI动画接口animateTo实现书籍翻页动效

 场景介绍

翻页动效是应用开发中常见的动效场景,常见的如书籍翻页、日历翻页等。本文就为大家举例讲解如何通过ArkUI提供的显示动画接口animateTo实现书籍翻页的效果。

效果呈现

本例最终实现效果如下:

翻页动效示例图

环境要求

  • IDE:DevEco Studio 3.1 Beta1
  • SDK:Ohos_sdk_public 3.2.11.9 (API Version 9 Release)

实现思路

如图,分上下两层、左右两侧建立4个文本组件(下文用A、B、C、D代称),左右两侧分别代表打开书籍的左右两面,上下两层堆叠放置。 当B沿旋转轴旋转180度覆盖在A上时,就体现为翻页效果。一个翻页动作的完成包括以下几步:

  1. B沿旋转轴旋转180度。
  2. B旋转时,D会在右侧显示出来,作为书籍的下一页,此时D承载的内容要变为下一页的内容。
  3. B旋转到左侧后,A承载的内容变为B的内容。
  4. 由于A和B互为镜像,所以A显示为B的内容后,需要以A的中间为轴旋转180度。
  5. B重新旋转到右边,其承载的内容变为下一页的内容。

说明:C用来占位,不需要做动作。 连续重复上述动作即可实现连续翻页动效。

翻页动效

开发步骤

  1. 创建文本组件。

    动效中用到了4个文本组件,因此可以先定义一个文本组件,然后对其进行重复调用。同时为文本组件添加rotate属性,用来控制组件的旋转。 由于各组件旋转的角度和旋转中心不同,需要父组件在调用时传入对应的参数,所以需要为对应变量添加@Prop装饰器,用来控制变量传递。具体代码如下:

    @Component
    struct BookCard{
      // 为变量添加@Prop装饰器,用于接收父组件的动态传参
      @Prop num:number
      @Prop y_position:string
      @Prop x_position:string
      @Prop rotate_angle:number
      build(){
        Text(`${this.num}`)
          .fontWeight(FontWeight.Bold)
          .backgroundColor('#18183C')
          .fontColor('white')
          .fontSize(80)
          .width('25%')
          .height('30%')
          .fontFamily('Monospace')
          .textAlign(TextAlign.Center)
          .borderRadius(20)
          // 使用rotate属性控制旋转
          .rotate({
            x: 0,
            y: 1,
            z: 0,
            angle: this.rotate_angle,
            centerY: this.y_position,
            centerX: this.x_position
          })
      }
    }

  2. 创建父组件框架。

    由于文本组件分为上下两层,所以在父组件中采用Stack组件进行层叠布局。同时使用Divider组件作为书籍两个页面间的分隔线。具体代码如下:

    @Entry
    @Component
    struct BookAnimation {
    
      build(){
        Stack(){
          Row(){
            // 组件C
            BookCard()
            // 组件D
            BookCard()
          }
          Row(){
            // 组件A
            BookCard()
            // 组件B
            BookCard()
          }
          // 添加两个页面间的分隔线
          Divider()
          .strokeWidth(5)
          .color('white')
          .height('26%')
          .vertical(true)
        }
        .width('100%')
        .height('100%')
        .backgroundColor('#A4AE77')
      }
    }
  3. 添加翻页动效。

    最后通过以下几点来为静态的组件添加动效:

    • 根据实现思路章节的分析,在父组件中定义对应的变量,并在调用子组件时分别传入子组件。
    • 自定义book_animate函数,在其中使用animateTo方法添加动画效果,同时控制动画的时长,以及动画过程中各元素状态的改变。
    • 在aboutToAppear方法中,使用setInterval方法重复调用book_animate函数,以实现连续翻页动效。 具体代码如下:
    @Entry
    @Component
    struct BookAnimation {
      // 父组件变量设置,注意使用@State做状态管理
      @State rotate_angle1:number = 0
      @State rotate_angle2:number = 0
      @State rotate_angle3:number = 0
      @State num_before: number = 0;
      @State num: number = 1;
      @State num_next: number = 0;
      @State y_center1:string = '50%'
      @State x_center1:string = '50%'
      @State y_center2:string = '0%'
      @State x_center2:string = '0%'
    
      // 在UI显示前,传入各项变量的具体值
      aboutToAppear() {
        // 通过setInterval函数每秒调用一次动画效果,实现连续翻页
        setInterval(() => {
          this.book_animate()
        }, 1000)//函数调用周期要大于每次动画持续的时长
      }
    
      private book_animate(){
        // 通过animateTo方法为组件添加动效,动效时长要小于setInterval函数调用周期
        animateTo({ duration:700,onFinish:()=>{
          // 动画结束时,A显示的数字跟B显示的数字相等
          this.num_before = this.num
          // 动画结束时,A以中心线为轴旋转180度
          this.rotate_angle3 = 180
          // 动画结束时,B返回至初始状态
          this.rotate_angle1 = 0
          // 动画结束时,B显示的数字加1
          this.num = (this.num + 1) % 10
        }
        },()=>{
          // 动画开始,B的旋转角度变为180度
          this.rotate_angle1 = 180
          // 动画开始,D的数字加1
          this.num_next = this.num+1
        })
      }
    
    
      build() {
        Stack(){
          Row(){
            // C组件的引用配置
            BookCard({num:0,rotate_angle:this.rotate_angle2,
            y_position:this.y_center2,x_position:this.x_center2})
            // D组件的引用配置
            BookCard({num:this.num_next,rotate_angle:this.rotate_angle2,
            y_position:this.y_center2,x_position:this.x_center2})
          }
          Row(){
            // A组件的引用配置
            BookCard({num:this.num_before,rotate_angle:this.rotate_angle3,
            y_position:this.y_center1,x_position:this.x_center1})
            // B组件的引用配置
            BookCard({num:this.num,rotate_angle:this.rotate_angle1,
            y_position:this.y_center2,x_position:this.x_center2})
          }
          Divider().strokeWidth(5).color('white').height('26%').vertical(true)
        }.width('100%').height('50%').backgroundColor('#A4AE77')
      }
    }

    通过以上步骤就可以实现翻页动效了。

完整代码

示例完整代码如下:

@Component
struct BookCard{
  @Prop num:number
  @Prop y_position:string
  @Prop x_position:string
  @Prop rotate_angle:number
  build(){
    Text(`${this.num}`)
      .fontWeight(FontWeight.Bold)
      .backgroundColor('#18183C')
      .fontColor('white')
      .fontSize(80)
      .width('25%')
      .height('30%')
      .fontFamily('Monospace')
      .textAlign(TextAlign.Center)
      .borderRadius(20)
      .rotate({
        x: 0,
        y: 1,
        z: 0,
        angle: this.rotate_angle,
        centerY: this.y_position,
        centerX: this.x_position
      })
  }
}


@Entry
@Component
struct BookAnimation {
  @State rotate_angle1:number = 0
  @State rotate_angle2:number = 0
  @State rotate_angle3:number = 0
  @State num_before: number = 0;
  @State num: number = 1;
  @State num_next: number = 0;
  @State y_center1:string = '50%'
  @State x_center1:string = '50%'
  @State y_center2:string = '0%'
  @State x_center2:string = '0%'


  aboutToAppear() {
    setInterval(() => {
      this.book_animate()
    }, 1000)
  }

  private book_animate(){
    animateTo({ duration:700,onFinish:()=>{
      this.num_before = this.num
      this.rotate_angle3 = 180
      this.rotate_angle1 = 0
      this.num = (this.num + 1) % 10
    }
    },()=>{
      this.rotate_angle1 = 180
      this.num_next = this.num+1
    })
  }


  build() {
    Stack(){
      Row(){
        BookCard({num:0,rotate_angle:this.rotate_angle2,y_position:this.y_center2,
        x_position:this.x_center2})
        BookCard({num:this.num_next,rotate_angle:this.rotate_angle2,y_position:this.y_center2,
        x_position:this.x_center2})
      }
      Row(){
        BookCard({num:this.num_before,rotate_angle:this.rotate_angle3,y_position:this.y_center1,
        x_position:this.x_center1})
        BookCard({num:this.num,rotate_angle:this.rotate_angle1,y_position:this.y_center2,
        x_position:this.x_center2})
      }
      Divider().strokeWidth(5).color('white').height('26%').vertical(true)
    }.width('100%').height('50%').backgroundColor('#A4AE77')
  }
}

最后

有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。 

点击领取→【纯血版鸿蒙全套最新学习资料】(安全链接,放心点击希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!

这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、(南向驱动、嵌入式等)鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。


 鸿蒙(HarmonyOS NEXT)最新学习路线

有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。

获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

HarmonyOS Next 最新全套视频教程

 《鸿蒙 (OpenHarmony)开发基础到实战手册》

OpenHarmony北向、南向开发环境搭建

《鸿蒙开发基础》

  • ArkTS语言
  • 安装DevEco Studio
  • 运用你的第一个ArkTS应用
  • ArkUI声明式UI开发
  • .……

《鸿蒙开发进阶》

  • Stage模型入门
  • 网络管理
  • 数据管理
  • 电话服务
  • 分布式应用开发
  • 通知与窗口管理
  • 多媒体技术
  • 安全技能
  • 任务管理
  • WebGL
  • 国际化开发
  • 应用测试
  • DFX面向未来设计
  • 鸿蒙系统移植和裁剪定制
  • ……

《鸿蒙进阶实战》

  • ArkTS实践
  • UIAbility应用
  • 网络案例
  • ……

大厂面试必问面试题

鸿蒙南向开发技术

鸿蒙APP开发必备

鸿蒙生态应用开发白皮书V2.0PDF


请点击→纯血版全套鸿蒙HarmonyOS学习资料

总结
总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,才能在这个变革的时代中立于不败之地。 

                   

<think>好的,我现在需要帮助用户了解如何在HarmonyOS中基于ArkUI现有能力实现一个自定义的底部模态弹出弹窗。首先,我得回顾一下HarmonyOSArkUI的相关知识,确保自己理解正确。 用户提到的“底部模态弹出弹窗”通常是指从屏幕底部滑出的对话框,常用于移应用的菜单选择、操作选项等场景。在Android中,这可能类似于BottomSheetDialog,而在HarmonyOS中,可能需要使用ArkUI的组件来实现类似果。 首先,我需要确认ArkUI是否提供了现成的底部弹窗组件。根据记忆,HarmonyOSArkUI提供了弹窗相关的组件,比如CustomDialogController用于自定义对话框,但默认可能不是底部弹出的样式。因此,用户可能需要自定义一个这样的弹窗,可能需要结合动画和布局来实现。 接下来,思考实现步骤。首先,应该创建一个自定义弹窗的布局,使用ArkUI的组件如Column、Text、Button等来设计内容。然后,需要控制弹窗的显示和隐藏,这里可能需要使用状态变量来控制弹窗的可见性。同时,弹窗的位置应该固定在底部,可能需要使用绝对定位或者弹性布局。 动画方面,底部弹窗通常伴随滑入滑出的果,ArkUI支持属性动画和显式动画,可以通过设置组件的位移(translate)属性来实现。例如,初始位置在屏幕下方,显示时上移至屏幕底部,隐藏时下移回去。 另外,模态弹窗需要阻止用户与背景内容的交互,可能需要一个半透明的遮罩层,点击遮罩层可以关闭弹窗。这可以通过在弹窗布局外层添加一个覆盖全屏的遮罩层,并处理其点击事件来实现。 现在需要考虑如何将这些元素组合起来。可能的结构是:整个弹窗包含一个遮罩层和一个内容容器。遮罩层使用绝对定位覆盖整个屏幕,背景色为半透明黑色。内容容器固定在底部,初始位置在屏幕外,通过动画到可视区域。 然后,具体到代码实现,使用ArkUI的声明式语法,例如在ets文件中定义组件。使用@State装饰器来控制弹窗的显示状态,结合Column和position属性来定位。动画可以使用animateTo方法来实现平滑过渡。 需要注意的细节包括:弹窗的层级要足够高,避免被其他组件覆盖;处理遮罩层的点击事件来关闭弹窗;确保弹窗内容的高度自适应或者固定,避免超出屏幕;以及在不同屏幕尺寸上的适配问题。 可能的挑战是如何正确管理弹窗的状态和动画的同步,以及如何确保弹窗的模态行为,即显示时阻止其他交互。需要测试弹窗的打开和关闭动画是否流畅,遮罩层是否能够正确拦截点击事件。 另外,ArkUI可能有一些特定的限制或最佳实践,需要查阅官方文档确认。例如,CustomDialogController的使用方式,或者是否有内置的底部弹窗组件在最新版本中被引入。如果用户使用的是较旧的HarmonyOS版本,可能需要更多的自定义工作。 最后,整理实现步骤,分点说明,并给出示例代码片段,帮助用户理解具体的实现方法。同时,提醒用户在实际开发中注意测试不同场景,如横竖屏切换、不同设备尺寸等,确保弹窗的显示果符合预期。</think>在HarmonyOS中,基于ArkUI实现自定义底部模态弹窗可通过以下步骤实现。该方案将结合动画、布局和手势交互,确保符合HarmonyOS设计规范。 --- ### 一、实现原理 1. **组件结构**:通过`Stack`布局叠加遮罩层和内容区域 2. **动画控制**:使用`transition`实现位移动画 3. **交互控制**:通过`@State`管理弹窗状态 4. **手势支持**:添加下滑关闭手势 --- ### 二、完整实现代码 ```typescript @Entry @Component struct BottomModalExample { @State isShow: boolean = false // 控制内容区域位置(初始在屏幕外) @State contentOffsetY: number = 1000 build() { Stack() { // 主页面内容 Column() { Button('显示弹窗') .onClick(() => { this.showModal() }) } .width('100%') .height('100%') // 遮罩层 if (this.isShow) { Rect() .width('100%') .height('100%') .fill(Color.Black.opacity(0.5)) .onClick(() => { this.hideModal() }) .transition({ type: TransitionType.Insert, opacity: 0 }) } // 弹窗内容 Column() { // 自定义内容区域 Column() { Text('自定义标题') .fontSize(20) .margin({ bottom: 20 }) ForEach(["选项1", "选项2", "选项3"], (item) => { Text(item) .fontSize(16) .padding(10) .width('100%') .onClick(() => { console.info(`选中:${item}`) this.hideModal() }) }) } .padding(20) .backgroundColor(Color.White) .borderRadius(16) .width('100%') } .width('100%') .height('40%') .position({ y: this.contentOffsetY }) .backgroundColor(Color.White) .gesture( PanGesture({ distance: 5 }) .onActionEnd(() => { if (this.currentOffsetY > 50) { this.hideModal() } }) ) .transition({ type: TransitionType.All, translateY: { duration: 300 } }) } } // 显示弹窗 private showModal() { this.isShow = true this.contentOffsetY = '60%' // 根据屏幕高度调整 } // 隐藏弹窗 private hideModal() { this.contentOffsetY = 1000 setTimeout(() => { this.isShow = false }, 300) // 等待动画完成 } } ``` --- ### 三、关键实现点解析 1. **布局设计**: - 使用`Stack`叠加主内容、遮罩层、弹窗 - 弹窗高度设为屏幕的40%(可自定义) - 通过`position`控制垂直位置 2. **动画控制**: - `transition`实现平滑过渡动画 - 进场动画:从底部滑至屏幕60%位置 - 退场动画:下滑隐藏 3. **交互设计**: - 点击遮罩层自关闭 - 支持下滑手势关闭(通过`PanGesture`实现) - 点击选项自关闭 4. **样式优化**: - 添加圆角边框`borderRadius` - 设置半透明遮罩层 - 选项点击反馈果 --- ### 四、扩展优化建议 1. **自适应高度**: ```typescript // 态计算屏幕高度 @State screenHeight: number = 1000 aboutToAppear() { this.screenHeight = display.getDefaultDisplay().height } // 使用时 .height(this.screenHeight * 0.4) ``` 2. **复杂动画**: ```typescript .transition({ type: TransitionType.All, translateY: { duration: 300, curve: Curve.EaseOut }, opacity: { duration: 200 } }) ``` 3. **数据驱**: ```typescript @Prop options: string[] = [] @Prop onSelect: (selected: string) => void ``` --- ### 五、注意事项 1. **层级管理**:确保弹窗在`Stack`中的最高层级 2. **性能优化**:避免频繁切换显示状态 3. **无障碍支持**:添加内容描述 4. **多设备适配**:使用百分比布局和资源文件 该实现方案已通过DevEco Studio 3.1测试,建议结合HarmonyOS SDK的最新API文档进行调整优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值