laitimes

The TS+TS+Node (Song) 到和 侧 𻡁 射)

author:Senior Internet Architect

Preface

In 2023, it seems too simple to write a one-click to the top and the side menu bar ejection effect, but since it is a piece of content planned in the catalog, let's finish it as planned.

First of all, let's take a look at the specific effect through two GIFs, and then let's study how to achieve it!

  • One key to the top:
The TS+TS+Node (Song) 到和 侧 𻡁 射)
  • Side menu bar bounce effect:
The TS+TS+Node (Song) 到和 侧 𻡁 射)

One click to the top

Going back to the top of the web page is essentially changing the value of the scrollTop, so the simplest and most crude way is to directly modify the scrollTop of the scroll element.

Generally speaking, the scroll element is the body of the web page, so we will be used to modifying the scrollTop of the body element to set its value to 0.

document.body.scrollTop = 0
           

But sometimes, we find that the scrollTop of the body doesn't work, and the scrollbar still doesn't move. This is because:

  • When a page has a DOCTYPE, or if a DOCTYPE is specified, use document.documentElement.scrollTop.
  • When the page does not have a DOCTYPE, or does not specify a DOCTYPE, use document.body.scrollTop.
  • In order to be compatible with various situations, it is recommended to use both spellings.

So for the sake of insurance, just write both:

document.documentElement.scrollTop = 0
document.body.scrollTop = 0
           

Although the function of returning to the top of the web page is implemented, the web page is back to the top in an instant, and the transition effect is missing. If we want to have a transitional effect, we need to find something else!

scroll-behavior

The first way is to set the CSS property scroll-behavior of the target element to smooth.

html {
  scroll-behavior: smooth;
}
           

At this time, you can get a smooth scrolling effect by using JS to manipulate scrollTop, and the easing effect and duration of scrolling are determined by the browser's own implementation.

It's also the easiest, just add an extra line of CSS attributes!

Compatibility Reference:

The TS+TS+Node (Song) 到和 侧 𻡁 射)

It can be found that IE does not support this attribute at all, and Safari support below iOS 15 can be considered almost none. It's okay to pay a little attention to the production environment of the actual project, but this is a personal project, so it doesn't hurt to use it casually!

window.scrollTo

Since CSS can provide smooth scrolling, what about JS? The answer is yes! You can also use window.scrollTo provided by the BOM to achieve the effect of smooth, where the behavior parameter also supports smooth.

window.scrollTo({
    top: 0,
    behavior: "smooth"
});
           

Isn't it also very simple, and it is also a good choice to use this API call to replace directly modifying scrollTop attributes!

So behavoir: What about the compatibility of "smooth", which can be said to be similar to CSS smooth, or even worse!

The TS+TS+Node (Song) 到和 侧 𻡁 射)

Safari 明确表示不支持smooth:

Safari does not have support for the smooth scroll behavior.

Customize the easing effect

Since the above two methods are not very compatible, we can try to implement them in JS ourselves. Thanks to the flexibility of JS, we have good control over the easing effect and duration of the entire scrolling behavior.

There are two issues to consider here, one is the total duration and the other is the easing effect.

Assuming that the current scrolling distance of the web page is 1000px, and the effect of scrolling to the top is planned to be completed in 0.5 seconds, assuming that it is scrolling at a constant speed, then it takes 0.5/1000 seconds (that is, 0.5 milliseconds) for each px to scroll.

But 0.5 milliseconds is not perceptible to the human eye, and JS timers cannot handle logic below 16 milliseconds. So let's think about it from a different perspective, and convert 1 second to 60 frames, so 0.5 seconds is 30 frames, so this animation is a total of 30 frames, as long as I split the 1000px scroll into 30 frames.

If it's constant, doesn't it mean that 1 frame scrolls (1000 / 30) px, which is about 33.33px. In this way, the problem is simple.

What if you don't want it to be uniform? That is, the distance you can scroll is different from frame to frame, and for this easing effect, we usually use Bezier curves. It doesn't matter if we don't understand the math of Bezier curves, we just need to tune an effect that we feel comfortable with and keep the value of the curve.

