background

With the popularity of mobile marketing pages in recent years, a Chinese term “H5” has emerged. The most common form of H5 is the slideshow flip effect.

The fastest way to build an H5 is to use a number of libraries such as Swiper from Idangero.us and iSlider from Baidu Be-Fe. With the powerful configuration provided by these page-flipping libraries, we can achieve cool page-flipping effects. Of course, these libraries also support auto-play, click-toggle, and current page instructions, so they can be used on web pages to achieve some of the effects of Web Carousel.

Baidu H5 has also used Swiper and iSlider as the page turning framework of H5 runtime. With the increasing number of users, there are also some problems: 1. H5 platform does not fit well with these libraries, some configuration items cannot be used, and some necessary functions need to be “Hack”. 2. Some H5 have a lot of elements and animations. When turning pages on low-end models, there will be a “sense of lag” and “sense of stickiness” when turning pages, resulting in poor user experience.

We wanted the H5 page-turning library to fit perfectly into the platform itself, keeping it small and “silky” when turning pages. So we began our journey of zao and LUN.

start

The development of H5 sliding screen framework, the first question is: does the page follow the finger slide? This is also the first question of Tencent ISUX team’s “Sliding H5 Development Practice nine Questions” (the original source of this article is now 404, you can see it on other reprint sites). Here is the picture of this article to illustrate this question.

Left: Slide without following finger, right: slide with finger.

The one on the left does not follow the finger slide, but only needs to pay attention to the two time points of the finger touch beginning and leaving, and the intermediate process is not considered. So it’s relatively easy to implement. However, there is no real-time feedback on users’ operations, and the experience is not good enough. So, even though it was more complicated to implement, we decided to implement the former “follow your finger” effect.

start

Below is the most intuitive version of the H5, with all the “pages” running from top to bottom, end to end. It should be noted that the “pages” are quoted here because they are actually divs, and the pages below refer to those divs. At the same time, let’s take the most common slide in the vertical direction, and the same thing in the horizontal direction.

Basic schematic diagram

The width and height of these divs are 100% container height, and the visual area is the middle part. We listen for touchStart, TouchMove, and TouchEnd events, similar to mouse drag-and-drop: 1. Touchmove calculates the sliding distance in real time, making all pages translate along the Y axis. 3. When touchend, the final sliding distance can be obtained and compared with the set threshold. Enter the page automatic control stage: if the value is greater than the threshold, the page will slide to the next page; if the value is smaller than the threshold, the page will be restored to the starting position.

Delve into

The simple version was easy to implement in the previous section, and if there were few other requirements and fewer elements and animations on the page, it would probably be enough. But this article will explore how to achieve “silky smooth”, in fact, two words: performance.

What are the performance bottlenecks?

Our goal is to slide through pages with as little lag as possible in the “three more and one lower” (multiple pages, elements, animations, low configuration) scenario.

Let’s look at this problem in two parts: before and after the finger leaves the screen.

Before you leave the screen

When touchMove is triggered, the distance to move is calculated, and all pages need to move the same distance along the Y-axis. DOM manipulation is very “expensive”, and the touchMove event is frequently triggered. If the performance processing is not good enough, it is easy to stall.

To optimize performance, we naturally came up with a strategy: reduce DOM manipulation.

There are two parts to this: reducing DOM manipulation elements and reducing DOM manipulation attributes. For the former, invisible pages do not participate in the animation. The latter, for example, changes only one or more of an element’s CSS attributes.

Reduce DOM manipulation elements

In the original simple version of the example, when touchMove is triggered, all pages move along the Y-axis. There’s no need, because a good portion of the page is invisible. What is the minimum number of pages we need to work on? The answer is two. Recall that when we swipe, we can see at most two pages at once. This method moves with respect to all the pages and increases performance exponentially.

Reduce the attributes of DOM operations

The main idea of this method is that you only need to manipulate the DOM once to achieve the same effect, not twice. In fact, for the Slide animation, we only need to change the transform value of the page, and other DOM operations (adding the class, modifying the innerHTML of the element) are not done.

We came up with a preliminary scenario where all the pages were placed in the Container at once, except for the two pages we used, and the display property was set to None. When touchMove is performed, only the transform property on these two pages changes.

The process of touchmove can be written as a mathematical expression:

X is how far the finger slides, S is how far the page slides, sideLength is the length of the current slide edge, and if it slides along the Y-axis, it is the height of the page. Writing here, with the very popular “data driven” concept is very similar. All we have to implement is a render function with the input of the user’s interaction data and the output of the page representation.

After the finger leaves the screen

When the finger leaves the screen, we already know the result of the swipe (up or down? Flip or rebound?) , to achieve only animation effects, we have two options: scheme 1: reuse the Render logic of TouchMove, according to the speed of finger sliding, use requestAnimationFrame control animation; Scheme 2: Using CSS3 Transition animation;

