项目中的滚动回顶部

2024/06/02

一、跳转页面滚动回顶部

当在项目中一个长页面滚动到一定位置后,再跳转(a标签、Vue Router 、React Router)其他长页面时,会导致新加载的页面直接展示为前一个页面滚动的位置,出现这个问题的本质是浏览器的滚动位置缓存机制

解决方式:手动重置滚动位置

1.React

新增组件 /src/components/ScrollToTop.jsx:

import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

function ScrollToTop() {
  const { pathname } = useLocation();

  useEffect(() => {
    // 页面跳转时重置滚动位置
    window.scrollTo(0, 0);
  }, [pathname]);

  return null;
}

在App.jsx中使用:

import ScrollToTop from "./components/ScrollToTop.jsx";
...
<App>
  <ScrollToTop />
  <Routes>...</Routes>
</App>

2.Vue

在 Vue Router 初始化中配置 scrollBehavior:

const router = new VueRouter({
  routes: [...],
  scrollBehavior(to, from, savedPosition) {
    return { x: 0, y: 0 }; // 每次跳转回顶部
  }
});

二、点击滚动回顶部

长页面添加滚动回顶部按钮,避免手动操作,提升用户体验。

1.html示例:

通过示例的理解可以将逻辑改写到React、Vue中

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .container {
        position: relative;
        width: 100%;
        height: 100%;
      }
      main {
        width: 100%;
        height: 2000px;
      }
      #back-to-top {
        display: none;
        width: 32px;
        height: 32px;
        cursor: pointer;
        position: fixed;
        bottom: 20px;
        right: 10px;
        z-index: 999;
        -webkit-animation: floatUp 2s infinite ease-in-out;
        animation: floatUp 2s infinite ease-in-out;
      }

      #back-to-top.show {
        display: block;
      }

      @keyframes floatUp {
        0% {
          -webkit-transform: translateY(-10px);
          transform: translateY(-10px);
        }
        50% {
          -webkit-transform: translateY(10px);
          transform: translateY(10px);
        }
        100% {
          -webkit-transform: translateY(-10px);
          transform: translateY(-10px);
        }
      }
    </style>
  </head>
  <body>
    <div class="container">
      <main>内容</main>
      <div id="back-to-top">
        <svg
          t="1749010463650"
          class="icon"
          viewBox="0 0 1024 1024"
          version="1.1"
          xmlns="http://www.w3.org/2000/svg"
          p-id="11007"
          width="32"
          height="32"
        >
          <path
            d="M116.096 596.08h90.87v231.988h42.486V596.08h90.87V553.6H116.095v42.48z m458.537-42.48H462.085c-13.588 0-27.182 5.187-37.545 15.55-10.373 10.367-15.554 23.961-15.554 37.555v168.248c0 13.594 5.181 27.187 15.554 37.555 10.368 10.368 23.962 15.555 37.545 15.555h112.548c13.588 0 27.187-5.187 37.555-15.555s15.55-23.961 15.55-37.555V606.71c0-13.599-5.182-27.187-15.55-37.56-10.368-10.363-23.961-15.55-37.555-15.55z m10.63 221.353c0 2.012-0.549 4.946-3.109 7.516-2.57 2.57-5.509 3.108-7.516 3.108H462.085c-2.007 0-4.936-0.538-7.506-3.108s-3.113-5.504-3.113-7.516V606.71c0-2.012 0.543-4.946 3.113-7.516s5.5-3.113 7.506-3.113h112.548c2.007 0 4.946 0.542 7.506 3.113 2.57 2.57 3.118 5.504 3.118 7.516v168.243h0.005z m307.086-205.804c-10.368-10.357-23.966-15.549-37.555-15.549H709.14v274.463h42.481V707.686h103.173c13.594 0 27.187-5.186 37.555-15.554s15.555-23.962 15.555-37.55v-47.877c0-13.594-5.181-27.182-15.555-37.556z m-26.93 85.433c0 2.007-0.538 4.94-3.109 7.51s-5.509 3.109-7.51 3.109H751.63v-69.12H854.8c2.007 0 4.941 0.542 7.511 3.113s3.108 5.504 3.108 7.516v47.872zM315.97 370.068c55.05-49.602 185.703-167.31 189.758-170.89 5.146-4.541 11.387-3.978 15.304-0.384 2.744 2.52 138.49 124.749 191.227 172.237 8.304 6.994 6.103 20.741-4.317 20.741s-377.85 0.097-384.43 0-14.95-12.416-7.541-21.704z"
            p-id="11008"
            fill="#1b82f1"
          ></path>
        </svg>
      </div>
    </div>
    <script>
      window.addEventListener("scroll", function () {
        // 展示返回顶部按钮
        if (window.scrollY > 400) {
          document.querySelector("#back-to-top").classList.add("show");
        } else {
          document.querySelector("#back-to-top").classList.remove("show");
        }
      });

      // 添加点击事件
      document
        .querySelector("#back-to-top")
        .addEventListener("click", function () {
          window.scrollTo({
            top: 0,
            behavior: "smooth",
          });
        });
    </script>
  </body>
</html>

2.React

新增组件 /src/components/BackToTop.jsx:

import { useState, useEffect } from "react";

export default function BackToTop() {
  const [visible, setVisible] = useState(false);

  useEffect(() => {
    const handleScroll = () => {
      setVisible(window.scrollY > 600);
    };

    window.addEventListener("scroll", handleScroll);
    return () => window.removeEventListener("scroll", handleScroll);
  }, []);

  const scrollToTop = () => {
    window.scrollTo({ top: 0, behavior: "smooth" });
  };

  return visible ? (
    <a id="back-to-top" onClick={scrollToTop}>
      <svg
        t="1749738298463"
        class="icon"
        viewBox="0 0 1024 1024"
        version="1.1"
        xmlns="http://www.w3.org/2000/svg"
        p-id="4276"
        width="32"
        height="32"
      >
        <path
          d="M533.333333 512L341.333333 704l29.866667 29.866667 162.133333-162.133334 162.133334 162.133334 29.866666-29.866667-192-192z m0-256L341.333333 448l29.866667 29.866667 162.133333-162.133334 162.133334 162.133334 29.866666-29.866667L533.333333 256z"
          fill="#ffffff"
          p-id="4277"
        ></path>
      </svg>
    </a>
  ) : null;
}

在style.css中添加按钮样式,该文件记得在main.jsx中引入:

/* back to top button*/
#back-to-top {
  font-size: 18px;
  font-weight: bold;
  width: 40px;
  height: 40px;
  line-height: 42px;
  text-align: center;
  background-color: rgba(0, 0, 0, 0.2);
  border-radius: 3px;
  color: #fff;
  position: fixed;
  bottom: 20px;
  right: 20px;
  z-index: 999;
  cursor: pointer;
  -webkit-box-shadow: 0px 20px 30px 0px rgba(26, 8, 119, 0.24);
  -moz-box-shadow: 0px 20px 30px 0px rgba(26, 8, 119, 0.24);
  box-shadow: 0px 20px 30px 0px rgba(26, 8, 119, 0.24);
  animation: fadeInUp 0.5s linear;
}
@keyframes fadeInUp {
  from {
    opacity: 0;
    -webkit-transform: translate3d(0, 100%, 0);
    transform: translate3d(0, 100%, 0);
  }

  to {
    opacity: 1;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

在App.jsx中使用:

import BackToTop from "./components/BackToTop.jsx";
...
<App>
  <Routes>...</Routes>
  <BackToTop />
</App>