Momentum based scrolling is a familiar term. It can be found in almost any situation involving scrolling, from browsers to desktop apps to mobile apps. Inertial scrolling first appeared in the IOS system, when the sliding gesture to end users, fingers left screen, page scrolling won’t stop but can according to the sliding velocity, the intensity of sliding gesture to continue for a period of time rolling effect, when the scroll to the top/bottom of the page, and may trigger a rebound “inertia” effect,

-webkit-overflow-scrolling

Safari on IOS was the first to support scrolling. We can set the elements to -webkit-overflow-scrolling: The touch attribute allows it to support inertial scrolling, in the Version of Safira 13+ all scrollable frames or set overflow scrolling elements support inertial scrolling by default,

.view {
  -webkit-overflow-scrolling: touch;
}
Copy the code

Unfortunately, in terms of compatibility, all browsers except Safari are pretty much dead,

In addition, -webkit-overflow-scrolling has its own problems on Safari.

How do you achieve momentum based scrolling in browsers that don’t support -webkit-overflow-scrolling?

First we do a simple model of inertial rolling.

modeling

Before describing inertial roll, let’s review, what is inertia? Newton’s first law indicates that all objects in the absence of external forces, always remain at rest or uniform linear motion, so inertia is an inherent property of objects. So what is inertial roll?

We can describe inertial rolling as a process in which the motion state of the object changes under the action of external force, but the object itself has inertia, so it will keep the original motion state and continue to move. Imagine that if you have a smooth enough surface, things will keep rolling inertia, but in real life, things tend to slow down and stop because of friction, air resistance, etc.

To understand this, we can use the familiar slider model to approximate the stages of the process,

Phase one,Slide the slider (F pull > F rub) to accelerate the motion from rest.

In the actual scene, we generally pay attention to the small stage before the user releases the finger, rather than the whole rolling process (the whole process has little significance), so this instant stage can also be simply simulated to uniformly accelerate the slider with balanced force.

The second stage,Release the slider so that it continues to slide with friction alone.

At this stage, the slider has an initial velocity and only receives the opposite friction force to make uniform deceleration motion. However, in the actual scene, we usually place the inertial rolling element in a container, and then the slider may roll to the boundary of the container to trigger the rebound, so we need to consider the rebound process.

We can use a spring to approximate the springback process, assuming that the left end of the slider is connected to a spring, the other end of the spring is fixed to the wall. When the inertia rolling of the slider reaches a critical point (the spring is about to be deformed), the slider will pull the spring to make it deformed. At this time, the slider is decelerated by the reverse pulling force of the spring until it stops.

The third stage,The slide block is decelerated by the reverse pull and friction of the spring.

At this stage, the slider is subjected to the joint action of the spring tension and friction force, and the acceleration increases gradually, and finally the speed becomes 0. At this time, the shape variable of the spring is the largest. Finally, the spring restores its deformation and pulls the slider to do reverse acceleration and deceleration.

In the fourth stage, the spring pulls the slider to do variable acceleration and deceleration,

Based on the above model, we can try to implement inertial rolling and inertial rebound (here using small program code as an example).

Inertial scrolling

Initialize two page elements, container and slider,

<! -- wxml -->
<wxs module="gesture" src="./index.wxs"></wxs>
<view class="container">
  <view
    class="slider"
    bindtouchstart="{{gesture.touchstart}}"
    bindtouchmove="{{gesture.touchmove}}"
    bindtouchend="{{gesture.touchend}}"
    bindtouchcancle="{{gesture.touchcancel}}"
  ></view>
</view>
Copy the code

For the first stage of the model, the slider moves in uniform acceleration, assuming that the sliding distance of the slider is S1, the sliding time is T1, and the speed of the slider is V1 when the finger leaves. According to the displacement formula,


s 1 = v 0 + v 1 2 t 1 {s}_{1} = \frac {{v}_{0}+{v}_{1}\, } {2}{t}_{1}

You can calculate the initial velocity of inertial roll,


v 1 = 2 s 1 t 1 {v}_{1} = \frac {2{s}_{1} } {{t}_{1}}

For the second stage, the slider is subjected to the reverse friction to make uniform deceleration, and the final velocity is 0m/s. It is assumed that the acceleration of the slider is A, the sliding time is T2, and the sliding distance is S2.


a = 0 v 1 t 2 a = \frac {0 – {v}_{1}}{{t}_{2}}

You can calculate how far the slider slides,


s 2 = v 1 2 2 a {s}_{2} = -\frac{{v}_{1}^{2}}{2a}

Since the deceleration of uniform deceleration is negative (a < 0), we might as well set an acceleration constant A here to satisfy the relation of A = -2a, then the sliding distance,


