javascript 平滑滚动到部分

ttcibm8c  于 11个月前  发布在  Java
关注(0)|答案(2)|浏览(132)

我想用scrollTo实现平滑滚动。如果我使用scrollIntoView({ behavior: 'smooth' }选项,我无法控制速度。因此我使用performance.now()requestAnimationFrame的组合来实现效果。
它在鼠标轮事件上工作得很好,问题是我有三个按钮,当我点击时它应该转到相对部分。但是它对按钮不起作用。
鼠标滚轮事件使用window.scrollTo(0, startY + distance * progress);,而按钮使用window.scrollTo(0, manualPosition * progress);,其中manualPositionlis[index].getBoundingClientRect().top的部分位置。
请尝试运行代码并点击按钮,或使用鼠标滚轮查看效果。

(function () {
    const lis = document.querySelectorAll('.section'),
        btnsNav = document.querySelectorAll('.button'),
        speed = 500;
    let currentSection = 0,
        isScrolling = false;

    function smoothScrollTo(time, direction, mode = 'auto', index = 1) {
        if (isScrolling) {
            return;
        }

        isScrolling = true;

        const start = performance.now(),
            startY = window.scrollY,
            distance =
                direction === 'up' ? -window.innerHeight : window.innerHeight;

        function animateScroll() {
            const now = performance.now(),
                elapsed = now - start,
                progress = elapsed / time,
                manualPosition = lis[index].getBoundingClientRect().top;

            if (mode === 'auto') {
                window.scrollTo(0, startY + distance * progress);
            } else if (mode === 'manual') {
                window.scrollTo(0, manualPosition * progress);
            }

            if (elapsed < time) {
                requestAnimationFrame(animateScroll);
            } else {
                isScrolling = false;
            }
        }

        requestAnimationFrame(animateScroll);

        if (direction === 'down') {
            currentSection++;
        } else if (direction === 'up') {
            currentSection--;
        }
    }

    document.addEventListener('wheel', function (event) {
        if (event.deltaY > 0 && currentSection < lis.length - 1) {
            smoothScrollTo(speed, 'down');
        } else if (event.deltaY < 0 && currentSection > 0) {
            smoothScrollTo(speed, 'up');
        }
    });

    for (let index = 0; index < btnsNav.length; index++) {
        btnsNav[index].addEventListener('click', function () {
            smoothScrollTo(speed, 'down', 'manual', index);
        });
    }
})();
body {
    margin: 0;
}

#div {
    position: fixed;
    top: 0;
    left: 0;
    display: flex;
    column-gap: 10px;
}

.section {
    height: 100vh;
    width: 100%;
}

.section:nth-of-type(1) {
    background-color: red;
}

.section:nth-of-type(2) {
    background-color: yellow;
}

.section:nth-of-type(3) {
    background-color: green;
}
<div id="div">
  <button class="button" type="button">Go to 1</button>
  <button class="button" type="button">Go to 2</button>
  <button class="button" type="button">Go to 3</button>
</div>

<section class="section"></section>
<section class="section"></section>
<section class="section"></section>
9gm1akwq

9gm1akwq1#

代码中的问题

我在你的代码中发现了两个问题,

  1. btnsNav变量:您已经将.section存储在此变量中,并将事件侦听器附加在那里,这是不正确的。将其更改为.button
btnsNav = document.querySelectorAll('.button'),

字符串
1.在for循环中调用smoothScrollTo函数时,将manual作为mode传递。将mode作为auto传递似乎可以解决这个问题。

smoothScrollTo(speed, 'down', 'auto', index);

注意:还有一些逻辑上需要更多的变化,我会离开你来修复,因为我已经能够向下进入所需的页面,但按钮点击不带我们向上到页面.干杯!

**提示:**在for循环中传递给smoothScrollTo的方向总是向下的。而不是使用'up'/'down'我会使用currentSection和正在传递的索引来标识是向上滚动还是向下滚动。请忽略逻辑;)

修复代码:

(function () {
    const lis = document.querySelectorAll('.button'),
        btnsNav = document.querySelectorAll('.button'),
        speed = 500;
    let currentSection = 0,
        isScrolling = false;

    function smoothScrollTo(time, direction, mode = 'auto', index = 1) {
        if (isScrolling) {
            return;
        }

        isScrolling = true;

        const start = performance.now(),
            startY = window.scrollY,
            distance =
                direction === 'up' ? -window.innerHeight : window.innerHeight;

        function animateScroll() {
            const now = performance.now(),
                elapsed = now - start,
                progress = elapsed / time,
                manualPosition = lis[index].getBoundingClientRect().top;

            if (mode === 'auto') {
                window.scrollTo(0, startY + distance * progress);
            } else if (mode === 'manual') {
                window.scrollTo(0, manualPosition * progress);
            }

            if (elapsed < time) {
                requestAnimationFrame(animateScroll);
            } else {
                isScrolling = false;
            }
        }

        requestAnimationFrame(animateScroll);

        if (direction === 'down') {
            currentSection++;
        } else if (direction === 'up') {
            currentSection--;
        }
    }

    document.addEventListener('wheel', function (event) {
        if (event.deltaY > 0 && currentSection < lis.length - 1) {
            smoothScrollTo(speed, 'down');
        } else if (event.deltaY < 0 && currentSection > 0) {
            smoothScrollTo(speed, 'up');
        }
    });

    for (let index = 0; index < btnsNav.length; index++) {
        btnsNav[index].addEventListener('click', function () {
            smoothScrollTo(speed, 'down', 'auto', index);
        });
    }
})();
body {
    margin: 0;
}

#div {
    position: fixed;
    top: 0;
    left: 0;
    display: flex;
    column-gap: 10px;
}

.section {
    height: 100vh;
    width: 100%;
}

.section:nth-of-type(1) {
    background-color: red;
}

.section:nth-of-type(2) {
    background-color: yellow;
}

.section:nth-of-type(3) {
    background-color: green;
}
<div id="div">
  <button class="button" type="button">Go to 1</button>
  <button class="button" type="button">Go to 2</button>
  <button class="button" type="button">Go to 3</button>
</div>

<section class="section"></section>
<section class="section"></section>
<section class="section"></section>
lskq00tm

lskq00tm2#

实际上,你可以在没有任何JavaScript的情况下实现这种效果-这是可能的HTML + CSS。
有三个步骤:
1.将scroll-behavior: smooth应用于文档根(即<html>元素):

:root {
  scroll-behavior: smooth;
}

字符串
1.用hashfragment链接包围每个按钮:

<a href="#section2"><button class="button" type="button">Go to 2</button></a>


1.将对应的id添加到每个.section,以指示散列片段链接的目的地:

<section id="section2" class="section"></section>

示例:

:root {
  scroll-behavior: smooth;
}

body {
    margin: 0;
}

#div {
    position: fixed;
    top: 0;
    left: 0;
    display: flex;
    column-gap: 10px;
}

.section {
    height: 100vh;
    width: 100%;
}

.section:nth-of-type(1) {
    background-color: red;
}

.section:nth-of-type(2) {
    background-color: yellow;
}

.section:nth-of-type(3) {
    background-color: green;
}
<div id="div">
  <a href="#section1"><button class="button" type="button">Go to 1</button></a>
  <a href="#section2"><button class="button" type="button">Go to 2</button></a>
  <a href="#section3"><button class="button" type="button">Go to 3</button></a>
</div>

<section id="section1" class="section"></section>
<section id="section2" class="section"></section>
<section id="section3" class="section"></section>

相关问题