在写移动端的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. 基于Cef内核的多店铺登录器(含源码)

    公司是做电商的,在速卖通平台上开了若干店铺,每天都需要登录店铺打理,如:发货提交.获取运单号等.多个店铺的情况下,同时使用浏览器就会非常繁琐,如:要记住帐户名和密码,还要在不同店铺间切换.如果能够制作 ...

  2. .Net中各种不同的对象创建方式的速度差异

    在.Net中,微软给我们提供了很多不同的创建对象实例的方法,它们的速度又各有不同,以下一一列举. 使用new关键字 这在.Net中是最常见,也是速度最快的方式:                     ...

  3. C#截取当前活动窗体的图片

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  4. ionic开发遇到的坑及总结

    前言 ionic是一个用来开发混合手机应用的,开源的,免费的代码库.可以优化html.css和js的性能,构建高效的应用程序,而且还可以用于构建Sass和AngularJS的优化.ionic会是一个可 ...

  5. Thymleaf——工作笔记本

    Thymleaf(工作笔记本)  1,循环遍历 th:each="li,information:${information}"   2,文本  th:text="${nu ...

  6. 解决微信公众平台IP白名单

    微信公众平台,作为自媒体的旗舰级产品,越来越多的人已经投入它的怀抱.正如它的广告词所说:再小的个体,也有品牌 好吧,闲话不多说,今天要说的是它的IP白名单机制. 我们现在安装的大部分的电信的家庭级别的 ...

  7. JavaFx新手教程-布局-StackPane

    cmlanche: 您叫什么名字? StackPane cmlanche: 您好,StackPane君,可以问下您在JavaFX家族中是什么地位? stackpane君: 我可重要了,我是在JavaF ...

  8. 崩溃 golang入坑系列

    早上(11.30)收到邮件,Vultr东京机房网络故障.当时搭建SS时,考虑到了机房故障.所以特意分出了日本和香港两条线路.但千算万算,忘记数据库还在东京机房中. 现在网络故障,SS服务器无法读取数据 ...

  9. base64位加密解密

    1.base64位加密base64是用于传输8Bit字节代码,由上图的编码表可以知道,编码后的内容只包含这64个字符类型,所以称为base64编码 2.编码过程 : 首先将待编码的内容转换成8位二进制 ...

  10. mybatis运行时拦截ParameterHandler注入参数

    在实现多租户系统时,每个租户下的用户,角色,权限,菜单都是独立的,每张表里都有租户Id字段 (tenantId),每次做数据库操作的时候都需要带上这个字段,很烦. 现在的需求就是在mybatis向sq ...