js 回到顶部逻辑实现和elementUI源码解析

news2024/11/18 20:30:01

回到顶部

大家或多或少都会遇到“回到顶部”这样的需求,在此分享这个技术点以及可能遇到的问题。再分析element源码。

回到顶部代码实现

<!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>
            * {
                padding: 0;
                margin: 0;
            }
            .scroll {
                border: 1px solid;
                height: 3600px;
                background-color: red;
            }
            .child {
                margin-top: 200px;
                display: grid;
                place-content: center;
                height: 600px;
                font-size: 20px;
                background-color: green;
            }
            .scrollToTop {
                position: fixed;
                right: 10px;
                bottom: 100px;
                background-color: #fff;
                padding: 8px 16px;
                border-radius: 8px;
                cursor: pointer;
            }
        </style>
    </head>
    <body>
        <div class="scroll">
            <div class="child">
                <div>kinghiee - 回到顶部 - demo</div>
            </div>
        </div>
        <div class="scrollToTop" onclick="backTopClickHandle()">回到顶部</div>
    </body>
    <script>
        let timer = null;

        function  backTopClickHandle() {
            if (this.timer) return;  
            let scrollTop = document.documentElement.scrollTop || document.body.scrollTop; // 获取滚动条高度 (注意点:1)
            this.timer = setInterval(() => {
                const speed = Math.floor(-scrollTop / 8); // 获取-scrollTop / 8 小于等于计算值的最大整数,作为滑动速度
                scrollTop = document.documentElement.scrollTop = document.body.scrollTop = scrollTop + speed; // 赋值

                if(scrollTop <= 0) {
                  clearInterval(this.timer);
                  this.timer = undefined;
                }
            }, 10); // (注意点:2)
        }
    </script>
</html>

请添加图片描述

回到顶部关键技术点在backTopClickHandle函数中

function  backTopClickHandle() {
    if (this.timer) return;  
    let scrollTop = document.documentElement.scrollTop || document.body.scrollTop; // 获取滚动条高度 (注意点:1)
    this.timer = setInterval(() => {
        const speed = Math.floor(-scrollTop / 8); // 获取-scrollTop / 8 小于等于计算值的最大整数,作为滑动速度
        scrollTop = document.documentElement.scrollTop = document.body.scrollTop = scrollTop + speed; // 赋值

        if(scrollTop <= 0) {
          clearInterval(this.timer);
          this.timer = undefined;
        }
    }, 10); // (注意点:2)
}

在该函数中,首先判断当前是否存在定时器,如果有下面步骤不执行。否则获取滚动条高度, 启动定时器,在定时器中计算出当前滑动速度,并计算出当前滚动条高度。
最后如果滚动条高度小于等于0,清除定时器。

在backTopClickHandle函数中有两点需要特别注意

注意点 1:获取scrollTop的位置


let scrollTop = document.documentElement.scrollTop || document.body.scrollTop; // 获取滚动条高度 (注意点:1)

// 该语句一定要在this.timer = setInterval(() => {...}),前获取scrollTop值,不能在setInterval(() => {...})内获取

scrollTop的位置在setInterval(() => {…})内,效果图如下
请添加图片描述

会发现,点击回到顶部,在回到顶部这段时间内,如果再次滑动滚轮,就会出现上图的状态。

注意点 2:定时器时间间隔

定时器时间间隔尽量小,会好在15ms~30ms之间。如果时间间隔过大,短时间内滚动条滑不到顶部,这时如果鼠标有交互,会带来不好的交互体验。

Element 回到顶部源码分析

<template>
  <transition name="el-fade-in">
    <div
      v-if="visible"
      @click.stop="handleClick"
      :style="{
        'right': styleRight,
        'bottom': styleBottom
      }"
      class="el-backtop">
      <slot>
        <el-icon name="caret-top"></el-icon>
      </slot>
    </div>
  </transition>
</template>

<script>
import throttle from 'throttle-debounce/throttle';