s 2 = v 1 2 A {s}_{2} = \frac{{v}_{1}^2}{A}

However, in practical application, we found that v1 square calculation would lead to the inertia rolling distance calculated in the final calculation is too large (that is, too sensitive to the intensity sensing of rolling gesture), so we might as well remove the square here.


s 2 = v 1 A = 2 s 1 A t 1 {s}_{2} = \frac{{v}_{1}}{A} = \frac{2{s}_{1}}{A{t}_{1}}

Therefore, when calculating the sliding distance of the slider, it only needs to record the user’s rolling distance S1 and rolling duration T1, and then set an appropriate acceleration constant A. After A lot of tests, the value of acceleration constant A is suggested to be 0.002 ~ 0.003.

var translateY = 0; // The offset of the Y axis
var startTime = 0; // Start time to trigger inertial roll
var startY = 0; // Trigger the initial Y coordinates of the inertial roll
var deceleration = 0.002; // The acceleration constant of inertial roll

function touchstart(e) {
  startTime = e.timeStamp;
  startY = e.detail.changedTouches[0].pageY;
}
Copy the code

It should be noted that for the actual scene of inertial scrolling, the scrolling distance and duration we discuss here refer to the distance and duration that can be applied to the inertial scrolling range, rather than the entire process of the user scrolling page elements, such as the following example,

User first, the elements in a very slow speed rolling for a long distance, then continue to scroll in a very short period of time and then release the fingers, this case, we need to roll the distance and time should be the second half of the users in a short period of time to trigger inertial scrolling fingers moving distance and time, with the code description is, in a touchmove event, If the user stays longer than the threshold during the sliding process, it means that inertial rolling may be triggered. At this time, we need to update the starting time and position of inertial rolling.

function touchmove(e) {
  // ...
  if (e.timeStamp - startTime > momentumTimeThreshold) {
    startTime = e.timestamp;
    startY = e.detail.changedTouches[0].pageY; }}Copy the code

Why is it possible to trigger inertial slip? Since we also need to judge the scrolling distance of the user, after the touch time ends, the triggering condition of inertial scrolling is that the duration is less than a certain threshold and the minimum displacement distance S1 is greater than a certain threshold. After testing, it is reasonable to set the threshold of residence time to 200ms ~ 300ms and the minimum displacement distance to 10px ~ 20px.


var momentumTimeThreshold = 300; // The maximum time for triggering inertial scrolling, ms
var momentumYThreshold = 15; // The minimum distance to trigger inertial roll, ms

