HarmonyOS 鸿蒙Next PageFlip翻书效果demo 鸿蒙场景化代码
HarmonyOS 鸿蒙Next PageFlip翻书效果demo 鸿蒙场景化代码
<markdown _ngcontent-tgy-c147="" class="markdownPreContainer">
阅读器翻页效果demo
介绍
本示例基于显式动画、List组件、drawing接口实现了阅读器上下翻页、左右覆盖翻页以及仿真翻页效果。
效果图
使用说明
- 进入应用默认为仿真翻页,长按屏幕并滑动可实现翻页效果,点击屏幕中部区域,弹出翻页方式选择栏。
- 选择上下翻页,显示上下翻页页面,支持上下滑动翻页。
- 选择覆盖翻页,显示覆盖翻页页面。支持左右滑动翻页,以及点击屏幕左右侧后滑动翻页。
实现思路
上下翻页效果
使用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中涉及的参数:
- duration:动画执行的时长,单位毫秒,项目中固定为300ms。
- 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> < (<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> >
(<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">() =></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">() =></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">() =></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
更多关于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