const cubic = value => Math.pow(value, 3);
const easeInOutCubic = value => value < 0.5
  ? cubic(value * 2) / 2
  : 1 - cubic((1 - value) * 2) / 2;

export default {
  name: 'ElBacktop',

  props: {
    visibilityHeight: {
      type: Number,
      default: 200
    },
    target: [String],
    right: {
      type: Number,
      default: 40
    },
    bottom: {
      type: Number,
      default: 40
    }
  },

  data() {
    return {
      el: null,
      container: null,
      visible: false
    };
  },

  computed: {
    styleBottom() {
      return `${this.bottom}px`;
    },
    styleRight() {
      return `${this.right}px`;
    }
  },

  mounted() {
    this.init(); // 初始化
    this.throttledScrollHandler = throttle(300, this.onScroll); // 节流
    this.container.addEventListener('scroll', this.throttledScrollHandler);
  },

  methods: {
    init() {
      this.container = document;
      this.el = document.documentElement;
      if (this.target) { // 如果存在target, 获取target dom作为el
        this.el = document.querySelector(this.target);
        if (!this.el) {
          throw new Error(`target is not existed: ${this.target}`);
        }
        this.container = this.el;
      }
    },
    onScroll() { 
      const scrollTop = this.el.scrollTop; // 获取滚动条高度
      this.visible = scrollTop >= this.visibilityHeight; // 判断是否达到可见的标准
    },
    handleClick(e) { // 点击事件处理函数
      this.scrollToTop();
      this.$emit('click', e); // 触发事件
    },
    scrollToTop() {
      const el = this.el; // 获取元素
      const beginTime = Date.now();// 获取当前时间戳
      const beginValue = el.scrollTop;
      const rAF = window.requestAnimationFrame || (func => setTimeout(func, 16));
      const frameFunc = () => {
        const progress = (Date.now() - beginTime) / 500;
        if (progress < 1) { // 和0.5s做对比,小于0.5秒执行下面步骤
          el.scrollTop = beginValue * (1 - easeInOutCubic(progress));
          rAF(frameFunc);
        } else {
          el.scrollTop = 0;
        }
      };
      rAF(frameFunc);
    }
  },

  beforeDestroy() { // 销毁时,移除监听
    this.container.removeEventListener('scroll', this.throttledScrollHandler);
  }
};
</script>

可以看到,除了对scroll监听加了节流外,其余的思想大致相同。主要分析scrollToTop函数

scrollToTop函数分析

scrollToTop() {
    const el = this.el; // 获取元素
    const beginTime = Date.now(); // 获取当前时间戳
    const beginValue = el.scrollTop; // 获取当前滚动条位置
    const rAF = window.requestAnimationFrame || (func => setTimeout(func, 16)); // 注意点 1
    const frameFunc = () => {
      const progress = (Date.now() - beginTime) / 500;
      if (progress < 1) { // 和0.5s做对比,小于0.5秒执行下面步骤
        el.scrollTop = beginValue * (1 - easeInOutCubic(progress));
        rAF(frameFunc); // 循环
      } else {
        el.scrollTop = 0;
      }
    };
    rAF(frameFunc);
}

注意点1

const rAF = window.requestAnimationFrame || (func => setTimeout(func, 16));

调用循环时,首先判断了当前环境是否存在requestAnimationFrame,如果不存在,则使用性能稍微差点的setTimeout。

优先使用requestAnimationFrame的主要优势时因为

1.经过浏览器优化,动画更流畅
2.窗口没激活时,动画将停止,省计算资源
3.更省电,尤其是对移动终端

requestAnimationFrame最大的优势是

由系统来决定回调函数的执行时机。具体一点讲,如果屏幕刷新率是60Hz,那么回调函数就每16.7ms被执行一次,如果刷新率是75Hz,那么这个时间间隔就变成了1000/75=13.3ms,换句话说就是,requestAnimationFrame的步伐跟着系统的刷新步伐走。它能保证回调函数在屏幕每一次的刷新间隔中只被执行一次,这样就不会引起丢帧现象,也不会导致动画出现卡顿的问题.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/11341.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于PHP+MySQL的个人博客系统毕设

