一、前言与背景:

大家好,我是完美句号!欢迎来到 HarmonyOS 5 开发实战系列。本系列致力于为开发者提供实用的技术方案和即拿即用的代码示例,帮助大家快速掌握 HarmonyOS Next 应用开发中的核心功能。在HarmonyOS 5鸿蒙系统中,基于显式动画、List组件、drawing接口实现了阅读器上下翻页、左右覆盖翻页、仿真翻页等效果。


二、阅读器翻页效果:

阅读器翻页效果是阅读器应用中常见的翻页效果,在阅读器应用中,翻页时可以使用不同的效果展示页面变更,使用List组件作为容器组件,提供上下滑动的能力。使用ListItem组件存放每一页的内容。页面内容可以自行定义,本文使用Text组件展示文本内容。通常有以下翻页效果:

  • 进入应用默认为仿真翻页,在屏幕上滑动手指执行翻页。支持点击左右两侧自动翻页。点击屏幕中部区域,弹出翻页选项。
  • 选择上下翻页,显示上下翻页页面。支持上下滑动。
  • 选择覆盖翻页,显示覆盖翻页页面。支持左右滑动翻页,以及点击屏幕左右侧后滑动翻页。

工程目录:

├──entry/src/main/ets/
│  ├──common
│  │  └──Constants.ets               // 公共常量类
│  ├──entryability
│  │  └──EntryAbility.ets            // 程序入口类
│  ├──pages
│  │  └──Index.ets                   // 首页
│  ├──view
│  │  ├──BottomView.ets              // 按钮弹窗
│  │  ├──CoverFlipPage.ets           // 覆盖翻页
│  │  ├──EmulationFlipPage.ets       // 仿真翻页
│  │  ├──ReaderPage.ets              // 文字页面
│  │  └──UpDownFlipPage.ets          // 上下翻页
│  └──viewmodel
│     ├──BasicDataSource.ets         // 列表数据类
│     └──PageNodeController.ets      // 节点控制类
└──entry/src/main/resource           // 应用静态资源目录

实现原理:

img

在文本阅读器应用上,翻页时可以使用不同的效果展示页面变更,通常有以下翻页效果:

  • 上下翻页:向上或向下的滑动效果,适合垂直滚动的文本阅读(如电子书、长篇文章)。
  • 覆盖翻页:通过水平滑动使当前页面向左侧滑出显示下一页,上一页从左侧滑入覆盖当前页,形成连贯的过渡效果。
  • 仿真翻页:模拟真实纸张的弯曲、翻折动作,例如页面边缘的弧形变形与阴影投影,实现沉浸式的体验效果。
  • 本文主要对上述翻页效果的实现进行讲解,旨在帮助开发者了解常见翻页动效开发的流程及实现细节。

上下翻页时,页面内容沿着垂直方向移动。当用户向上滑动时,当前页面内容向上滑出屏幕顶部,同时下一页内容从屏幕底部滑入;向下滑动则相反(当前页向下滑出,上一页从顶部滑入)。实现效果如下:


三、实现原理:

开发步骤,构建模拟数据。

// entry/src/main/ets/view/UpDownFlipPage.ets
@Link currentPageNum: number;
private data: BasicDataSource = new BasicDataSource([]);
// ...
aboutToAppear(): void {
  for (let i = Constants.PAGE_FLIP_PAGE_START; i <= Constants.PAGE_FLIP_PAGE_END; i++) {
    this.data.pushItem(Constants.PAGE_FLIP_RESOURCE + i.toString());
  }
  // ...
}
UpDownFlipPage.ets
使用List组件实现上下翻页效果。
// entry/src/main/ets/view/UpDownFlipPage.ets
List({ initialIndex: this.currentPageNum - 1, scroller: this.scroller }) {
  LazyForEach(this.data, (item: string) => {
    ListItem() {
      Text($r(item))
      // ...
    }
  }, (item: string, index: number) => index + JSON.stringify(item))
}
// ...
.onScrollIndex((firstIndex: number) => {
  this.currentPageNum = firstIndex + 1;
})

覆盖翻页效果模拟卡片切换,新页面(上一页)从屏幕的左侧水平滑入,完全覆盖当前页面。当前页支持从屏幕另一侧滑出,滑出时显示下层新页面。在整个过程中页面没有弯曲或折叠效果,页面作为一个整体平面进行移动。效果如下:

img

使用Stack堆叠容器,存放1、2、3三个页面,借助图形变换的translate平移属性,将上层页面向左平移屏幕宽度移至窗口左侧。使用PanGesture滑动手势事件判断手势滑动方向及平移距离,依据滑动方向及平移距离,执行1页面向右平移滑入屏幕,或2页面向左平移滑出屏幕。滑动手势结束后通过显式动画 (animateTo)完成页面平移至窗口边缘,并重新渲染1、2、3页面。

使用Stack堆叠容器存放3个页面,并将上层页面向左平移至屏幕外。

img

// entry/src/main/ets/view/CoverFlipPage.ets
@Component
export struct CoverFlipPage {
  // ...
  @State offsetX: number = 0;
  @State screenW: number = 0;
  // ...

  build() {
    Stack() {
      // Next page.
      ReaderPage({ content: this.rightPageContent })

      // Current page.
      ReaderPage({ content: this.midPageContent })
        .translate({ x: this.offsetX >= Constants.PAGE_FLIP_ZERO ? Constants.PAGE_FLIP_ZERO : this.offsetX })
        // ...

      // Previous page, shift the window width to the left.
      ReaderPage({ content: this.leftPageContent })
        .translate({ x: -this.screenW + this.offsetX })
        // ...
    }
    // ...
  }
  // ...
}

根据滑动手势事件获取平移的距离,修改状态变量offsetX刷新页面,控制页面移动。手势结束后调用自定义方法pageAnimateTo()方法执行显示动画,完成页面剩余滑动。

// entry/src/main/ets/view/CoverFlipPage.ets
.gesture(
  PanGesture(this.panOption)
    .onActionUpdate((event?: GestureEvent) => {
      // ...
        this.offsetX = event.offsetX;
        // ...
    })
    .onActionEnd(() => {
      // ...
        this.pageAnimateTo(false);
        // ...
    })
)

pageAnimateTo()方法中设置offSetX的结束值,手势向右时offsetX的值为屏幕宽度screenW,手势向左时offsetX的值为负的屏幕宽度-screenW。设置完成后animateTo方法自动插入过渡动画。动画播放结束后,onFinish()完成回调方法中重置offsetX为0,执行自定义方法simulatePageContent()方法更新各内容页ReaderPage组件展示的数据。

img

// entry/src/main/ets/view/CoverFlipPage.ets
private pageAnimateTo(isClick: boolean, isLeft?: boolean) {
  this.getUIContext().animateTo({
    duration: Constants.PAGE_FLIP_TO_AST_DURATION,
    curve: Curve.EaseOut,
    onFinish: () => {
      // ...
      this.offsetX = Constants.PAGE_FLIP_ZERO;
      this.simulatePageContent();
      // ...
    }
  }, () => {
    // ...
        this.offsetX = this.screenW;
        // ...
        this.offsetX = -this.screenW;
        // ...
  });
}
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