The advantages of scheme 1 are: you can use the same render function in the finger sliding and animation process, maximizing the reuse of the code, logic unity; At the same time can accurately control the animation of each frame, animation curve will be more smooth. The downside is possible performance issues. Plan two is the opposite of plan one. It comes down to JS animation versus CSS animation.

Animation performance experiment

To compare the performance of the two schemes on H5 page-turning animation, let’s take a slightly more complicated example:

CPU: 6 * Slowdowns CPU: 6 * Slowdowns CPU: 6 * Slowdowns

Js animation scheme: Click here CSS animation scheme: click here

Js page-turning animation scheme, Profile results

CSS page turning animation scheme, Profile result

Through experiments, we can see that the frame rate of JS animation is mostly maintained around 30 FPS. CSS animations, on the other hand, are generally around 60 FPS. And in the animation process, it is obvious that JS animation is stuck. This is especially true on some Android models with relatively low CPU and graphics. For those interested in this question, take a look at the RAF branch of the Swiper library, which is the JS used for this comparison test.

Therefore, although the JS animation scheme looks “elegant”, it can unify the sliding process and animation process with the concept of “data-driven”. There was actually a performance bottleneck, and we had to use CSS animations after the fingers left the screen to ensure performance. Just answer a sentence “can use CSS to do, never use JS solution”.

Implementation plan

The figure below visually shows the basic idea of our implementation, with only two pages: currentPage: currentPage activePage: the next page to be turned

The rest of the page is initialized to load into the DOM structure, but display is None and z-index is 0. The “cascading” state is shown here for visual purposes.

The principle diagram of the swiper

In order to get pages easily, we use bidirectional linked list to save the page structure. Each page has prev and next pointing to the previous and next page, respectively. Our main concern is, how do we determine the activePage? The next page to go to. The answer is simple, in fact, as users start to touch the screen and swipe, they can determine: 1. Activepage = currentPage. Next 2. Activepage = currentPage.prev If the sliding distance x is greater than 0, the page is sliding down

extension

Flip effect

The page-turning effect in our example is the most common sliding effect. How to extend support for cubes, flips, etc.? If you look back at the “finger before leaving the screen” section, we figured out that S =f(x), where x is how far the user slides, and S is how far the page slides. We can extend s to “page flip Angle” or “page zoom ratio” to support other effects.

In fact, we use the transform property of CSS3 when we swipe, and the appropriate combination of translate, rotate, and scale can produce a variety of page turning effects.

More pleasing animation

This refers to animation-timing-function, taking the simplest sliding effect as an example. If it’s a linear function, the user’s slide speed is always equal to the page slide speed. And “feel” more fluid, more sensitive, it should be: first page slide speed sliding speed is greater than the user, as you progress through the pages, both tend to be the same, after a certain point per unit time, page sliding velocity, sliding speed gradually less than users will represent for distance, speed can get the relationship between x and s as follow:



Diagram of x and S (x on the horizontal axis and S on the vertical axis)

Here, I have to mention two animation schemes: JS animation and CSS animation.

One advantage of the JS animation scheme is that you can control the progress of the animation precisely, whereas CSS cannot. For example, when the user’s finger leaves the screen at x = 0.8, js can know that x is at 0.8 at the moment when the finger leaves the screen because of the same render. The following animation is completed by requestAnimationFrame, and the whole process is smooth and complete.

However, CSS animation is different. Only animation-timing-function is set before the animation starts. When the user’s finger leaves the screen at x = 0.8, the original JS control sliding process is interrupted and CSS completes the rest of the animation. CSS cannot dynamically calculate animation-timing-function according to the moment when the finger leaves the screen. Therefore, the speed of the two points does not match at the connecting point, which will affect the overall animation effect.

Unfortunately, the JS animation scheme has performance issues and we can only use CSS animation after the user’s finger leaves the screen. This “more pleasant animation” can only be used during finger swipes.

Summary and Outlook

This article describes some problems encountered in the development process of a “silky smooth” H5 page-turning library and corresponding solutions. After the establishment of the basic slide page turning model, focus on the performance problem, divided into two stages: before the finger leaves the screen and after the finger leaves the screen. The previous phase focused on reducing DOM manipulation. The latter phase focuses on the performance of animations and compares the performance data of JS animations and CSS animations, and finally comes to the conclusion that CSS animations are used after the fingers leave the screen. In addition, based on the idea of “data-driven”, we have expanded the page-turning effects and animation functions to enhance the function of the page-turning library and enrich the display effects of H5.

This paper tries to explain the whole process with the idea of “data-driven”, but has to give up temporarily due to performance problems, hoping to find a better solution in the future. Due to the limitations of the level, the text will inevitably appear flaws, welcome everyone to criticize and correct, common learning progress. Thanks to Swiper and Islider for the inspiration, and special thanks to @Ronny for the lively discussion.

The swiper library address described in this article: github.com/fex-team/sw… . The code used for master branch is currently used on Baidu H5 line. Raf branch is mentioned in the article using JS animation scheme.