随着时代和网络的发展,人们越来越希望通过多种模式来展示自己。于是个人博客就出现了,它可以更好的让人们来记录自己的工作和学习方式。博客不仅仅可以让自己抒发个人感情,还可以展示自己真实的生活,从而建立起一种友好的交友平台。 PHP个人博客系统毕设系统分为前台和后台两部…

Python每日一练 02

Python每日一练 02 文章目录Python每日一练 02一、对象二、对象属性三、赋值一、对象 Python中所有数字、序列、集合、映射、类、实例、异常、模块、类与类的实例、函数、方法、布尔值、空值等都被称为对象。 二、对象属性 每个对象都有3个基本属性&#xff1a; 类型(type)…

Flink-源算子Source(获取数据源)的使用

5.1 整体介绍 获取执行环境读取数据源定义基于数据的转换操作定义计算结果的输出位置触发程序执行 5.2 创建集成环境 5.2.1 获取执行环境 批处理getExecutionEnvironment 提交命令行设置 bin/flink run -Dexecution.runtime-modeBATCH ...代码 StreamExecutionEnvironme…

pip Command Not Found – Mac 和 Linux 错误被解决

使用Python时&#xff0c;可能需要安装和使用某些软件包。有一个命令可用于’pip‘ 使用pip&#xff0c;您可以安装、升级和卸载各种Python包。在本文中&#xff0c;您将学习如何使用它&#xff0c;以及如何处理pip错误。 如何使用 pip Pip是一个可以在Linux或Mac命令行上使用…

HTTP(http+抓包Fiddler+协议格式+请求+响应)

目录 &#x1f984;1. 了解HTTP &#x1f984;2. 抓包 &#x1f984;3. http协议格式 &#x1f432;3.1 完整的HTTP请求格式 &#x1f432;3.2 完整的HTTP响应的格式 HTTP请求 &#x1f984;4. 认识URL &#x1f984;5. http中的"方法" &#x1f432;5.1…

智能与工程学院2022级计算机朱元华

智能与工程学院 《高级语言程序设计》 小组学习任务书 第 1 次 专业年级&#xff1a; 2022级计算机 指导教师&#xff1a; 朱元华 2022-2023学年 第 1 学期 一、任务 XXX信息管理系统的需求分析和功能设计 二、分组形式 学生自由组合&#xff0c;5-8人为一组&#xff0c;根据…

Tuxera NTFS2023Mac读写ntfs磁盘工具

Tuxera Ntfs for mac2023是Mac中专用于读写外置存储的工具&#xff0c;具有强大的磁盘管理和修复功能&#xff0c;它在Mac上完全读写NTFS格式硬盘&#xff0c;快捷的访问、编辑、存储和传输文件。能够在 Mac 上读写 Windows NTFS 文件系统。Tuxera NTFS 实现在Mac OS X系统读写…

【Spring】——6、按照条件向Spring容器中注册bean

&#x1f4eb;作者简介&#xff1a;zhz小白 公众号&#xff1a;小白的Java进阶之路 专业技能&#xff1a; 1、Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理 2、熟悉Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理&#xff0c;具备⼀定的线…

静态时序分析简明教程(六)]时钟组与其他时钟特性

生成时钟的sdc约束方法一、写在前面1.1 快速导航链接二、时钟组2.1 引入时钟组2.2 set_clock_group2.2.1 -name2.2.2 -group clock_list2.2.3 -logically_exclusive|-physically_exclusive|-asynchronous2.2.4 -allow_path2.2.5 -comment三、其他时钟特性3.1 过渡时间3.2 偏移与…

【Linux】进程间通信——管道

