利用JS实现scroll自定义滚动效果详解


Posted in Javascript onOctober 17, 2017

前言

最近在公司开发项目的时候,原生滚动条中有些东西没办法自定义去精细的控制,于是开发一个类似于better-scroll一样的浏览器滚动监听的JS实现,下面我们就来探究一下自定义滚动需要考虑哪些东西,经过哪些过程。话不多说了,来一起看看详细的介绍吧。

选择滚动监听的事件

因为是自定义手机端的滚动事件,那我选择的是监听手机端的三个touch事件来实现监听,并实现了两种滚动效果,一种是通过-webkit-transform,一种是通过top属性。两种实现对于滚动的基本效果够能达到,可是top的不适合滚动中还存在滚动,可是能解决滚动中存在postion:fixed属性的问题;而transform可以实现滚动中有滚动,可是又不能解决postion:fixed的问题,所以,最后选择性考虑使用哪一种实现方式,用法一样。

主要的实现业务逻辑

handleTouchMove(event){
 event.preventDefault();
 this.currentY = event.targetTouches[0].screenY;
 this.currentTime = new Date().getTime();
 // 二次及以上次数滚动(间歇性滚动)时间和路程重置计算,0.05是间歇性滚动的停顿位移和时间比
 if (Math.abs(this.currentY - this.lastY) / Math.abs(this.currentTime - this.lastTime) < 0.05) {
  this.startTime = new Date().getTime();
  this.resetY = this.currentY;
 }
 this.distance = this.currentY - this.startY;
 let temDis = this.distance + this.oldY;
 /*设置移动最小值*/
 temDis = temDis > this.minValue ? temDis * 1 / 3 : temDis;
 /*设置移动最大值*/
 temDis = temDis < -this.maxValue ? -this.maxValue + (temDis + this.maxValue) * 1 / 3 : temDis;
 this.$el.style["top"] = temDis + 'px';
 this.lastY = this.currentY;
 this.lastTime = this.currentTime;
 this.dispatchEvent();
 this.scrollFunc(event);
},

代码解读:这是监听touchmove事件的回调,其中主要计算出目标节点this.$el的top或者-webkit-transform中translateY的值,而计算的参考主要以事件节点的screenY的垂直移动距离为参考,当然其中还要判断一下最大值和最小值,为了保证移动可以的超出最大值小值一定的距离所以加了一个1/3的移动计算。这里可能主要到了有一个间歇性滚动的判断和计算,主要是服务于惯性滚动的,目的是让惯性滚动的值更加精确。

handleTouchEnd(event){
 /*点透事件允许通过*/
 if (!this.distance) return;
 event.preventDefault();
 let temDis = this.distance + this.oldY;
 /*计算缓动值*/
 temDis = this.computeSlowMotion(temDis);
 /*设置最小值*/
 temDis = temDis > this.minValue ? this.minValue : temDis;
 /*设置最大值*/
 temDis = temDis < -this.maxValue ? -this.maxValue : temDis;
 this.$el.style["transitionDuration"] = '500ms';
 this.$el.style["transitionTimingFunction"] = 'ease-out';
 /*确定最终的滚动位置*/
 setTimeout(()=> {
  this.$el.style["top"] = temDis + 'px';
 }, 0);
 // 判断使用哪一种监听事件
 if (this.slowMotionFlag) {
  this.dispatchEventLoop();
 } else {
  this.dispatchEvent();
 }
 this.$el.addEventListener('transitionend', ()=> {
  window.cancelAnimationFrame(this.timer);
 });
 this.scrollFunc(event);
}

代码解读:这是touchend事件监听的回调,其中这里要判断是否要拦截click和tap事件,并且这里还要计算惯性缓动值,设置最终的最大最小值,以及设置动画效果和缓动效果。下面来谈一下滚性滚动的计算:

// 计算惯性滚动值
computeSlowMotion(temDis){
 var duration = new Date().getTime() - this.startTime;
 // 300毫秒是判断间隔的最佳时间
 var resetDistance = this.currentY - this.resetY;
 if (duration < 300 && Math.abs(resetDistance) > 10) {
  var speed = Math.abs(resetDistance) / duration,
   destination;
  // 末速度为0 距离等于初速度的平方除以2倍加速度
  destination = (speed * speed) / (2 * this.deceleration) * (resetDistance < 0 ? -1 : 1);
  this.slowMotionFlag = true;
  return temDis += destination;
 } else {
  this.slowMotionFlag = false;
  return temDis;
 }
},

