点击滚动到任意位置

滚动到任意位置

涉及知识点:

window.scrollTo(x, y)
requestAnimationFrame(()=>{})  //帧动画,优点:按浏览器的刷新频率渲染,更加流畅。特点:类似 setTimeout 只执行一次

原生的写法:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    main {
      width: 100vw;
      height: 100vh;
      overflow: auto;
    }

    main>.content div {
      transition: all 1s ease-in-out;
      height: 200px;
      border: 1px solid #000;
    }

    #box1 {
      background-color: red;
    }

    #box2 {
      background-color: orange;
    }

    #box3 {
      background-color: yellow;
    }

    #box4 {
      background-color: green;
    }

    #box5 {
      background-color: cyan;
    }

    #box6 {
      background-color: blue;
    }

    #box7 {
      background-color: blueviolet;
    }

    #but {
      outline: none;
      position: fixed;
      bottom: 30px;
      right: 30px;
      display: inline-block;
      width: 70px;
      height: 70px;
      line-height: 70px;
      border-radius: 50%;
      border: 1px solid red;
      font-size: 12px;
      text-align: center;
      z-index: 1000;
    }
  </style>
</head>

<body>
  <main>
    <button href="javascript:;" id="but">回到box2</button>
    <div class="content">
      <div id="box1">1</div>
      <div id="box2">2</div>
      <div id="box3">3</div>
      <div id="box4">4</div>
      <div id="box5">5</div>
      <div id="box6">6</div>
      <div id="box7">7</div>
    </div>
  </main>
  <script>
    const but = document.getElementById("but");
    but.onclick = () => {
      const main = document.querySelector("main"); // 获取滚动的元素(带滚动条)
      const toTop = document.getElementById("box2").offsetTop; // 获取需要滚动的高度
      smoothscroll(main, toTop)
    };
    // smoothscroll(需要滚动的元素(默认document), 滚动的最终高度(默认0), 速度(默认1.4))
    const smoothscroll = (ele, toTop = 0, speed = 1.4) => {
      if (!ele) {
        ele = document.documentElement || document.body;
      }
      if (speed <= 1) {
        speed = 1.4
      }
      let timer = null;
      let scrollTo = () => {
        const Dvalue = ele.scrollTop - toTop; // 到目标点的距离,正数:需要向上滚,负数:需要向下滚
        const absDvalue = Math.abs(Dvalue); // 和目标点的距离
        const distance = absDvalue - (absDvalue / speed); // 需要滚动的距离
        const onOff = distance < 1; // 判断是否已到位,距离小于1px是,表示已到位
        if (Dvalue > 0 && !onOff) { // 向上滚
          ele.scrollTop = ele.scrollTop - distance;
          timer = window.requestAnimationFrame(scrollTo);
        }
        if (Dvalue < 0 && !onOff) { // 向下滚
          ele.scrollTop = ele.scrollTop + distance;
          timer = window.requestAnimationFrame(scrollTo);
        }
        if (onOff) {
          console.log("滚到了")
          ele.scrollTop = toTop;
          window.cancelAnimationFrame(timer);
        };
      };
      scrollTo();
    };
  </script>
</body>

</html>

react 版代码:

  num 是需要到达的位置, 数字类型。调用:this.transitionScroll(100)
  下面这段代码,只需要知道,每个索引对应的高度就可以了。

let timer = null;

this.state = {
    onOff: true, //初始值为true,默认可以点击,在滚动的时候变成false,结束滚动再次为true
    active: 0
}

headerBtnClick = (index) => { //点击改变索引
    if(this.state.onOff) {
        this.setState({
            active: index
        }, () => {
            this.scrollFn(index)
        })
    }
}

scrollFn = (index) => {  //每个索引对应的位置
    //this.transitionScroll(...)  根据索引,判断需要滚动到的位置。
}

componentDidMount() {
    window.addEventListener("scroll", () => { //监听是否为滚动状态,在滚动状态不可再次点击
        clearTimeout(timer) //利用防抖技巧,在结束滚动250ms之后恢复可点击状态
        timer = setTimeout(() => {
            this.setState({
                onOff: true
            })
        }, 250)
        this.setState({
            onOff: false
        })
    })
}

componentWillUnmount() {
    if(timer) {
        clearTimeout(timer)
    }
}
//sensitivity: 容错率和灵敏度为 5 时正好
//容错率越小,滚动的位置越精确,太小可能会出现无限回调的错误(主要原因是数字相除再取整,值不再精确)
transitionScroll = (num) => { //判断需要上滚,还是下滚
    const n = window.pageYOffset;
    if(num < n) {
        this.ScrollDown(num)
    }
    if(num > n) {
        this.ScrollUp(num)
    }
}

ScrollDown = (num) => {  //向下滚
    const sensitivity = 5;
    window.requestAnimationFrame(() => {
        const n = window.pageYOffset;
        const Dvalue = Math.abs(num - n);
        if (num > n + sensitivity) {//容错率
            window.scrollTo(0, n + parseInt(Dvalue / sensitivity))//灵敏度
            this.ScrollDown(num)
        }
    })
}
   
ScrollUp = (num) => {  //向上滚
    const sensitivity = 5;
    window.requestAnimationFrame(() => {
        const n = window.pageYOffset;
        const Dvalue = Math.abs(num - n);
        if (num < n - sensitivity) {
            window.scrollTo(0, n - parseInt(Dvalue / sensitivity))
            this.ScrollDown(num)
        }
    })
}
posted @ 2021-03-23 16:22  真的想不出来  阅读(165)  评论(0编辑  收藏  举报