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 相关文章推荐
JavaScript中的Primitive对象封装介绍
Dec 31 Javascript
javascript实现点击按钮弹出一个可关闭层窗口同时网页背景变灰的方法
May 13 Javascript
微信小程序 textarea 组件详解及简单实例
Jan 10 Javascript
基于jQuery制作小图标上下滑动特效
Jan 18 Javascript
微信小程序中input标签详解及简单实例
May 18 Javascript
js学习总结_选项卡封装(实例讲解)
Jul 13 Javascript
AngularJS创建一个上传照片的指令实例代码
Feb 24 Javascript
微信小程序实现上传图片裁剪图片过程解析
Aug 22 Javascript
element 中 el-menu 组件的无限极循环思路代码详解
Apr 26 Javascript
分享8个JavaScript库可更好地处理本地存储
Oct 12 Javascript
vue实现动态表格提交参数动态生成控件的操作
Nov 09 Javascript
vue中使用mockjs配置和使用方式
Apr 06 Vue.js
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
全国FM电台频率大全 - 9 上海市
2020/03/11 无线电
PHP+MYSQL开发工具及资源收藏
2007/01/02 PHP
PHP处理excel cvs表格的方法实例介绍
2013/05/13 PHP
php加密之discuz内容经典加密方式实例详解
2017/02/04 PHP
php ajax confirm 删除实例详解
2019/03/06 PHP
ASP.NET中基于JQUERY的高性能的TreeView补充
2011/02/23 Javascript
基于JavaScript实现继承机制之原型链(prototype chaining)的详解
2013/05/07 Javascript
js实现文本框中输入文字页面中div层同步获取文本框内容的方法
2015/03/03 Javascript
JQuery EasyUI学习教程之datagrid 添加、修改、删除操作
2016/07/09 Javascript
bootstrap-datetimepicker实现只显示到日期的方法
2016/11/25 Javascript
浅谈 Vue v-model指令的实现原理
2017/06/08 Javascript
vue 之 .sync 修饰符示例详解
2018/04/21 Javascript
JS/HTML5游戏常用算法之路径搜索算法 随机迷宫算法详解【普里姆算法】
2018/12/13 Javascript
vue项目中全局引入1个.scss文件的问题解决
2019/08/01 Javascript
微信浏览器下拉黑边解决方案 wScroollFix
2020/01/21 Javascript
python网络编程之TCP通信实例和socketserver框架使用例子
2014/04/25 Python
Python随机读取文件实现实例
2017/05/25 Python
python 统计一个列表当中的每一个元素出现了多少次的方法
2018/11/14 Python
python如何实现数据的线性拟合
2019/07/19 Python
Python pandas.DataFrame 找出有空值的行
2019/09/09 Python
python字符串格式化方式解析
2019/10/19 Python
用python解压分析jar包实例
2020/01/16 Python
Python API len函数操作过程解析
2020/03/05 Python
将不规则的Python多维数组拉平到一维的方法实现
2021/01/11 Python
Python实现给PDF添加水印的方法
2021/01/25 Python
Python中对象的比较操作==和is区别详析
2021/02/12 Python
针对HTML5的Web Worker使用攻略
2015/07/12 HTML / CSS
AmazeUI 手机版页面的顶部导航条Header与侧边导航栏offCanvas的示例代码
2020/08/19 HTML / CSS
武汉瑞得软件笔试题
2015/10/27 面试题
中间件的定义
2016/08/09 面试题
计算机系毕业生推荐信
2013/11/06 职场文书
俄语翻译实习生的自我评价分享
2013/11/06 职场文书
《只有一个地球》教学反思
2014/02/14 职场文书
2015年司机工作总结
2015/04/23 职场文书
党支部季度考核意见
2015/06/02 职场文书
格林童话读书笔记
2015/06/30 职场文书