1# 左右翻页
2## 场景介绍
3在众多阅读或者文档应用中,都会涉及到文本翻页、电子书翻页、书籍翻页的场景。本文即为大家介绍左右翻页的开发过程。
4
5## 效果呈现
6效果图如下:
7
8![](./figures/turn-the-page-left-and-right.gif)
9
10## 运行环境
11本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发:
12- IDE: DevEco Studio 4.0 Release
13- SDK: Ohos_sdk_public 4.0.10.13 (API Version 10 Release)
14
15## 实现思路
16本例的包含的关键特性及其实现方案如下:
17- 页面左右覆盖:通过NavDestination组件实现。
18- 小说翻页:通过点击事件(点击不同区域产生不同的交互动作)完成,并对小说首页进行处理。
19- 展示文本:通过创建并调用自定义函数novelContent(),最终展示在页面。
20
21## 开发步骤
22本例详细开发步骤如下,开发步骤仅展示关键代码。
231. 构建小说内容:通过创建自定义函数novelContent()。具体代码块如下:
24    ```ts
25    novelContent(){
26      this.content1 = (this.currentPageNum - 1) + "___小说___" + (this.currentPageNum - 1)
27      this.content2 = (this.currentPageNum) + "___小说___" + (this.currentPageNum)
28      this.content3 = (this.currentPageNum + 1) + "___小说___" + (this.currentPageNum + 1)
29      console.log("oh----->currentPage=" + this.currentPageNum)
30      console.log("oh----->content1=" + this.content1)
31      console.log("oh----->content2=" + this.content2)
32      console.log("oh----->content3=" + this.content3);
33    }
34    ```
352. 展示小说文本:通过NavDestination作为小说文本页面(子页面)的根容器,最后调用自定义组件ReadPage()将小说内容展示到页面上。
36    ```ts
37    NavDestination(){
38      Stack(){
39        ReadPage({content:this.content3})
40          .backgroundColor(this.bgColor)
41
42        ReadPage({content:this.content2})
43          .translate({x:this.offsetX >= 0 ? 0:this.offsetX , y:0 , z:0})
44          .backgroundColor(this.bgColor)
45          .width(this.screenW)
46
47        ReadPage({content:this.content1})
48          .backgroundColor(this.bgColor)
49          .translate({
50            x:-this.screenW + this.offsetX
51          })
52      }
53    }
54    // 自定义组件ReaderPage
55    @Component
56    struct ReadPage{
57      @Prop content:string= ""
58
59      build(){
60        Column(){
61          Text(this.content)
62            .textAlign(TextAlign.Center)
63            .width("100%")
64            .height("100%")
65            .fontSize(50)
66        }
67        .borderColor(Color.Black)
68        .borderWidth(2)
69        .width("100%")
70        .height("100%")
71      }
72    }
73    ```
743. 通过点击完成翻页以及提示消息完成:首先将点击事件封装clickAnimateTo(),点击不同区域产生不同的交互事件。点击距离屏幕左端1/3(横向)的区域,小说页面跳到上一页(第一页除外);点击距离屏幕右端1/3(横向)的区域,小说页面跳转到下一页;点击剩余区域出现提示语“打开菜单”。具体代码如下:
75    ```ts
76    // 封装点击事件clickAnimateTo()
77    private clickAnimateTo(isLeft: Boolean){
78      animateTo({
79        duration:400,
80        curve:Curve.EaseOut,
81        onFinish:()=>{
82          if (this.offsetX > 100){
83            this.currentPageNum -= 1
84          }else if (this.offsetX < -100){
85            this.currentPageNum += 1
86            console.log("oh----->当前页变为" + this.currentPageNum)
87          }
88            this.offsetX = 0
89            this.novelContent()
90        }
91      },()=>{
92        if (isLeft){
93          this.offsetX = this.screenW
94        }else{
95          this.offsetX = -this.screenW
96        }
97      })
98    }
99
100    // 点击事件产生的交互动作
101    .onClick((event?:ClickEvent)=>{
102      if (event){
103        console.log("点击位置:" + event.x)
104        if (event.x > this.screenW / 3 * 2){
105          this.clickAnimateTo(false)
106        }else if(event.x > this.screenW /3){
107          promptAction.showToast({
108            message:"打开菜单",
109            duration:300
110          })
111        }else{
112          if (this.currentPageNum == 1){
113              promptAction.showToast({
114              message:"没有内容!",
115              duration:300
116              })
117          }else{
118              this.clickAnimateTo(true)
119          }
120        }
121      }
122    })
123    ```
124## 完整代码
125完整示例代码如下:
126```ts
127import display from '@ohos.display'
128import promptAction from '@ohos.promptAction';
129import window from '@ohos.window';
130import common from '@ohos.app.ability.common';
131
132@Component
133struct ReadPage{
134  @Prop content:string= ""
135
136  build(){
137    Column(){
138      Text(this.content)
139        .textAlign(TextAlign.Center)
140        .width("100%")
141        .height("100%")
142        .fontSize(50)
143    }
144    .borderColor(Color.Black)
145    .borderWidth(2)
146    .width("100%")
147    .height("100%")
148  }
149}
150
151@Entry
152@Component
153export struct Reader{
154  @State bgColor:string = "#EDA7A7"
155  @State content1:string = ""
156  @State content2:string = ""
157  @State content3:string = ""
158  private currentPageNum:number = 1
159  @State offsetX:number = 0
160  @State curPosition:number = 0
161  private panOption:PanGestureOptions = new PanGestureOptions({direction:PanDirection.Left | PanDirection.Right})
162  private screenW:number = px2vp(display.getDefaultDisplaySync().width)
163  // 沉浸式窗口
164  context:common.UIAbilityContext = getContext(this) as common.UIAbilityContext
165  async setSystemBar(){
166    let windowClass:window.Window = await window.getLastWindow(this.context)
167    await windowClass.setWindowSystemBarProperties({
168      navigationBarColor:"#EDA7A7",
169      statusBarColor:"#EDA7A7",
170      navigationBarContentColor:"#EDA7A7",
171      statusBarContentColor:"#EDA7A7"
172    })
173  }
174
175  aboutToAppear(){
176    console.info("oh----->内容宽度:" + this.screenW)
177    this.novelContent()
178    this.setSystemBar()
179  }
180
181  /// 构建小说内容
182  novelContent(){
183    this.content1 = (this.currentPageNum - 1) + "___小说___" + (this.currentPageNum - 1)
184    this.content2 = (this.currentPageNum) + "___小说___" + (this.currentPageNum)
185    this.content3 = (this.currentPageNum + 1) + "___小说___" + (this.currentPageNum + 1)
186    console.log("oh----->currentPage=" + this.currentPageNum)
187    console.log("oh----->content1=" + this.content1)
188    console.log("oh----->content2=" + this.content2)
189    console.log("oh----->content3=" + this.content3);
190  }
191
192  private clickAnimateTo(isLeft: Boolean){
193    animateTo({
194      duration:400,
195      curve:Curve.EaseOut,
196      onFinish:()=>{
197        if (this.offsetX > 100){
198          this.currentPageNum -= 1
199        }else if (this.offsetX < -100){
200          this.currentPageNum += 1
201          console.log("oh----->当前页变为" + this.currentPageNum)
202        }
203        this.offsetX = 0
204        this.novelContent()
205      }
206    },()=>{
207      if (isLeft){
208        this.offsetX = this.screenW
209      }else{
210        this.offsetX = -this.screenW
211      }
212    })
213  }
214
215  build(){
216    NavDestination(){
217      Stack(){
218        ReadPage({content:this.content3})
219          .backgroundColor(this.bgColor)
220
221        ReadPage({content:this.content2})
222          .translate({x:this.offsetX >= 0 ? 0:this.offsetX , y:0 , z:0})
223          .backgroundColor(this.bgColor)
224          .width(this.screenW)
225
226        ReadPage({content:this.content1})
227          .backgroundColor(this.bgColor)
228          .translate({
229            x:-this.screenW + this.offsetX
230          })
231      }
232      .onClick((event?:ClickEvent)=>{
233        if (event){
234          console.log("点击位置:" + event.x)
235          if (event.x > this.screenW / 3 * 2){
236            this.clickAnimateTo(false)
237          }else if(event.x > this.screenW /3){
238            promptAction.showToast({
239              message:"打开菜单",
240              duration:300
241            })
242          }else{
243            if (this.currentPageNum == 1){
244              promptAction.showToast({
245                message:"没有内容!",
246                duration:300
247              })
248            }else{
249              this.clickAnimateTo(true)
250            }
251          }
252        }
253      })
254    }
255    .hideTitleBar(true)
256    // 可展示标题栏
257    // .title("小说阅读器")
258  }
259}
260```
261## 参考
262[NavDestination](../application-dev/reference/apis-arkui/arkui-ts/ts-basic-components-navdestination.md)
263
264[管理应用窗口(Stage模型)](../application-dev/windowmanager/application-window-stage.md)