HarmonyOS 鸿蒙Next PageFlip翻书效果demo 鸿蒙场景化代码

发布于 1周前 作者 yuanlaile 来自 鸿蒙OS

HarmonyOS 鸿蒙Next PageFlip翻书效果demo 鸿蒙场景化代码
<markdown _ngcontent-tgy-c147="" class="markdownPreContainer">

阅读器翻页效果demo

介绍

本示例基于显式动画、List组件、drawing接口实现了阅读器上下翻页、左右覆盖翻页以及仿真翻页效果。

阅读器翻页效果源码地址

效果图

图片名称

使用说明

  1. 进入应用默认为仿真翻页,长按屏幕并滑动可实现翻页效果,点击屏幕中部区域,弹出翻页方式选择栏。
  2. 选择上下翻页,显示上下翻页页面,支持上下滑动翻页。
  3. 选择覆盖翻页,显示覆盖翻页页面。支持左右滑动翻页,以及点击屏幕左右侧后滑动翻页。

实现思路

上下翻页效果

使用List组件实现上下滑动效果,通过添加点击事件,点击屏幕,底部会出现翻页方式选择栏。核心代码如下,源码参考

UpDownFlipPage.ets

深色代码主题
复制
Column() {
      List({ initialIndex: this.currentPageNum - Constants.PAGE_FLIP_PAGE_COUNT }) {
        LazyForEach(this.data, (item: string) => {
          ListItem() {
            Text($r(item))
              .fontSize($r('app.integer.flip_page_text_font_size'))
              .width($r('app.string.page_flip_full_size'))
              .lineHeight($r('app.integer.flip_page_text_line_height'))
              .padding({ left: $r('app.integer.flip_page_padding_middle_two') })
              .fontColor($r('app.color.text_font_color'))
              .fontWeight(FontWeight.Normal)
              .letterSpacing(1)
          }
        }, (item: string, index: number) => index + JSON.stringify(item))
      }
      .width($r('app.string.pageflip_bottomview_row_text_width'))
      .height($r('app.string.page_flip_full_size'))
      .scrollBar(BarState.Off)
      .cachedCount(Constants.PAGE_FLIP_CACHE_COUNT)
      .onScrollIndex((firstIndex: number) => {
        this.currentPageNum = firstIndex + Constants.PAGE_FLIP_PAGE_COUNT;
      })
    }
    .width($r('app.string.page_flip_full_size'))
    .backgroundColor($r("app.color.page_flip_background_color"))
    .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
    .onClick((event?: ClickEvent) => {
      if (event) {
        if (this.isMenuViewVisible) {
          this.isMenuViewVisible = false;
        } else {
          this.isMenuViewVisible = true;
        }
      }
    })

覆盖翻页效果

使用显式动画animateTo和堆叠容器Stack实现页面覆盖效果,通过触发滑动手势事件PanGesture,左右滑动屏幕或点击屏幕左侧右侧可进行翻页。animateTo中涉及的参数:

  1. duration:动画执行的时长,单位毫秒,项目中固定为300ms。
  2. curve:插值曲线,此处用了EaseOut,表示动画逐渐减速以低速结束。

核心代码如下,源码参考

CoverFlipPage.ets

深色代码主题
复制
private clickAnimateTo(isClick: boolean, isLeft?: boolean) {
    animateTo({
      duration: Constants.PAGE_FLIP_TO_AST_DURATION,
      curve: Curve.EaseOut,
      onFinish: () => {
        if (this.offsetX > Constants.PAGE_FLIP_RIGHT_FLIP_OFFSETX &&
          this.currentPageNum !== Constants.PAGE_FLIP_PAGE_START) {
          this.currentPageNum -= Constants.PAGE_FLIP_PAGE_COUNT;
        } else if (this.offsetX < Constants.PAGE_FLIP_LEFT_FLIP_OFFSETX &&
          this.currentPageNum !== Constants.PAGE_FLIP_PAGE_END) {
          this.currentPageNum += Constants.PAGE_FLIP_PAGE_COUNT;
        }
        this.offsetX = Constants.PAGE_FLIP_ZERO;
        this.simulatePageContent();
      }
    }, () => {
      if (isClick) {
        if (isLeft) {
          this.offsetX = this.screenW;
        } else {
          this.offsetX = -this.screenW;
        }
      } else {
        if (this.offsetX > Constants.PAGE_FLIP_RIGHT_FLIP_OFFSETX &&
          this.currentPageNum !== Constants.PAGE_FLIP_PAGE_START) {
          this.offsetX = this.screenW;
        } else if (this.offsetX < Constants.PAGE_FLIP_LEFT_FLIP_OFFSETX &&
          this.currentPageNum !== Constants.PAGE_FLIP_PAGE_END) {
          this.offsetX = -this.screenW;
        } else {
          this.offsetX = Constants.PAGE_FLIP_ZERO;
        }
      }
    });
  }