目录 一、概念 二、管道函数 1.popen函数 2.pclose函数 3.文件函数 三、管道的操作 1.管道的分类 无名管道 有名管道 管道的特点 四、管道的实现 操作系统对进程之间相互保护 两个进程之间相互通信 前言&#xff1a; 进程间通信的方法/IPC机制都有哪些&#xff1a; …

求二进制中1的个数的三种方法

求二进制中的1的个数 文章目录第一种方法&#xff1a;模2除2第二种方法&#xff1a;利用操作符右移后与1第三种方法&#xff1a;该数与上比它小1的数&#xff08;最优的方法&#xff09;第一种方法&#xff1a;模2除2 首先明白如何得到一个数的十进制的每一位&#xff1f; 以1…

PHP代码审计入门-DVWA靶场CSRF篇

0x00 写在前面 从零学习php&#xff0c;最终目的实现代码审计入门&#xff0c;软件采用sublime text&#xff0c;环境使用phpstudy搭建&#xff0c;数据库是navicat&#xff0c;需要有基本的前端基础、简单的phpmysql后端基础、渗透知识和漏洞原理&#xff0c;文章跟随流沙前…

bizlog通用操作日志组件(使用篇)

引言 如上图所示&#xff0c;产品的新需求&#xff0c;需要将操作人在系统中具体编辑操作的变更内容记录下来。 按正常思路来说&#xff0c;无非就是将修改前后的对象字段逐个比较&#xff0c;再拼接为详细的操作描述记录到操作日志表中。如果是一个模块的需求&#xff0c;单独…

用HTML+CSS做一个学生抗疫感动专题网页设计作业网页

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

springboot如何修改thymeleaf中默认的页面路径及默认的后缀名呢?

转自: springboot如何修改thymeleaf中默认的页面路径及默认的后缀名呢? 下文讲述springboot修改thymefleaf修改页面默认路径及后缀名的方法分享&#xff0c;如下所示: 实现思路:只需在配置文件中修改相应的配置即可&#xff0c;如&#xff1a;application.yaml spring:thym…

MySQL单表查询操作详解,不做CRUD程序员

在我们对数据进行操作时&#xff0c;查询无疑是至关重要的&#xff0c;查询操作灵活多变&#xff0c;我们可以根据开发的需求&#xff0c;设计高效的查询操作&#xff0c;把数据库中存储的数据展示给用户。 文章目录前言1. 基础查询1.1 基础查询语法1.2 基础查询练习2. 条件查询…

大数据路线

一、概念部分 1.1 大数据、数仓、数据湖、中台的概念 区别数仓数据湖使用场景批处理&#xff0c;BI&#xff0c;数据可视化机器学习、预测分析、数据分析Schema写入型读取型数据源类型OLTP为主的结构化数据loT&#xff0c;日志&#xff0c;各个端等结构非结构均可性价比需要快…

牛客刷题总结——Python入门08:面向对象、正则表达式

&#x1f935;‍♂️ 个人主页: 北极的三哈 个人主页 &#x1f468;‍&#x1f4bb; 作者简介&#xff1a;Python领域优质创作者。 &#x1f4d2; 系列专栏&#xff1a;《牛客题库-Python篇》 &#x1f310;推荐《牛客网》——找工作神器|笔试题库|面试经验|实习经验内推&am…

Design A Youtube

title: Notes of System Design No.05 — Design a Youtube description: ‘Design a Youtube’ date: 2022-05-14 13:45:37 tags: 系统设计 categories: 系统设计 01. Funtional Requirements 02. Non Functional Requirements 03.Assumption 04 API 05 High Level Design 上…

05 MSYS2中安装树莓派32位和64位ARM交叉编译工具

作者将狼才鲸创建日期2022-11-14 Gitee源码和工程地址&#xff1a;才鲸嵌入式 / 开源安防摄像机&#xff08;嵌入式软件&#xff09;CSDN文章地址&#xff1a;项目介绍&#xff1a;开源安防摄像机&#xff08;嵌入式软件&#xff09; 4.3 MSYS2中安装32位和64位ARM交叉编译工具…