Vue.js实战之通过监听滚动事件实现动态锚点


Posted in Javascript onApril 04, 2017

前言

前几天工作中在做项目的时候,需要实现一个动态锚点的效果,具体效果如下:

Vue.js实战之通过监听滚动事件实现动态锚点

如果是传统项目,这个效果就非常简单。但是放到 Vue 中,就有两大难题:

      1. 在没有 jQuery 的 animate() 方法的情况下,如何实现平滑滚动?

      2. 如何监听页面滚动事件?

在浏览了大量文章、进行多次尝试之后,终于解决了这些问题

期间主要涉及到了 setTimeout 的递归用法,和 Vue 生命周期中的 mounted

一、锚点实现

在实现平滑滚动之前,得先确保基本的锚点功能

如果没有其他要求,直接用 <a href="#id" rel="external nofollow" > 是最简单粗暴的办法

但是为了满足后续的要求,必须另外想办法 

首先在父组件(表单)中添加 class="d_jump" 作为钩子

Vue.js实战之通过监听滚动事件实现动态锚点

然后在子组件中添加一个 jump 方法

Vue.js实战之通过监听滚动事件实现动态锚点

jump (index) {
 let jump = document.querySelectorAll('.d_jump')
 // 获取需要滚动的距离
 let total = jump[index].offsetTop
 // Chrome
 document.body.scrollTop = total
 // Firefox
 document.documentElement.scrollTop = total
 // Safari
 window.pageYOffset = total
},

通过 offsetTop 获取对象到窗体顶部的距离,然后赋值给 scrollTop,就能实现锚点的功能

需要注意的是:各浏览器下获取 scrollTop 有所差异

      Chrome: document.body.scrollTop

      Firefox: document.documentElement.scrollTop

      Safari: window.pageYOffset

二、平滑滚动

仅仅是锚点是不够的,这样的跳转十分突兀

为了更好的用户体验 ,需要将滚动的过程展现出来

如果有 jQuery 实现平滑滚动就非常简单:

$('html body').animate({scrollTop: total}, 500);

可惜没如果~~

在看了好些文章之后,有了一个大概的开发思路:

将总距离分成很多小段,然后每隔一段时间跳一段

只要每段的时间足够短,频次足够多,在视觉上就是正常的平滑滚动了

// 平滑滚动,时长500ms,每10ms一跳,共50跳
// 获取当前滚动条与窗体顶部的距离
let distance = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
// 计算每一小段的距离
let step = total / 50
(function smoothDown () {
 if (distance < total) {
 distance += step
// 移动一小段
 document.body.scrollTop = distance
 document.documentElement.scrollTop = distance
 window.pageYOffset = distance

// 设定每一次跳动的时间间隔为10ms
 setTimeout(smoothDown, 10)
 } else {

// 限制滚动停止时的距离
 document.body.scrollTop = total
 document.documentElement.scrollTop = total
 window.pageYOffset = total
 }
})()

实际情况下,得考虑向上滚动和向下滚动两种情况,完整的代码为:

jump (index) {
 // 用 class="d_jump" 添加锚点
 let jump = document.querySelectorAll('.d_jump')
 let total = jump[index].offsetTop
 let distance = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
 // 平滑滚动,时长500ms,每10ms一跳,共50跳
 let step = total / 50
 if (total > distance) {
 smoothDown()
 } else {
 let newTotal = distance - total
 step = newTotal / 50
 smoothUp()
 }
 function smoothDown () {
 if (distance < total) {
 distance += step


 document.body.scrollTop = distance
 document.documentElement.scrollTop = distance
 window.pageYOffset = distance
 setTimeout(smoothDown, 10)
 } else {
 document.body.scrollTop = total
 document.documentElement.scrollTop = total
 window.pageYOffset = total
 }
 }
 function smoothUp () {
 if (distance > total) {
 distance -= step



 document.body.scrollTop = distance
 document.documentElement.scrollTop = distance
 window.pageYOffset = distance
 setTimeout(smoothUp, 10)
 } else {
 document.body.scrollTop = total
 document.documentElement.scrollTop = total
 window.pageYOffset = total
 }
 } 
 }

三、修改锚点状态

在上面展示的效果中,当页面滚动的时候,锚点的激活状态会有相应的改变

其实这个效果并不难,只需要监听页面滚动事件,然后根据滚动条的距离修改锚点状态就可以了

但是在 Vue 中,应该在什么地方监听滚动事件呢?

mounted: function () {
 this.$nextTick(function () {
 window.addEventListener('scroll', this.onScroll)
 })
 },
 methods: {
 onScroll () {
 let scrolled = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
  // 586、1063分别为第二个和第三个锚点对应的距离
 if (scrolled >= 1063) {
 this.steps.active = 2
 } else if (scrolled < 1063 && scrolled >= 586) {
 this.steps.active = 1
 } else {
 this.steps.active = 0
 }
 }
 }

