在写移动端的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. 阿里云RDS SQL Server 2008 R2 使用本地SQL备份文件还原全过程

    最近公司准备全面转向阿里云,写了好几个方案,最终决定购买一台ECS和一台RDS搭配使用.开始对阿里的RDS产品陌生,加上公司的数据库文件近20G,诸多担心,生怕产品买来了不能用,给公司造成损失.后来联 ...

  2. Velocity(7)——velocity进阶用法

    1.截取部分字段substring 1 原始字符串:$!ag.tagValue,也许很长,前端页面展示时需要截取字符串. 2 #set($str=$!ag.tagValue) 3 4 #if($str ...

  3. SmartAdmin(SmartAdmin_v1.5.2)

    SmartAdmin_v1.5.2是一个基于bootstrap上开发的页面设置,前后台数据交互和样式显示. 用过的例子有F:\SmartAdmin_v1.5.2\SmartAdmin_v1.5.2\D ...

  4. C#中访问私有成员

    首先访问一个类的私有成员不是什么好做法.大家都知道私有成员在外部是不能被访问的.一个类中会存在很多私有成员:如私有字段.私有属性.私有方法.对于私有成员造访,可以套用下面这种非常好的方式去解决. pr ...

  5. [转载] Spark:大数据的“电光石火”

    转载自http://www.csdn.net/article/2013-07-08/2816149 Spark已正式申请加入Apache孵化器,从灵机一闪的实验室“电火花”成长为大数据技术平台中异军突 ...

  6. 图片转换base64数据上传,并且实现预览的简便方法

    对于很多新手来说,实现上传图片并且预览功能,都会感到不知所可,然后开始在网站搜索各种各样的图片上传预览插件,但是有的时候我们只是想简单的实现判断格式,以及预览的功能,使用插件的话,会使得项目的资源空间 ...

  7. JavaWeb面试(六)

    51.说一说Servlet的生命周期? Servlet有良好的生存期的定义,包括加载和实例化.初始化.处理请求以及服务结束.这个生存期由javax.servlet.Servlet接口的init(),s ...

  8. iBatis & myBatis & Hibernate 要点记录

    iBatis & myBatis & Hibernate 要点记录 这三个是当前常用三大持久层框架,对其各自要点简要记录,并对其异同点进行简单比较. 1. iBatis iBatis主 ...

  9. ios2048小游戏

    最近突然想写一个2048的小游戏,由于全部是自定义控件,所以程序看起来冗杂,但是核心的算法部分还是很不错的,大家感兴趣的可以仔细看看. 声明部分: #import <UIKit/UIKit.h& ...

  10. 用JS实现Ajax请求

    AJAX核心(XMLHttpRequest) 其实AJAX就是在Javascript中多添加了一个对象:XMLHttpRequest对象.所有的异步交互都是使用XMLHttpServlet对象完成的. ...