在写移动端的APP或者页面时,经常会遇到惯性滚动与回弹效果。用插件iscroll可以轻松解决这个问题,大多数的移动框架也能轻松解决这个问题,它们内部都封装了这个效果。

一直好奇这个效果原生JS是怎么实现的,里面涉及到的弹力公式以及惯性效果还有一大堆临界点的判断,很是考验人。

在网上找了一下,看到有大神的一篇相关的笔记,所以复制过来,仔细研究。

<!DOCTYPE html>
<html lang="zh-CN"> <head>
<meta charset="UTF-8"/>
<meta name="Keywords" content=""/>
<meta name="Description" content=""/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no"/>
<meta name="apple-mobile-web-app-capable" content="yes"/>
<meta content="telephone=no" name="format-detection"/>
<meta content="email=no" name="format-detection"/>
<title>Document</title>
<style>
body {
margin: 0;
padding: 0;
} div {
position: relative;
width: 200px;
height: 300px;
margin: 3em auto;
border: 1px solid #CCC;
overflow: hidden;
-webkit-user-select: none;
user-select: none;
} ol {
width: 100%;
} ol > li {
height: 30px;
}
</style>
</head> <body>
<div>
<ol>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ol>
</div>
<script>
function myScroll(ctx) {
var ol = ctx.firstElementChild || ctx.firstChild,
offset = 50,//最大溢出值
cur = 0,//列表滑动位置
isDown = false,
vy = 0,//滑动的力度
fl = 150,//弹力,值越大,到度或到顶后,可以继续拉的越远
isInTransition = false;//是否在滚动中 ctx.addEventListener("mousedown", function (e) {
if (isInTransition)return;//如果在滚动中,则中止执行
clearTimeout(this._timer);//清除定时器
vy = 0;
this._oy = e.clientY - cur;//计算鼠标按下位置与列表当前位置的差值,列表位置初始值为0
this._cy = e.clientY;//鼠标按下的位置
this._oh = this.scrollHeight;//列表的高度
this._ch = this.clientHeight;//容器的高度
this._startTime = e.timeStamp;//鼠标按下时的时间戳
isDown = true;//鼠标是否有按下,主要防止用户是从容器外开始滑动的 }); ctx.addEventListener("mousemove", function (e) {
if (isDown) {//如果鼠标是从容器里开始滑动的
if (e.timeStamp - this._startTime > 40) {//如果是慢速滑动,就不会产生力度,列表是跟着鼠标移动的
this._startTime = e.timeStamp;//慢速滑动不产生力度,所以需要实时更新时间戳
cur = e.clientY - this._oy;//列表位置应为 鼠标当前位置减去鼠标按下时与列表位置的差值,如:列表初始位置为0,鼠标在 5的位置按,那么差值为 5,此处假如鼠标从5滑动到了4,向上滑,cur = 4-5 =-1 ,假如鼠标从5滑动到了6,向下滑,cur= 6 - 5 = 1 if (cur > 0) {//如果列表位置大于0,既鼠标向下滑动并到顶时
cur *= fl / (fl + cur);//列表位置带入弹力模拟,公式只能死记硬背了,公式为:位置 *=弹力/(弹力+位置)
}else if (cur < this._ch - this._oh) {//如果列表位置小于 容器高度减列表高度(因为需要负数,所以反过来减),既向上滑动到最底部时。
//当列表滑动到最底部时,cur的值其实是等于 容器高度减列表高度的,假设窗口高度为10,列表为30,那此时cur为 10 - 30 = -20,但这里的判断是小于,所以当cur<-20时才会触发,如 -21;
cur += this._oh - this._ch;//列表位置加等于 列表高度减容器高度(这是与上面不同,这里是正减,得到了一个正数) ,这里 cur 为负数,加上一个正数,延用上面的假设,此时 cur = -21 + (30-10=20) = -1 ,所以这里算的是溢出数 // console.log(cur);
cur = cur * fl / (fl - cur) - this._oh + this._ch;//然后给溢出数带入弹力,延用上面的假设,这里为 cur = -1 * 150 /(150 - -1 = 151)~= -0.99 再减去 30 等于 -30.99 再加上容器高度 -30.99+10=-20.99 ,这也是公式,要死记。。
}
setPos(cur);//移动列表
}
vy = e.clientY - this._cy;//记录本次移动后,与前一次鼠标位置的滑动的距离,快速滑动时才有效,慢速滑动时差值为 1 或 0,vy可以理解为滑动的力度 // console.log(vy);
this._cy = e.clientY;//更新前一次位置为现在的位置,以备下一次比较
} }, false); ctx.addEventListener("mouseleave", mleave, false); ctx.addEventListener("mouseup", mleave, false); function setPos(y) {//传们列表y轴位置,移动列表
ol.style.transform = "translateY(" + y + "px) translateZ(0)";
} function ease(target) {
isInTransition = true;
ctx._timer = setInterval(function () {//回弹算法为 当前位置 减 目标位置 取2个百分点 递减
cur -= (cur - target) * 0.2;
if (Math.abs(cur - target) < 1) {//减到 当前位置 与 目标位置相差小于1 之后直接归位
cur = target;
clearInterval(ctx._timer);
isInTransition = false;
}
setPos(cur);
}, 20);
} function mleave(e) {
if (isDown) {
isDown = false;
console.log(vy);
var t = this,
friction = ((vy >> 31) * 2 + 1) * 0.5,//根据力度套用公式计算出惯性大小,公式要记住
oh = this.scrollHeight - this.clientHeight;
this._timer = setInterval(function () {//
vy -= friction;//力度按 惯性的大小递减
cur += vy;//转换为额外的滑动距离
setPos(cur);//滑动列表 if (-cur - oh > offset) {//如果列表底部超出了
clearTimeout(t._timer);
ease(-oh);//回弹
return;
}
if (cur > offset) {//如果列表顶部超出了
clearTimeout(t._timer);
ease(0);//回弹
return;
}
if (Math.abs(vy) < 1) {//如果力度减小到小于1了,再做超出回弹
clearTimeout(t._timer);
if (cur > 0) {
ease(0);
return;
}
if (-cur > oh) {
ease(-oh);
return;
}
}
}, 20);
}
}
} myScroll(document.querySelector("div"));
</script>
</body> </html>
												