上面的代码中,我先写了一个修改锚点状态的方法 onScroll,然后在 mounted 中监听 scroll 事件,并执行 onScroll 方法

mounted 是 Vue 生命周期中的一个状态,在这个状态下,$el (vue 实例的根元素)已经创建完毕,但还没有加载数据

从结果上看,也可以在 created 状态下监听 scroll 事件

如果对 mounted 和 created 还不够了解,可以参考官方文档·生命周期图示

总结

上面只能算是一个应急之法,而且这种操作 DOM 的方法,并不符合 Vue 的设计理念,待我研究出更合理更高效的办法之后,再发出来分享~

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
基于JQuery的访问WebService的代码(可访问Java[Xfire])
Nov 19 Javascript
javascript学习笔记(十九) 节点的操作实现代码
Jun 20 Javascript
js获取浏览器基本信息大全
Nov 27 Javascript
JavaScript实现点击自动选择TextArea文本的方法
Jul 02 Javascript
js闭包引起的事件注册问题介绍
Mar 29 Javascript
详解设置Webstorm 利用babel将ES6自动转码成ES5
Dec 20 Javascript
ExtJs整合Echarts的示例代码
Feb 27 Javascript
js jquery 获取某一元素到浏览器顶端的距离实现方法
Sep 05 jQuery
js实现网页同时进行多个倒计时功能
Feb 25 Javascript
Webpack中loader打包各种文件的方法实例
Sep 03 Javascript
浅谈React中组件逻辑复用的那些事儿
May 21 Javascript
详解jQuery的核心函数和事件处理
Feb 18 jQuery
Vue.js实战之使用Vuex + axios发送请求详解
Apr 04 #Javascript
Vue.js实战之组件的进阶
Apr 04 #Javascript
关于axios返回空对象的问题解决
Apr 04 #Javascript
jQuery自定义图片上传插件实例代码
Apr 04 #jQuery
jQuery使用unlock.js插件实现滑动解锁
Apr 04 #jQuery
利用jquery正则表达式在页面验证url网址输入是否正确
Apr 04 #jQuery
JavaScript实现实时更新系统时间的实例代码
Apr 04 #Javascript
You might like
php面向对象全攻略 (七) 继承性
2009/09/30 PHP
php数组函数序列之array_key_exists() - 查找数组键名是否存在
2011/10/29 PHP
Ubuntu中启用php的mail()函数并解决发送邮件速度慢问题
2015/03/27 PHP
PHP中的use关键字及文件的加载详解
2016/11/28 PHP
浅谈htmlentities 、htmlspecialchars、addslashes的使用方法
2016/12/09 PHP
PHP 二维关联数组根据其中一个字段排序(推荐)
2017/04/04 PHP
PHP设计模式之 策略模式Strategy详解【对象行为型】
2020/05/01 PHP
flexigrid 参数说明
2010/11/23 Javascript
编写自己的jQuery插件简单实现代码
2011/04/19 Javascript
多个js与css文件的合并方法详细说明
2012/12/26 Javascript
javascript学习(一)构建自己的JS库
2013/01/02 Javascript
第一次接触神奇的Bootstrap表单
2016/07/27 Javascript
JS中闭包的经典用法小结(2则示例)
2016/12/28 Javascript
使用JavaScript为一张图片设置备选路径的方法
2017/01/04 Javascript
深入理解Javascript中的作用域链和闭包
2017/04/25 Javascript
jquery DataTable实现前后台动态分页
2017/06/17 jQuery
打造通用的匀速运动框架(实例讲解)
2017/10/17 Javascript
AngularJS实现的根据数量与单价计算总价功能示例
2017/12/26 Javascript
如何使用VuePress搭建一个类型element ui文档
2019/02/14 Javascript
JS控制下拉列表左右选择实例代码
2020/05/08 Javascript
linux服务器快速卸载安装node环境(简单上手)
2021/02/22 Javascript
[52:31]VP vs Serenity 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/17 DOTA
python读写二进制文件的方法
2015/05/09 Python
Python 多线程抓取图片效率对比
2016/02/27 Python
python+mongodb数据抓取详细介绍
2017/10/25 Python
python scatter散点图用循环分类法加图例
2019/03/19 Python
python中时间、日期、时间戳的转换的实现方法
2019/07/06 Python
Django Admin后台模型列表页面如何添加自定义操作按钮
2020/11/11 Python
苹果美国官方商城:Apple美国
2016/08/24 全球购物
见习期自我鉴定
2014/01/31 职场文书
大学生素质拓展活动方案
2014/02/11 职场文书
信息管理与信息系统专业求职信
2014/06/21 职场文书
关于晚自习早退的检讨书
2014/09/13 职场文书
乡镇党员干部群众路线对照检查材料思想汇报
2014/09/28 职场文书
初中毕业感言300字
2015/07/31 职场文书
对讲机知识
2022/04/07 无线电