仿真翻页效果

使用@ohos.graphics.drawing接口及NodeContainer组件,实现仿真翻页效果的绘制。根据手指滑动触摸位置,计算仿真页的边缘节点,填充区域后实现。核心代码如下,源码参考

EmulationFlipPage.ets

深色代码主题
复制
NodeContainer(this.myNodeController)
          .onAppear(() => {
            AppStorage.setOrCreate('colorFlag', true);
            this.newRectNode();
          })
          .width(px2vp(this.windowWidth))
          .height(px2vp(this.windowHeight))
          .gesture(
            PanGesture()
              .onActionUpdate((event: GestureEvent) => {
                AppStorage.setOrCreate('drawState', DrawState.DS_MOVING);
                clearInterval(this.timeID);
                for (let i = 0; i < event.fingerList.length; i++) {
                  if (event.fingerList[i] == undefined ||
                    event.fingerList[i].localX < 0 ||
                    event.fingerList[i].localY < 0 ||
                    event.fingerList[i].localX > px2vp(this.windowWidth) ||
                    event.fingerList[i].localY > px2vp(this.windowHeight)) {
                    return;
                  }
                  if (this.panPositionX < event.fingerList[i].localX) {
                    this.moveForward = MoveForward.MF_FORWARD;
                    this.panPositionX = event.fingerList[i].localX;
                  } else {
                    this.moveForward = MoveForward.MF_BACKWARD;
                    this.panPositionX = event.fingerList[i].localX;
                  }
              <span class="hljs-variable language_">this</span>.<span class="hljs-property">positionX</span> = <span class="hljs-title function_">vp2px</span>(event.<span class="hljs-property">fingerList</span>[i].<span class="hljs-property">localX</span>);
              <span class="hljs-variable language_">this</span>.<span class="hljs-property">positionY</span> = <span class="hljs-title function_">vp2px</span>(event.<span class="hljs-property">fingerList</span>[i].<span class="hljs-property">localY</span>);

              <span class="hljs-title class_">AppStorage</span>.<span class="hljs-title function_">setOrCreate</span>(<span class="hljs-string">'positionX'</span>, <span class="hljs-variable language_">this</span>.<span class="hljs-property">positionX</span>);
              <span class="hljs-title class_">AppStorage</span>.<span class="hljs-title function_">setOrCreate</span>(<span class="hljs-string">'positionY'</span>, <span class="hljs-variable language_">this</span>.<span class="hljs-property">positionY</span>);

              <span class="hljs-keyword">if</span> (!<span class="hljs-variable language_">this</span>.<span class="hljs-property">isDrawing</span>) {
                <span class="hljs-variable language_">this</span>.<span class="hljs-property">isDrawing</span> = <span class="hljs-literal">true</span>;
                <span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">positionY</span> &lt; (<span class="hljs-variable language_">this</span>.<span class="hljs-property">windowHeight</span> / <span class="hljs-title class_">Constants</span>.<span class="hljs-property">PAGE_FLIP_THREE</span>)) {
                  <span class="hljs-title class_">AppStorage</span>.<span class="hljs-title function_">setOrCreate</span>(<span class="hljs-string">'drawPosition'</span>, <span class="hljs-title class_">DrawPosition</span>.<span class="hljs-property">DP_TOP</span>);
                } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">positionY</span> &gt;
                  (<span class="hljs-variable language_">this</span>.<span class="hljs-property">windowHeight</span> * <span class="hljs-title class_">Constants</span>.<span class="hljs-property">PAGE_FLIP_TWO</span> / <span class="hljs-title class_">Constants</span>.<span class="hljs-property">PAGE_FLIP_THREE</span>)) {
                  <span class="hljs-title class_">AppStorage</span>.<span class="hljs-title function_">setOrCreate</span>(<span class="hljs-string">'drawPosition'</span>, <span class="hljs-title class_">DrawPosition</span>.<span class="hljs-property">DP_BOTTOM</span>);
                } <span class="hljs-keyword">else</span> {
                  <span class="hljs-title class_">AppStorage</span>.<span class="hljs-title function_">setOrCreate</span>(<span class="hljs-string">'drawPosition'</span>, <span class="hljs-title class_">DrawPosition</span>.<span class="hljs-property">DP_MIDDLE</span>);
                }
              }
              <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">newRectNode</span>();
            }
          })
          .<span class="hljs-title function_">onActionEnd</span>(<span class="hljs-function">() =&gt;</span> {
            <span class="hljs-title class_">AppStorage</span>.<span class="hljs-title function_">set</span>(<span class="hljs-string">'drawState'</span>, <span class="hljs-title class_">DrawState</span>.<span class="hljs-property">DS_RELEASE</span>);
            <span class="hljs-keyword">let</span> <span class="hljs-attr">num</span>: number = <span class="hljs-title class_">Constants</span>.<span class="hljs-property">DISTANCE_FRACTION</span>;
            <span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">moveForward</span> === <span class="hljs-number">1</span>) {
              <span class="hljs-keyword">let</span> xDiff = (<span class="hljs-variable language_">this</span>.<span class="hljs-property">windowWidth</span> - (<span class="hljs-title class_">AppStorage</span>.<span class="hljs-title function_">get</span>(<span class="hljs-string">'positionX'</span>) <span class="hljs-keyword">as</span> number)) / num;
              <span class="hljs-keyword">let</span> yDiff = <span class="hljs-number">0</span>;
              <span class="hljs-keyword">if</span> (<span class="hljs-title class_">AppStorage</span>.<span class="hljs-title function_">get</span>(<span class="hljs-string">'drawPosition'</span>) <span class="hljs-keyword">as</span> number === <span class="hljs-title class_">DrawPosition</span>.<span class="hljs-property">DP_BOTTOM</span>) {
                yDiff = (<span class="hljs-variable language_">this</span>.<span class="hljs-property">windowHeight</span> - (<span class="hljs-title class_">AppStorage</span>.<span class="hljs-title function_">get</span>(<span class="hljs-string">'positionY'</span>) <span class="hljs-keyword">as</span> number)) / num;
              } <span class="hljs-keyword">else</span> {
                yDiff = (<span class="hljs-number">0</span> - (<span class="hljs-title class_">AppStorage</span>.<span class="hljs-title function_">get</span>(<span class="hljs-string">'positionY'</span>) <span class="hljs-keyword">as</span> number)) / num;
              }
              <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">setTimer</span>(xDiff, yDiff, <span class="hljs-function">() =&gt;</span> {
                <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">newRectNode</span>();
              });
            } <span class="hljs-keyword">else</span> {
              <span class="hljs-title class_">AppStorage</span>.<span class="hljs-title function_">setOrCreate</span>(<span class="hljs-string">'positionY'</span>, (<span class="hljs-title class_">AppStorage</span>.<span class="hljs-title function_">get</span>(<span class="hljs-string">'flipPositionY'</span>) <span class="hljs-keyword">as</span> number));
              <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">setTimer</span>(<span class="hljs-title class_">Constants</span>.<span class="hljs-property">FLIP_X_DIFF</span>, <span class="hljs-number">0</span>, <span class="hljs-function">() =&gt;</span> {
                <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">newRectNode</span>();
              });
            }
            <span class="hljs-variable language_">this</span>.<span class="hljs-property">isDrawing</span> = !<span class="hljs-variable language_">this</span>.<span class="hljs-property">isDrawing</span>;
          })
      )

