vue+帧动画 实现 获奖奖品列表滚动循环展示
实现效果
初级方法:
实现原理:
- 由于列表的总数是变化的,所以不能用css把动画写死,通过定时器移动列表,实现动画效果
- 计算总高度,建一个变量存储移动距离,两者之前比较,当移动距离>=总高度 就把移动距离重置为0
- 在css中给列表盒子绝对定位,通过移动top值实现动画
注意事项:由于需要循环滚动,可以把获取到的数组在复制一份放到列表里,做到无缝衔接
html:
<div class="inner_box"> <div class="prize_container"> <ul ref="prizeRef" class="prize_ul" v-if="info.joinList && info.joinList.length" > <li class="prize_item" v-for="(item, index) in info.joinList" :key="index" > <div class="left">{{ item.mobile | mobileFilter }}</div> <div class="right">{{ item.name }}</div> </li> </ul> </div> </div>
js:
// 获取活动信息 getData() { this.fetch .get("TjModule/GetActivityInfo", { activityId: this.id }) .then(res => { if (res.data && res.data.success) { this.$setTitle(res.data.result.activityName); this.info = res.data.result; let arr = []; if (res.data.result.joinList.length) { arr = res.data.result.joinList; arr.map(i => { arr.push(i); }); } this.info.joinList = arr; console.log(this.info.joinList); this.$nextTick(() => { this.changeAnimation(this.info.joinList.length); }); } }) .catch(error => { if (error.response && error.response.status === 500) { this.$toast(error.response.data.error.message); } }); }, // 编写动画 changeAnimation(d) { console.log(9090); let total = 25.5 * d/2 let every = 0 const dm = this.$refs.prizeRef; this.timers = setInterval(()=>{ every += 5 if(every < total){ dm.style.top = - every + 'px' }else{ dm.style.top = 0 every = 0 } },120) },
// 关闭定时器
beforeDestroy() {
clearInterval(this.timers)
},
css:
.prize_container { overflow: hidden; position: absolute; height: 90%; width: 100%; padding: 30px 0; } .prize_ul { width: 100%; color: #fff; position: absolute; padding: 0px 80px; // animation: opacityShow linear 5s infinite; .prize_item { width: 100%; display: flex; justify-content: space-between; margin-bottom: 12px; } }
在changeAnimation函数中, let total = 25.5 * d/2 这个25.5是每行的高度,通过 every(移动距离) 与 total(总高度) 比较,实现循环
缺点:
- 滚动的时候总是感觉一卡一卡的效果,不够流畅( 原因: 浏览器每秒刷新的次数 和 每次移动距离的时间频率 不一致,导致视觉上产生卡顿效果 )
- 通过top值进行移动,比较损耗性能,每次改变都会影响布局
- 获取总高度total,不要手动计算,应该通过 clientHeight 动态获取比较好
高级方法:
使用一个属性: 帧动画
window.requestAnimationFrame()
告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行这个方法挂载在window上,很方便我们的使用
实现原理:
- 通过控制 transform 的 translateY值进行移动动画
- 动态获取列表高度,padding,margin去掉,否则会在计算高度的时候,有误差,循环衔接点会有卡顿
- requestAnimationFrame与animate函数相互调用,执行帧动画requestAnimationFrame时,调用移动动画的函数,实现同频率改变 requestAnimationFrame(this.animate)
animate() {
requestAnimationFrame(this.animate);
this.changeAnimation();
},
html:
<div class="prize_container"> <div class="prize_list" :style="{ transform: 'translateY( -'+ ulLeft + 'px)' }" > <ul ref="prizeUl" class="prize_ul" v-if="info.joinList && info.joinList.length" > <li class="prize_item" v-for="(item, index) in info.joinList" :key="index" > <div class="left">{{ item.mobile | mobileFilter }}</div> <div class="right">{{ item.name }}</div> </li> </ul> <ul class="prize_ul" v-if="info.joinList && info.joinList.length" > <li class="prize_item" v-for="(item, index) in info.joinList" :key="index" > <div class="left">{{ item.mobile | mobileFilter }}</div> <div class="right">{{ item.name }}</div> </li> </ul> </div> </div>
这里使用复制两个ul元素,不在js中操作复制列表
js:
data(){ prizeUlHeight:0, //滚动ref高度 $prizeUl:null, //滚动的ref ulLeft:0, //移动参数 }
beforeDestroy() {
cancelAnimationFrame(this.times); //清理原生的监听
},
methods:{ // 获取活动信息 getData() { this.fetch .get("TjModule/GetActivityInfo", { activityId: this.id }) .then(res => { if (res.data && res.data.success) { this.$setTitle(res.data.result.activityName); this.info = res.data.result; this.$nextTick(() => { this.$prizeUl = this.$refs.prizeUl; this.prizeUlHeight = this.$prizeUl.clientHeight; this.times = requestAnimationFrame(this.animate); }); } }) .catch(error => { if (error.response && error.response.status === 500) { this.$toast(error.response.data.error.message); } }); }, animate() { requestAnimationFrame(this.animate); this.changeAnimation(); }, // 编写动画 changeAnimation(d) { if( this.ulLeft == this.prizeUlHeight ){ this.ulLeft = 0; }else{ this.ulLeft += 1; } }, }
css:
.prize_container { overflow: hidden; position: absolute; height: 90%; width: 100%; } .prize_ul { width: 100%; color: #fff; // position: absolute; padding: 0px 80px; .prize_item { width: 100%; display: flex; line-height: 58px; justify-content: space-between; } }
这样就完美解决了循环滚动动画,
使用帧动画还有一个优点:
每次切换页面的时候,帧动画会暂停执行,直到切换回该页面,才会继续执行动画,这样就比定时器更加保护性能
其实还有一个方案:
用css + js
- css中写动画,把动画属性分开写, animation-duration 动态计算:根据每行花费几秒 * 几行 这样动态赋值
- css中通过控制 transform 的 translateY值进行移动50%即可
// 写动画 @keyframes opacityShow { from { transform: translateY(0); } to { transform: translateY(-50%); } }
.prize_ul {
width: 100%;
color: #fff;
padding: 0px 80px;
animation-name: opacityShow;
animation-iteration-count: infinite;
.prize_item {
width: 100%;
display: flex;
line-height: 58px;
justify-content: space-between;
}
}
使用