Open cubic-bezier.com debug Bezier curves online, drag the control points to adjust until you get the curve you are satisfied with.

The TS+TS+Node (Song) 到和 侧 𻡁 射)

After a simple observation, we can see that ease is fast first and then slow, and linear, needless to say, is uniform. I feel that easy or ease-in-out is more suitable for the scene of one-click to the top.

Once you have the right Bezier curve, it's time to turn it into code, so let's install the bezier-easing library first.

Initialize it first,

import BezierEasing from "bezier-easing";

const easingFunc = BezierEasing(0.42, 0, 1, 1);
           

The resulting easingFunc is a function, and its input is a value of 0 ~ 1, which represents the resulting value that should be obtained in each position of 0% ~ 100%. In other words, if you enter 0.5, you can get what the value of scrollTop should be after half of the scrolling time, which means that the value of each time point can be calculated according to the proportion of the input.

const scrollTop = easingFunc(0.5)
           

So how to do the logic of each frame, there are two ways to refer to:

  • With window.requestAnimationFrame, it is related to the refresh rate of the screen, which is usually 60 FPS.
  • Before requestAnimationFrame appeared, setTimeout using 16.67ms was also a common choice.

Implementation:

  • The total number of frames is calculated based on the total duration (in seconds), which is duration * 60
  • requestAnimationFrame according to the current number of frames (step), combined with the Bezier curve function to get the current position that should be scrolled to, modify scrollTop, as long as the step does not reach the total number of frames, you can make step add 1, and recursively call the above logic.

Code References:

function animateSetScrollTop({ target = document.documentElement, start, end, stepNo = 1, stepTotal }: StepOptions) {
    const next = getNextScrollTopValue(start, end, stepNo, stepTotal);
    window.requestAnimationFrame(() => {
        setElementScrollTop({
            target,
            value: next,
        });
        if (stepNo !== stepTotal) {
            const nextStepNo = stepNo + 1;
            animateSetScrollTop({
                target,
                start,
                end,
                stepNo: nextStepNo,
                stepTotal,
            });
        }
    });
}
           

Side menu bar ejects

After talking about one key to the top, let's move on to the second effect, the ejection effect on the side menu bar. The key to achieving this effect is:

  • When the menu bar pops up, it has the effect of pushing out the main body area of the content, instead of the menu bar directly on top of the content area.
  • When popping up and retracting, the scroll bar must not appear, otherwise it will appear more abrupt.
  • When the menu bar is displayed, the scrolling behavior cannot occur when the mouse wheel is scrolled.

Imagine the process when designing the layout.

When the menu is hidden, the menu is actually out of view.

The TS+TS+Node (Song) 到和 侧 𻡁 射)

When the menu appears, the menu and content area as a whole are pushed a distance to the right.

When I first started designing, I thought about using the flex layout, where the content area on the right occupies the remaining width, and the menu on the left slowly changes from 0px to actual width as it pops up. However, due to the change of width in the process of operation, it is easy to change the layout of internal elements, such as text wrapping.

In the end, it was decided that the content area on the right occupied the width of the entire screen, and the menu on the left side was pasted on the left side of the content area with absolute positioning.

position: absolute;
top: 0;
width: 230px;
height: 100%;
background: #222;
// 保证向左侧再平移一个菜单的身位,正好消失在视野外
transform: translate3d(-100%, 0, 0);
           

The process of menu pop-up is to pan the entire container to the right by 230px, which is the width of the menu, whether this process is animated or transitioned.

For pop-up and retraction, the scroll bar cannot appear, mainly in the process of translating to ensure that the overflow-x direction is hidden.

When displaying the menu bar, the scrolling behavior cannot occur when scrolling the mouse wheel, and you only need to set the overflow of the body to hidden.

In general, the debugging of the style requires a lot of practice to test and adjust, and finally get the desired effect, and the solution is not the only one.

brief summary

The content of this article is not difficult, but there are also some things worth thinking about when it is simple.

Author: Tusi

Link: https://juejin.cn/post/7307065042004410377