工程结构&模块类型

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

参考资料

</markdown>

更多关于HarmonyOS 鸿蒙Next PageFlip翻书效果demo 鸿蒙场景化代码的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html

1 回复

更多关于HarmonyOS 鸿蒙Next PageFlip翻书效果demo 鸿蒙场景化代码的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS中实现Next PageFlip翻书效果demo,可以通过使用ArkUI框架的动画和组件功能来完成。以下是一个简化的场景化代码示例,展示如何实现翻页效果:

@Entry
@Component
struct PageFlipDemo {
  @State page: number = 0

  @Builder pageView() {
    Column() {
      Swiper({
        space: 20,
        indicator: false,
        autoplay: false,
        loop: false,
        currentIndex: this.page,
        onSwipe: (index) => {
          this.page = index
        }
      }) {
        ForEach(this.pages, (pageData, index) => {
          Image($pageData.image)
            .width('100%')
            .height('100%')
            .objectFit(ImageFit.Cover)
        })
      }
    }
  }

  @State pages = [
    { image: '$media:page1' },
    { image: '$media:page2' },
    { image: '$media:page3' }
  ]

  build() {
    Column() {
      this.pageView()
    }
  }
}

此代码示例中,使用了Swiper组件来实现页面的切换效果,ForEach循环用于渲染每一页的内容。this.pages数组存储了每一页的图片资源。通过onSwipe事件监听用户的滑动操作,并更新当前显示的页面。

如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html

回到顶部