1e41f4b71Sopenharmony_ci# High-Performance Swiper Development
2e41f4b71Sopenharmony_ci
3e41f4b71Sopenharmony_ci## Background
4e41f4b71Sopenharmony_ci
5e41f4b71Sopenharmony_ciDuring application development, the **\<Swiper>** component is frequently seen in page turning scenarios, as in home screen and gallery applications. In general cases, the on-demand loading technique is adopted for the **\<Swiper>** component. This means that, a new page is loaded and rendered only when it is about to be displayed.This process includes the following steps:
6e41f4b71Sopenharmony_ci
7e41f4b71Sopenharmony_ci- If the target page uses a custom component decorated by @Component, the build function of the custom component is executed and the internal UI component is created.
8e41f4b71Sopenharmony_ci
9e41f4b71Sopenharmony_ci- If [LazyForEach](../quick-start/arkts-rendering-control-lazyforeach.md) is used, the UI generation function of LazyForEach is executed to generate UI components.
10e41f4b71Sopenharmony_ci
11e41f4b71Sopenharmony_ci- After the UI components are built, their layout is calculated and drawn.
12e41f4b71Sopenharmony_ci
13e41f4b71Sopenharmony_ciIn complex page scenarios, the preceding process may last for a long time. As a result, frame freezing occurs during swiping, undermining the swiping experience and even becoming a performance bottleneck of the entire application. For example, in terms of browsing large images in the Gallery application, if an image is not loaded until the first frame of swiping, the first frame will take a long time or even frame loss will occur, slowing down the application performance.
14e41f4b71Sopenharmony_ci
15e41f4b71Sopenharmony_ciThis is where the pre-loading mechanism of the **\<Swiper>** component comes into picture. By allowing for prebuilding and prerendering components in the idle time of the main thread, this mechanism can greatly improve the swiping experience.
16e41f4b71Sopenharmony_ci
17e41f4b71Sopenharmony_ci## When to Use
18e41f4b71Sopenharmony_ci
19e41f4b71Sopenharmony_ciThe pre-loading mechanism of the **\<Swiper>** component is preferred where time-consuming loading is involved, especially in the following scenarios:
20e41f4b71Sopenharmony_ci
21e41f4b71Sopenharmony_ci- The number of child components in **\<Swiper>** is greater than or equal to 5.
22e41f4b71Sopenharmony_ci
23e41f4b71Sopenharmony_ci- The child components in **\<Swiper>** exhibit complex animations.
24e41f4b71Sopenharmony_ci
25e41f4b71Sopenharmony_ci- Loading child components in **\<Swiper>** is accompanied by time-consuming operations such as network requests.
26e41f4b71Sopenharmony_ci
27e41f4b71Sopenharmony_ci- The child components in **\<Swiper>** contain a large number of images or resources to be rendered.
28e41f4b71Sopenharmony_ci
29e41f4b71Sopenharmony_ci## Understanding Pre-loading of \<Swiper>
30e41f4b71Sopenharmony_ci
31e41f4b71Sopenharmony_ciThe pre-loading mechanism of **\<Swiper>** is designed to offer an uninterrupted swiping experience, by allowing **\<Swiper>** to load page content before the associated page is actually displayed As opposed to the traditional approach, where page loading happens concurrently with processing of the swipe event, pre-loading is triggered when the animation for finger lifting starts. As the animation for finger lifting is calculated is in the rendering thread, the main thread has time to perform pre-loading. To improve swipe performance with less memory usage, combine pre-loading with the on-demand loading and destruction capabilities of **LazyForEach**.
32e41f4b71Sopenharmony_ci
33e41f4b71Sopenharmony_ci## How to Use
34e41f4b71Sopenharmony_ci
35e41f4b71Sopenharmony_ci- Set the number of elements to load ahead of time through the [cachedCount](../reference/apis-arkui/arkui-ts/ts-container-swiper.md#attributes) attribute of **\<Swiper>**.
36e41f4b71Sopenharmony_ci
37e41f4b71Sopenharmony_ciBelow shows the pre-loading result of the **\<Swiper>** component that contains five pages and its **cacheCount** attribute set to **1** and **loop** attribute set to **false**.
38e41f4b71Sopenharmony_ci ![loop=false](figures/swiper_loop_false.png)
39e41f4b71Sopenharmony_ci
40e41f4b71Sopenharmony_ci\
41e41f4b71Sopenharmony_ci Below shows the pre-loading result of the **\<Swiper>** component that contains five pages and its **cacheCount** attribute set to **1** and **loop** attribute set to **true**.
42e41f4b71Sopenharmony_ci ![loop=true](figures/swiper_loop_true.png)
43e41f4b71Sopenharmony_ci
44e41f4b71Sopenharmony_ci- [LazyForEach](../quick-start/arkts-rendering-control-lazyforeach.md) is used to dynamically load and destroy the child components in **\<Swiper>**.
45e41f4b71Sopenharmony_ci
46e41f4b71Sopenharmony_ci**Example**
47e41f4b71Sopenharmony_ci
48e41f4b71Sopenharmony_ci```TypeScript
49e41f4b71Sopenharmony_ciclass MyDataSource implements IDataSource { // LazyForEach data source
50e41f4b71Sopenharmony_ci  private list: number[] = [];
51e41f4b71Sopenharmony_ci
52e41f4b71Sopenharmony_ci  constructor(list: number[]) {
53e41f4b71Sopenharmony_ci    this.list = list;
54e41f4b71Sopenharmony_ci  }
55e41f4b71Sopenharmony_ci
56e41f4b71Sopenharmony_ci  totalCount(): number {
57e41f4b71Sopenharmony_ci    return this.list.length;
58e41f4b71Sopenharmony_ci  }
59e41f4b71Sopenharmony_ci
60e41f4b71Sopenharmony_ci  getData(index: number): number {
61e41f4b71Sopenharmony_ci    return this.list[index];
62e41f4b71Sopenharmony_ci  }
63e41f4b71Sopenharmony_ci
64e41f4b71Sopenharmony_ci  registerDataChangeListener(_: DataChangeListener): void {
65e41f4b71Sopenharmony_ci  }
66e41f4b71Sopenharmony_ci
67e41f4b71Sopenharmony_ci  unregisterDataChangeListener(): void {
68e41f4b71Sopenharmony_ci  }
69e41f4b71Sopenharmony_ci}
70e41f4b71Sopenharmony_ci
71e41f4b71Sopenharmony_ci@Component
72e41f4b71Sopenharmony_cistruct SwiperChildPage { // Child component in <Swiper>
73e41f4b71Sopenharmony_ci  @State arr: number[] = [];
74e41f4b71Sopenharmony_ci
75e41f4b71Sopenharmony_ci  aboutToAppear(): void {
76e41f4b71Sopenharmony_ci    for (let i = 1; i <= 100; i++) {
77e41f4b71Sopenharmony_ci      this.arr.push(i);
78e41f4b71Sopenharmony_ci    }
79e41f4b71Sopenharmony_ci  }
80e41f4b71Sopenharmony_ci
81e41f4b71Sopenharmony_ci  build() {
82e41f4b71Sopenharmony_ci    Column() {
83e41f4b71Sopenharmony_ci      List({ space: 20 }) {
84e41f4b71Sopenharmony_ci        ForEach(this.arr, (index: number) => {
85e41f4b71Sopenharmony_ci          ListItem() {
86e41f4b71Sopenharmony_ci            Text(index.toString())
87e41f4b71Sopenharmony_ci              .height('4.5%')
88e41f4b71Sopenharmony_ci              .fontSize(16)
89e41f4b71Sopenharmony_ci              .textAlign(TextAlign.Center)
90e41f4b71Sopenharmony_ci              .backgroundColor(0xFFFFFF)
91e41f4b71Sopenharmony_ci          }
92e41f4b71Sopenharmony_ci          .border({ width: 2, color: Color.Green })
93e41f4b71Sopenharmony_ci        }, (index: number) => index.toString());
94e41f4b71Sopenharmony_ci      }
95e41f4b71Sopenharmony_ci      .height("95%")
96e41f4b71Sopenharmony_ci      .width("95%")
97e41f4b71Sopenharmony_ci      .border({ width: 3, color: Color.Red })
98e41f4b71Sopenharmony_ci      .lanes({ minLength: 40, maxLength: 40 })
99e41f4b71Sopenharmony_ci      .alignListItem(ListItemAlign.Start)
100e41f4b71Sopenharmony_ci      .scrollBar(BarState.Off)
101e41f4b71Sopenharmony_ci
102e41f4b71Sopenharmony_ci    }.width('100%').height('100%').padding({ top: 5 });
103e41f4b71Sopenharmony_ci  }
104e41f4b71Sopenharmony_ci}
105e41f4b71Sopenharmony_ci
106e41f4b71Sopenharmony_ci@Entry
107e41f4b71Sopenharmony_ci@Preview
108e41f4b71Sopenharmony_ci@Component
109e41f4b71Sopenharmony_cistruct SwiperExample {
110e41f4b71Sopenharmony_ci  private dataSrc: MyDataSource = new MyDataSource([]);
111e41f4b71Sopenharmony_ci
112e41f4b71Sopenharmony_ci  aboutToAppear(): void {
113e41f4b71Sopenharmony_ci    let list: Array<number> = []
114e41f4b71Sopenharmony_ci    for (let i = 1; i <= 10; i++) {
115e41f4b71Sopenharmony_ci      list.push(i);
116e41f4b71Sopenharmony_ci    }
117e41f4b71Sopenharmony_ci    this.dataSrc = new MyDataSource(list);
118e41f4b71Sopenharmony_ci  }
119e41f4b71Sopenharmony_ci
120e41f4b71Sopenharmony_ci  build() {
121e41f4b71Sopenharmony_ci    Column({ space: 5 }) {
122e41f4b71Sopenharmony_ci      Swiper() {
123e41f4b71Sopenharmony_ci        LazyForEach(this.dataSrc, (_: number) => {
124e41f4b71Sopenharmony_ci          SwiperChildPage();
125e41f4b71Sopenharmony_ci        }, (item: number) => item.toString());
126e41f4b71Sopenharmony_ci      }
127e41f4b71Sopenharmony_ci      .loop(false)
128e41f4b71Sopenharmony_ci      .cachedCount(1) // Load one child component ahead of time.
129e41f4b71Sopenharmony_ci      .indicator(true)
130e41f4b71Sopenharmony_ci      .duration(100)
131e41f4b71Sopenharmony_ci      .displayArrow({
132e41f4b71Sopenharmony_ci        showBackground: true,
133e41f4b71Sopenharmony_ci        isSidebarMiddle: true,
134e41f4b71Sopenharmony_ci        backgroundSize: 40,
135e41f4b71Sopenharmony_ci        backgroundColor: Color.Orange,
136e41f4b71Sopenharmony_ci        arrowSize: 25,
137e41f4b71Sopenharmony_ci        arrowColor: Color.Black
138e41f4b71Sopenharmony_ci      }, false)
139e41f4b71Sopenharmony_ci      .curve(Curve.Linear)
140e41f4b71Sopenharmony_ci
141e41f4b71Sopenharmony_ci    }.width('100%')
142e41f4b71Sopenharmony_ci    .margin({ top: 5 })
143e41f4b71Sopenharmony_ci  }
144e41f4b71Sopenharmony_ci}
145e41f4b71Sopenharmony_ci
146e41f4b71Sopenharmony_ci```
147e41f4b71Sopenharmony_ci
148e41f4b71Sopenharmony_ci## Verification
149e41f4b71Sopenharmony_ci
150e41f4b71Sopenharmony_ciTo better demonstrate the performance improvements brought by the **\<Swiper>** component's pre-loading mechanism, the following prerequisites are used:
151e41f4b71Sopenharmony_ci
152e41f4b71Sopenharmony_ci- The **\<Swiper>** component contains 10 **\<List>** components.
153e41f4b71Sopenharmony_ci
154e41f4b71Sopenharmony_ci- Each of the **\<List>** component contains 100 list items.
155e41f4b71Sopenharmony_ci
156e41f4b71Sopenharmony_ciUnder these prerequisites, using the **\<Swiper>** component's pre-loading mechanism can save about 40% time for each page turning action and ensure that no frame is lost during page turning.
157e41f4b71Sopenharmony_ci
158e41f4b71Sopenharmony_ci## Optimization Suggestion
159e41f4b71Sopenharmony_ci
160e41f4b71Sopenharmony_ciComponent building and layout calculation take some time. Therefore, the value of **cachedCount** cannot be as large as possible. If the value of **cachedCount** is too large, the application performance may deteriorate. Currently, the duration of the animation for finger lifting is about 400 ms for the **\<Swiper>** component. If the time for the application to load a child component ranges from 100 ms to 200 ms, you are advised to set **cachedCount** to **1** or **2**, so that the pre-loading can be completed before the animation ends.
161e41f4b71Sopenharmony_ci
162e41f4b71Sopenharmony_ciIn light of this, the [OnAnimationStart](../reference/apis-arkui/arkui-ts/ts-container-swiper.md#events) callback provided by **\<Swiper>** can be used, which is called when the switching animation starts. At this time, the main thread is idle, and the application can make full use of this period to preload resources such as images, reducing the time required for preloading nodes specified by **cachedCount**.
163e41f4b71Sopenharmony_ci
164e41f4b71Sopenharmony_ci**Example**
165e41f4b71Sopenharmony_ci
166e41f4b71Sopenharmony_ciThe page code of the child component in **\<Swiper>** is as follows.
167e41f4b71Sopenharmony_ci
168e41f4b71Sopenharmony_ciWhen the child component is built for the first time (the lifecycle reaches [aboutToAppear](../quick-start/arkts-page-custom-components-lifecycle.md)), the system checks the data source for data that matches the index. If no data is found, the system loads resources before building the node. If data is found, the system directly builds the node.
169e41f4b71Sopenharmony_ci
170e41f4b71Sopenharmony_ci```TypeScript
171e41f4b71Sopenharmony_ciimport image from '@ohos.multimedia.image';
172e41f4b71Sopenharmony_ciimport { MyDataSource } from './Index'
173e41f4b71Sopenharmony_ci
174e41f4b71Sopenharmony_ci@Component
175e41f4b71Sopenharmony_ciexport struct PhotoItem { // Child component in <Swiper>
176e41f4b71Sopenharmony_ci  myIndex: number = 0;
177e41f4b71Sopenharmony_ci  private dataSource: MyDataSource = new MyDataSource([]);
178e41f4b71Sopenharmony_ci  context = getContext(this);
179e41f4b71Sopenharmony_ci  @State imageContent: image.PixelMap | undefined = undefined;
180e41f4b71Sopenharmony_ci
181e41f4b71Sopenharmony_ci  aboutToAppear(): void {
182e41f4b71Sopenharmony_ci    console.info(`aboutToAppear` + this.myIndex);
183e41f4b71Sopenharmony_ci    this.imageContent = this.dataSource.getData(this.myIndex)?.image;
184e41f4b71Sopenharmony_ci    if (!this.imageContent) {// Check the data source for data that matches the index. If no data is found, the system loads resources before building the node.
185e41f4b71Sopenharmony_ci      try {
186e41f4b71Sopenharmony_ci        // Obtain a resource manager.
187e41f4b71Sopenharmony_ci        const resourceMgr = this.context.resourceManager;
188e41f4b71Sopenharmony_ci        // Obtain the ArrayBuffer instance of item.jpg in the rawfile folder.
189e41f4b71Sopenharmony_ci        let str = "item" + (this.myIndex + 1) + ".jpg";
190e41f4b71Sopenharmony_ci        resourceMgr.getRawFileContent(str).then((value) => {
191e41f4b71Sopenharmony_ci          // Create an ImageSource instance.
192e41f4b71Sopenharmony_ci          const imageSource = image.createImageSource(value.buffer);
193e41f4b71Sopenharmony_ci          imageSource.createPixelMap().then((value) => {
194e41f4b71Sopenharmony_ci            console.log("aboutToAppear push" + this.myIndex)
195e41f4b71Sopenharmony_ci            this.dataSource.addData(this.myIndex, { description: "" + this.myIndex, image: value })
196e41f4b71Sopenharmony_ci            this.imageContent = value;
197e41f4b71Sopenharmony_ci          })
198e41f4b71Sopenharmony_ci        })
199e41f4b71Sopenharmony_ci      } catch (err) {
200e41f4b71Sopenharmony_ci        console.log("error code" + err);
201e41f4b71Sopenharmony_ci      }
202e41f4b71Sopenharmony_ci    }
203e41f4b71Sopenharmony_ci  }
204e41f4b71Sopenharmony_ci
205e41f4b71Sopenharmony_ci  build() {
206e41f4b71Sopenharmony_ci    Column() {
207e41f4b71Sopenharmony_ci      Image(this.imageContent)
208e41f4b71Sopenharmony_ci        .width("100%")
209e41f4b71Sopenharmony_ci        .height("100%")
210e41f4b71Sopenharmony_ci    }
211e41f4b71Sopenharmony_ci  }
212e41f4b71Sopenharmony_ci}
213e41f4b71Sopenharmony_ci```
214e41f4b71Sopenharmony_ci
215e41f4b71Sopenharmony_ciThe code of the \<Swiper> home page is as follows:
216e41f4b71Sopenharmony_ci```
217e41f4b71Sopenharmony_ciimport Curves from '@ohos.curves';
218e41f4b71Sopenharmony_ciimport { PhotoItem } from './PhotoItem'
219e41f4b71Sopenharmony_ciimport image from '@ohos.multimedia.image';
220e41f4b71Sopenharmony_ci
221e41f4b71Sopenharmony_ciinterface MyObject {
222e41f4b71Sopenharmony_ci  description: string,
223e41f4b71Sopenharmony_ci  image: image.PixelMap,
224e41f4b71Sopenharmony_ci};
225e41f4b71Sopenharmony_ci
226e41f4b71Sopenharmony_ciexport class MyDataSource implements IDataSource {
227e41f4b71Sopenharmony_ci  private list: MyObject[] = []
228e41f4b71Sopenharmony_ci
229e41f4b71Sopenharmony_ci  constructor(list: MyObject[]) {
230e41f4b71Sopenharmony_ci    this.list = list
231e41f4b71Sopenharmony_ci  }
232e41f4b71Sopenharmony_ci
233e41f4b71Sopenharmony_ci  totalCount(): number {
234e41f4b71Sopenharmony_ci    return this.list.length
235e41f4b71Sopenharmony_ci  }
236e41f4b71Sopenharmony_ci
237e41f4b71Sopenharmony_ci  getData(index: number): MyObject {
238e41f4b71Sopenharmony_ci    return this.list[index]
239e41f4b71Sopenharmony_ci  }
240e41f4b71Sopenharmony_ci
241e41f4b71Sopenharmony_ci  registerDataChangeListener(listener: DataChangeListener): void {
242e41f4b71Sopenharmony_ci  }
243e41f4b71Sopenharmony_ci
244e41f4b71Sopenharmony_ci  unregisterDataChangeListener(listener: DataChangeListener): void {
245e41f4b71Sopenharmony_ci  }
246e41f4b71Sopenharmony_ci
247e41f4b71Sopenharmony_ci  addData(index: number, data: MyObject) {
248e41f4b71Sopenharmony_ci    this.list[index] = data;
249e41f4b71Sopenharmony_ci  }
250e41f4b71Sopenharmony_ci}
251e41f4b71Sopenharmony_ci
252e41f4b71Sopenharmony_ci@Entry
253e41f4b71Sopenharmony_ci@Component
254e41f4b71Sopenharmony_cistruct Index {
255e41f4b71Sopenharmony_ci  @State currentIndex: number = 0;
256e41f4b71Sopenharmony_ci  cacheCount: number = 1
257e41f4b71Sopenharmony_ci  swiperController: SwiperController = new SwiperController();
258e41f4b71Sopenharmony_ci  private data: MyDataSource = new MyDataSource([]);
259e41f4b71Sopenharmony_ci  context = getContext(this);
260e41f4b71Sopenharmony_ci
261e41f4b71Sopenharmony_ci  aboutToAppear() {
262e41f4b71Sopenharmony_ci    let list: MyObject[] = []
263e41f4b71Sopenharmony_ci    for (let i = 0; i < 6; i++) {
264e41f4b71Sopenharmony_ci      list.push({ description: "", image: this.data.getData(this.currentIndex)?.image })
265e41f4b71Sopenharmony_ci    }
266e41f4b71Sopenharmony_ci    this.data = new MyDataSource(list)
267e41f4b71Sopenharmony_ci  }
268e41f4b71Sopenharmony_ci
269e41f4b71Sopenharmony_ci  build() {
270e41f4b71Sopenharmony_ci    Swiper(this.swiperController) {
271e41f4b71Sopenharmony_ci      LazyForEach(this.data, (item: MyObject, index?: number) => {
272e41f4b71Sopenharmony_ci        PhotoItem({
273e41f4b71Sopenharmony_ci          myIndex: index,
274e41f4b71Sopenharmony_ci          dataSource: this.data
275e41f4b71Sopenharmony_ci        })
276e41f4b71Sopenharmony_ci      })
277e41f4b71Sopenharmony_ci    }
278e41f4b71Sopenharmony_ci    .cachedCount(this.cacheCount)
279e41f4b71Sopenharmony_ci    .curve(Curves.interpolatingSpring(0, 1, 228, 30))
280e41f4b71Sopenharmony_ci    .index(this.currentIndex)
281e41f4b71Sopenharmony_ci    .indicator(true)
282e41f4b71Sopenharmony_ci    .loop(false)
283e41f4b71Sopenharmony_ci    // Pre-load resources in the OnAnimationStart callback.
284e41f4b71Sopenharmony_ci    .onAnimationStart((index: number, targetIndex: number) => {
285e41f4b71Sopenharmony_ci      console.info("onAnimationStart " + index + " " + targetIndex);
286e41f4b71Sopenharmony_ci      if (targetIndex !== index) {
287e41f4b71Sopenharmony_ci        try {
288e41f4b71Sopenharmony_ci          // Obtain a resource manager.
289e41f4b71Sopenharmony_ci          const resourceMgr = this.context.resourceManager;
290e41f4b71Sopenharmony_ci          // Obtain the ArrayBuffer instance of item.jpg in the rawfile folder.
291e41f4b71Sopenharmony_ci          let str = "item" + (targetIndex + this.cacheCount + 2) + ".jpg";
292e41f4b71Sopenharmony_ci          resourceMgr.getRawFileContent(str).then((value) => {
293e41f4b71Sopenharmony_ci            // Create an ImageSource instance.
294e41f4b71Sopenharmony_ci            const imageSource = image.createImageSource(value.buffer);
295e41f4b71Sopenharmony_ci            imageSource.createPixelMap().then((value) => {
296e41f4b71Sopenharmony_ci              this.data.addData(targetIndex + this.cacheCount + 1, {
297e41f4b71Sopenharmony_ci                description: "" + (targetIndex + this.cacheCount + 1),
298e41f4b71Sopenharmony_ci                image: value
299e41f4b71Sopenharmony_ci              })
300e41f4b71Sopenharmony_ci            })
301e41f4b71Sopenharmony_ci          })
302e41f4b71Sopenharmony_ci        } catch (err) {
303e41f4b71Sopenharmony_ci          console.log("error code" + err);
304e41f4b71Sopenharmony_ci        }
305e41f4b71Sopenharmony_ci      }
306e41f4b71Sopenharmony_ci    })
307e41f4b71Sopenharmony_ci    .width('100%')
308e41f4b71Sopenharmony_ci    .height('100%')
309e41f4b71Sopenharmony_ci  }
310e41f4b71Sopenharmony_ci}
311e41f4b71Sopenharmony_ci
312e41f4b71Sopenharmony_ci```
313e41f4b71Sopenharmony_ci
314e41f4b71Sopenharmony_ci## Takeaway
315e41f4b71Sopenharmony_ci
316e41f4b71Sopenharmony_ci- A tie-in of the **\<Swiper>** component's pre-loading mechanism and **LazyForEach** delivers optimal performance improvements.
317e41f4b71Sopenharmony_ci
318e41f4b71Sopenharmony_ci- The value of **cachedCount** for pre-loading must be set appropriately based on the loading duration of a single child component. A larger value does not necessarily lead to better performance. If a child component takes *N* ms to load, you are advised to set **cachedCount** to a value less than 400/*N*.
319e41f4b71Sopenharmony_ci
320e41f4b71Sopenharmony_ci- To further improve pre-loading efficiency to meet high-demanding performance requirements, use the **\<Swiper>** component's pre-loading mechanism together with the **OnAnimationStart** callback.
321