代码解读:滚性滚动的算法主要是根据一个路程和时间计算出初速度,以及原生滚动的加速度的大于值0.006来计算滚动的总位移。这里主要还要判断一下一个300ms的经验值。

总结

大概的流程和思考就是这样了,后续还会增加更多的功能进行扩展

附上git地址:https://github.com/yejiaming/scroll

本地下载:http://xiazai.3water.com/201710/yuanma/js-scroll-custom(3water.com).rar

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

Javascript 相关文章推荐
PJ Blog修改-禁止复制的代码和方法
Oct 25 Javascript
js获取TreeView控件选中节点的Text和Value值的方法
Nov 24 Javascript
基于datagrid框架的查询
Apr 08 Javascript
一些老手都不一定知道的JavaScript技巧
May 06 Javascript
Ext JS框架中日期函数的用法及日期选择控件的实现
May 21 Javascript
jQuery EasyUI的TreeGrid查询功能实现方法
Aug 08 jQuery
vuejs父子组件之间数据交互详解
Aug 09 Javascript
浅谈es6 javascript的map数据结构
Dec 14 Javascript
React 组件转 Vue 组件的命令写法
Feb 28 Javascript
微信小程序网络层封装的实现(promise, 登录锁)
May 08 Javascript
js实现弹框效果
Mar 24 Javascript
js前端设计模式优化50%表单校验代码示例
Jun 21 Javascript
jquery实现图片跟随鼠标的实例
Oct 17 #jQuery
vue获取input输入值的问题解决办法
Oct 17 #Javascript
node.js 用socket实现聊天的示例代码
Oct 17 #Javascript
Bootstrap图片轮播效果详解
Oct 17 #Javascript
vue组件之Alert的实现代码
Oct 17 #Javascript
JS实现按钮添加背景音乐示例代码
Oct 17 #Javascript
vue-cli之router基本使用方法详解
Oct 17 #Javascript
You might like
Smarty模板快速入门
2007/01/04 PHP
PHP 地址栏信息的获取代码
2009/01/07 PHP
如何取得中文字符串中出现次数最多的子串
2013/08/08 PHP
php ci框架中加载css和js文件失败的解决方法
2014/03/03 PHP
php抓取并保存网站图片的实现代码
2015/10/28 PHP
PHP中模拟链表和链表的基本操作示例
2016/02/27 PHP
Laravel 自定命令以及生成文件的例子
2019/10/23 PHP
在网站上应该用的30个jQuery插件整理
2011/11/03 Javascript
在新窗口打开超链接的方法小结
2013/04/14 Javascript
7个让JavaScript变得更好的注意事项
2015/01/28 Javascript
jQuery实现每隔几条元素增加1条线的方法
2016/06/27 Javascript
jQuery 获取页面li数组并删除不在数组中的key
2016/08/02 Javascript
jQuery实现的无限级下拉菜单功能示例
2016/09/12 Javascript
浅谈JS之iframe中的窗口
2016/09/13 Javascript
ES6学习教程之块级作用域详解
2017/10/09 Javascript
js实现以最简单的方式将数组元素添加到对象中的方法
2017/12/20 Javascript
微信小程序实现传参数的几种方法示例
2018/01/10 Javascript
AnglarJs中的上拉加载实现代码
2018/02/08 Javascript
Vue-CLI3.x 设置反向代理的方法
2018/12/06 Javascript
jQuery中DOM常见操作实例小结
2019/08/01 jQuery
刷新页面后让控制台的js代码继续执行
2019/09/20 Javascript
Vue2.X和Vue3.0数据响应原理变化的区别
2019/11/07 Javascript
js实现上下左右键盘控制div移动
2020/01/16 Javascript
Python中处理字符串之islower()方法的使用简介
2015/05/19 Python
Python中新式类与经典类的区别详析
2019/07/10 Python
如何使用Python脚本实现文件拷贝
2019/11/20 Python
python标准库OS模块详解
2020/03/10 Python
如何Tkinter模块编写Python图形界面
2020/10/14 Python
伊利莎白雅顿官网:Elizabeth Arden
2016/10/10 全球购物
《鱼游到了纸上》教学反思
2014/02/20 职场文书
中班开学寄语
2014/04/04 职场文书
干部作风整顿自我剖析材料和整改措施
2014/09/18 职场文书
区政府领导班子个人对照检查材料
2014/09/25 职场文书
文化苦旅读书笔记
2015/06/29 职场文书
幼儿园保育员随笔
2015/08/14 职场文书
深入理解 Golang 的字符串
2022/05/04 Golang