源生js惯性滚动与回弹效果的更多相关文章

  1. ios客户端快速滚动和回弹效果的实现

    现在很多for Mobile的HTML5网页内都有快速滚动和回弹的效果,看上去和原生app的效率都有得一拼. 要实现这个效果很简单,只需要给容器加一行css代码即可 -webkit-overflow- ...

  2. js之滚动置顶效果

    0.js获取高度 ? 1 2 3 4 5 6 document.all   // 只有ie认识   document.body.clientHeight              // 文档的高,屏幕 ...

  3. js 上下滚动加停顿效果,js 跑马灯加停顿效果

    <div id="middle"> <ul id="slide1"> <li>尾号1183的用户刚刚领取了 78.23元 的 ...

  4. js鼠标滚轮滚动图片切换效果

    效果体验网址:http://keleyi.com/keleyi/phtml/image/12.htm HTML文件代码: <!DOCTYPE html PUBLIC "-//W3C// ...

  5. 移动端页面 弹出框滚动,底部body锁定,不滚动 / 微信网页禁止回弹效果

    需求:页面有弹出层菜单,当弹出层菜单超出屏幕可视区域时,不能滚动.加上滚动后,底部body的滚动事件如何禁止,加上了overflow:hidden;还是不可用. 如下图:地区弹出框可以滚动,而底部的b ...

  6. -webkit-overflow-scrolling 与滚动回弹效果.

    参考来源:https://developer.mozilla.org/zh-CN/docs/Web/CSS/-webkit-overflow-scrolling https://www.w3cways ...

  7. JS 判断滚动底部并加载更多效果。。。。。。。。。

    JS 判断滚动底部并加载更多效果......... <html lang="zh-cn"> <head> <meta http-equiv=" ...

  8. -webkit-overflow-scrolling : touch 快速滚动 回弹 效果

    现在很多for Mobile的HTML5网页内都有快速滚动和回弹的效果,看上去和原生app的效率都有得一拼. 要实现这个效果很简单,只需要在元素上加一行css代码即可: -webkit-overflo ...

  9. html通过css,js实现div悬浮效果总汇,如原生JS实现滚动到一定位置实现div悬浮

    在我们的实际开发中,经常会遇到页面中需要悬浮效果,比如最早的客服联系,对联悬浮广告等,今天为大家介绍一些如何实现div悬浮的效果. 传统的fixed实现: 通过css中的属性position参数设为f ...

随机推荐

  1. 数据库中float类型字段,转化到前端显示,统一保留两位小数

    客户的一个需求,mybatis查询到的数据库的数据进行转换,采用TypeHandler<T>的方式.float保留两位精度可以采用DecimalFormat 直接贴上最终的解决代码(事情没 ...

  2. 记录一次Session偶尔获取不到的解决过程

    导读 平台下某子系统有密码登录需求,初步考虑用Session,登录后设置Session[key]=value;Session中若某key对应的Session,即Session[key]为null则限制 ...

  3. Ext3和Ext4文件系统区别

    inode http://www.cnblogs.com/itech/archive/2012/05/15/2502284.html Ex3使用15个inode查询数据块,前12个为直接数据块,直接指 ...

  4. HTML的用法

    今天学习了HTMIL,标签.这个东西吧,没什么很难的,就是得多练多打.今天的一些个人心得: 标签:成对存在的名称 标签注意:1.标签名放在<> 2.标签成对存在的 3.结束标签有斜杠/ 例 ...

  5. iOS打包静态库(完整篇)

    1. 什么是库? 所谓库就是程序代码的集合,是共享程序代码的一种方式. 2. 库的分类 根据程序代码的开源情况,库可以分为两类 开源库源代码是公开的,你可以看到具体实现.比如GitHub上比较出名的第 ...

  6. springboot-helloworld

    1使用idea创建springboot项目如下图所示 并选择web模块 2,登录springboot官网 http://projects.spring.io/spring-boot/ 引入相关依赖包如 ...

  7. ES6模块化

    关于ES6模块化 历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来.其他语言都有这项功能,比如 Ruby 的require ...

  8. php面试之四-Linux部分

    php面试题之四——Linux部分(高级部分) 四.Linux部分 1.请解释下列10个shell命令的用途(新浪网技术部) top.ps.mv.find.df.cat.chmod.chgrp.gre ...

  9. LinuxRPM包安装

    转载注明出处:原文地址 ## LinuxRPM包安装 二进制包(RPM包.系统默认包) RPM安装 rpm -ivh 包全名(查询依赖网址:http://www.rpmfind.net) -i(ins ...

  10. 自定义spring mvc的json视图

    场景 前端(安卓,Ios,web前端)和后端进行了数据的格式规范的讨论,确定了json的数据格式: { "code":"200", "data&quo ...