本文由云+社区发表
最近的一个活动页面须要作一个能够左右滑动的抽签效果,故经过用css的transform属性和js结合来模拟能够无限滚动的效果。css
先上效果:html
demo地址:https://kiroroyoyo.github.io/...git
结构:卡片分先后两排,每列插入10个div结点,以便作左右位移效果。github
样式:设置每一列都刚好好在中间位置(或中间位置附近),以下所示。chrome
a. 前排(cardFrond)相对于视口的初始位置(left:-255.5%;):dom
b. 后排(backFrond)相对于视口的初始位置(left:-228.3%;):布局
因为这里的中止位置是固定的,前排永远是当前卡片相对于视口居中,后排永远是两个卡片相对于视口居中,且每一个卡片是同样的,因此当卡片列表向前或向右移动到一个目标位置时,都将列表重置为初始位置继续滚动。以下图之前排卡片为例:动画
因此当滚动中止后会统一将列表样式设置为transform: translateX(0)。而对于用户这一操做是无感知的,认为已经滑动到了新的位置。this
a. 目标位移与帧位移spa
为了作出滑动后到停留位置的缓动效果,因此当用户左右滑动屏幕时,会记录滑动距离,计算出卡片该到的目标位移位置,目标位移位置是有规则的,由于这里有10张卡片均分宽度,位置必须是(100%/10)的整数倍,例如-40%、-30%、……40%,这样才能保证目标位置与初始位置相重合。
目标位移代码片断
onDocumentMouseUp : function(e){ //若是是点击事件 不设置移动 if (!this.fingerTouch) return; this.moveDirect = this.lon > 0 ? 1 : -1; this.transNum = this.lon/10 + this.moveDirect; this.lon = Math.round(this.transNum) * 10; this.fingerTouch = false; }
记录了目标位移后,每一帧会以必定的帧位移不断靠近目标位移,使其在手指离开屏幕时仍有慢慢滑动到目标位置的缓动效果。此时须要判断当前位置是否大于40%或者小于-40%,若超过这个极限值须要重设目标位移及帧位移,使其在极限值内。
animate: function(){ this.prePos += (this.lon - this.prePos) * 0.1; if (this.prePos > 40) { this.lon = this.lon - 40; this.prePos = this.prePos - 40; }else if (this.prePos < -40) { this.lon = this.lon + 40; this.prePos = this.prePos + 40; } //判断是否到达了目标位置 if (Math.abs(this.prePos - this.lon) < 0.01 && Math.abs(this.lon) > 0.01 && (!this.fingerTouch)) { this.ani_move = false; this.prePos = 0; this.frondCard.style = "transform: translateX("+ this.prePos +"%)"; this.backCard.style = "transform: translateX("+ this.prePos +"%)"; }else{ this.frondCard.style = "transform: translateX("+ this.prePos +"%)"; this.backCard.style = "transform: translateX("+ (-this.prePos) +"%)"; requestAnimationFrame(this.animate.bind(this)); } },
b. 连续滑动判断
当在上次滑动动画还未播放结束时用户又进行了第二次滑动时,须要执行一下操做:
1). 判断滑动时机处于上次滑动手指已离开屏幕但动画还未结束,此时须要记录两个flag,一个是ani_move,记录动画是否仍在进行,fingerTouch记录手指是否停留屏幕。
2). 判断第二次滑动是否与第一次不一样方向,若不一样向需重置上次帧位移为0。以避免上次帧位移太大影响移动方向。
1)与2)代码片断:
if( this.ani_move && this.fingerTouch == false) { // 判断是否不一样向 if (((e.clientX - prex) > 0 ? 1: -1) == -this.moveDirect ) { this.lon = 0; this.prePos = 0; this.moveDirect = -this.moveDirect; } }
3). 取消第二次滑动时的动画播放和位移重置
// 如果上次动画未结束不须要再次启动动画和重置目标位移 if( this.ani_move && this.fingerTouch == false) { } else { this.lon = 0; cardAnimate.animate(); }
目前这个滑动效果只能针对卡片相同,停留位置固定的状况,由于须要作到位置重合。使用css transform来作无限滚动的效果,能够避免改变dom结点带来的页面从新布局。
下图是chrome cpu6倍减速调试效果,没有触发layout,FPS基本维持在60左右。
代码地址:
https://github.com/kiroroyoyo...
此文已由做者受权腾讯云+社区发布