function touchend(e) {
  var endY = e.detail.changedTouches[0].pageY;
  var duration = e.timeStamp - startTime;
  var s1 = endY - startY;
  if (duration < momentumTimeThreshold && Math.abs(s1) > momentumYThreshold) {
    // Trigger inertial slip...}}Copy the code

Next, we will analyze the rebound process of the third and fourth stages that may be triggered after inertial rolling.

The springback of inertia

For the third phase of the model, the slider by spring back tension and friction force are changed with the slow movement, the function of acceleration of slider is more and more big, the speed is reduced to 0 m/s will be short of time, we can use a slow approximate curve to describe the process, assuming that this slider triggered after the third phase of speed curve is ease – out, The scrolling time is t,

We need to calculate the rolling distance after the slider touches the boundary of the container, that is, the area s enclosed by the curve and the X-axis within the time range of 0 ~ t, which can be expressed by integration as,


s = 0 t v ( t ) d t s = \int^{t}_{0} v(t)dt

We substitute the ease-out function f(t)=− T2 +2tf(t) = -t^2 +2tf(t) =− T2 +2t,


s = 0 t ( t 2 + 2 t ) d t s = \int^{t}_{0} (-t^2 + 2t)dt

According to the Newton-Leibniz formula,


a b f ( x ) d x = F ( b ) F ( a ) = F ( x ) a b \int^{b}_{a}f(x)dx = F(b) – F(a) = F(x)|^{b}_{a}

To compute the integral above, we need to find the antiderivative of ease-out, F,


f ( x ) = t 2 + 2 t f'(x) = -t^2 + 2t


F = 1 3 t 3 + t 2 + C F = -\frac {1}{3}t^3 + t^2 + C


s = F ( t ) F ( 0 ) s = F(t) – F(0)

In principle, as long as we have defined the slider of the rolling time and scroll curve, the rolling distance can be calculated, the rolling distance is also a model in the fourth stage of the springback, but in practice, the definition and function of scroll curve calculation is very complicated, we can consider to do appropriate to simplify the above model.

Since the acceleration of the slider in the third stage is greater than that in the case of uniform deceleration,

Therefore, the rolling distance of the slider in the third stage after it touches the boundary must be smaller than that in the case of uniform deceleration. Assume that the rolling distance of the slider in the third stage is S3 and the boundary value of the container is boundY. Previously, we analyzed that the total sliding distance of the slider in the case of uniform motion is S2, then it must satisfy the following equation:

s3 < s2 - boundY
Copy the code

So let’s say we have a springback resistance constant B that satisfies,

s3 = (s2 - boundY) / B
Copy the code

After testing, the value of resistance constant B here is more appropriate between 10 and 12. So far, we have completed a simple implementation of some key indicators of the above model (rolling distance, rolling duration, start and stop conditions, etc.). Next, we will pay attention to the rolling effect (slow effect) of the slider in each stage of the model, that is, the speed curve.

slow

In real life, objects do not suddenly start or stop, or keep moving at a constant speed. In dynamic effect design, we often use slow motion to describe the variable speed movement of objects, so that the whole movement process is more natural. CSS provides transition and animation to achieve slow motion effect. Here we need to pay attention to two important indicators of easing, namely easing function and easing duration.

We can use Bezier curve to describe the relationship of animation progress over time. Next, we use some open source online Bezier curve drawing tools, such as Cubic – Bezier, to do a simple analysis of the slider model.

For the first and second stages of the model, that is, inertial rolling is triggered and the container boundary is not reached after rolling, the slider first accelerates, inertial rolling is triggered after reaching the maximum speed, and finally decelerates until it stops.

The gentle curve of the whole process can be described as cubic- Bezier (0, 0.5, 0.2, 1),

Here, we need to consider setting the slow time dynamically according to the distance of inertial rolling. For example, define strong and weak inertia time, and then give a segmentation value of strong and weak inertia.

var inertialThreshold = 100; // Strong and weak inertia segmentation value

.weekInertial {
  transition: transform cubic-bezier(0.0.5.0.2.1) 1.5s;
}

.strongInertial {
  transition: transform cubic-bezier(0.0.5.0.2.1) 3.5s;
}
Copy the code

For the first, second and third stages of the model, the slider first triggers inertial rolling, but the distance of inertial rolling exceeds the boundary of the container, and then slows down rapidly until it finally stops.

The gentle curve of the whole process can be described as cubic- Bezier (0.25, 0.46, 0.45, 0.94),

Here it is important to note that in addition to the boundary values of the container, also need to set up to allow more than one of the biggest border, when we calculate the slider inertial scrolling distance is too large (i.e., the user’s sliding gestures too strong), to have more than allow the biggest border, we need to reset the rolling distance for the biggest border.

For the fourth phase of the model, the slider returns to the boundary from some value that exceeds the boundary, kind of triggering a rebound,

The gentle curve of this process can be described as cubic- Bezier (0.16, 0.5, 0.4, 1),

There are two trigger modes of rebound: rebound after the user slides over the boundary and rebound after the inertial roll over the boundary. The former is easier to monitor and can be handled when the user touches the end of the event, but how do we know if inertial scrolling is over when it bounces back beyond the boundary?

The rebound must be after the inertial scroll (scroll we set animation), by listening for the end event of the slow animation,

function transitionend() {
  var overBounce = translateY > minY || translateY < maxY;
  if (overBounce) {
    if (translateY > minY) translateY = minY;
    if (translateY < maxY) translateY = maxY;
    ins.addClass('rebound'); setStyle(); }}Copy the code

In addition, when inertial scrolling is not over, or the user is in the process of bouncing back and touches the element again, we should pause the easing. How do we stop the easing? So if you recall that we set the easing using CSS styles, one idea is that as soon as the user touches the element again, we get the element’s current computed style, and then we get the offset that we need to reset it,

function touchstart(e, instance) {
  // ...
  stop();
  // ...
}

function stop() {
  var computedStyle = ins.getComputedStyle(['transform']);
  var matrix = computedStyle.transform;
  var offsetY = +matrix.split(') ') [0].split(', ') [5];
  if (matrix.indexOf('matrix')! = = -1&& offsetY) { translateY = offsetY; setStyle(); }}Copy the code

The above is a simple implementation of inertia scrolling on the small program side. We can also use similar ideas to achieve inertia scrolling on other platforms. You can click on a runnable code fragment based on the above ideas to open the experience in wechat developer tools.

Refer to the link

  • The Basics of easing
  • Choosing the right easing
  • The front end also needs to understand physics – inertia rolling
  • Understand the mathematics of Bézier curves
  • Uniformly variable linear motion

Write in the last

This article first in my blog, uneducated, unavoidably have mistakes, the article is wrong also hope not hesitate to correct!

If you have questions or find mistakes, you can ask questions and errata in the comments section,

If you like or are inspired by it, welcome star and encourage the author.

(after)