最近看了看人气眼的界面,感觉到学习的地方有很多呀。这里先带大家看看人气值跳动的实现。本篇代码基于Vue2.x.x。
一、概要
首先看一下效果图:
要想实现上面的效果,我们分为这几个部分:
- 判断元素是否在可视区域内;
- 函数节流的使用;
- 元素高度过渡动画;
- 数字跳动动画;
二、判断元素是否在可视区域之内
首先我们要先获取元素的位置信息,这里我们采用getBoundingClientRect方法,MDN上对于该方法的介绍。然后我们只要与可视区域做个比较,就OK了。
// methodsisElementInViewport (el, offset) {const h = offset || 20,box = el.getBoundingClientRect(),top = (box.top >= 0),left = (box.left >= 0),bottom = (box.bottom <= (window.innerHeight || document.documentElement.clientHeight) h),right = (box.right <= (window.innerWidth || document.documentElement.clientWidth) h);return (top && left && bottom && right);}
三、函数节流的使用
接下来我们需要监听'scroll'事件,判断元素是否出现在可视区域内。对于scroll事件的优化之一,我们需要使用函数节流。你可以选择导入underscore.js的throttle函数,但是这里我尝试了一下requestAnimationFrame来实现函数节流:
//mounted: document.addEventListener('scroll', this.onScroll , false);// methods:onScroll (e) {if (!this.LOCK) {this.LOCK = true;window.requestAnimationFrame(this.scrollAction);}},scrollAction () {const flag = this.isElementInViewport(this.$refs.zfbitem, 100);if (!flag) {this.LOCK = false;} else {//触发元素高度过渡动画this.active = true;//去除掉事件监听document.removeEventListener('scroll', this.onScroll , false);}}
四、元素的高度过渡动画
在CSS当中,实现一种动画效果,你可以有很多种方式,这里我也就不一一枚举了,此例子中我采用高度过渡的方式实现效果。有人就会说通过高度过渡没有任何难度啊?实际上,你需要注意的点还是蛮多的:
- 想让一个元素的高度为0,并不是简单的height:0;就能做到的,前提是你不能设置border、垂直方向的padding等;
- 当你的元素设置height为100px时,你再设置max-height为0,它一样只显示0的高度;
- 当height的值为auto时,我们是无法过渡的。所以对于auto的情况,我们可以采用max-height来模拟一下,一般情况下,效果还行。
实现的代码还是很简单的,这里就不贴代码了。
五、数字跳动动画
首先我们需要在高度过渡动画完成后执行数字跳动动画,这里我们需要监听'transitionend'事件,对于这个事件需要特别注意的点:
- 每个过渡属性完成后多会触发一次transitionend;
- transitionend事件支持冒泡,如果子元素也有过渡效果的话,一定要阻止冒泡。
// watch : active (newValue) {if (newValue) {this.$refs.zfbitem.addEventListener('transitionend', this.transitionAction, false);}}// methods:transitionAction (e) {//不再需要监听时,一定要移除监听this.$refs.zfbitem.removeEventListener('transitionend', this.transitionAction, false);this.numberBounce();}
对于数字跳动的动画,正好利用了Vue响应式的特性偷懒了一波,感觉实现的还是有些生硬,我主要是从这几个方面下手的:
- 暂且默认两位数;
- 个位和十位多需要先跳一轮0~9,然后再跳向最终的数字,这样避免特殊的情况;
- 个位和十位动画执行的时长是一样的,通过时长和各自需要跳动的字数,计算出每一帧需要的时间。
//组件需要传入的参数props: {rate: Number}//分割个位 和 十位computed: {numberArray () {return (this.rate '').split('');}}numberBounce () {let arr = this.numberArray,totalTime = 200,a = arr[1],aLen = Number.parseInt(a),aTime = Math.floor(totalTime / (aLen 9)),i = 0,b = arr[0],bLen = Number.parseInt(b),bTime = Math.floor(totalTime / (bLen 9)),j = 0;this.bit = 0;this.ten = 0;this.bitTimeId = setInterval(_ => {i ;this.bit = i % 10; // 计数if (i - 10 === aLen) {//千万不要忘记清除定时器哦clearInterval(this.bitTimeId);this.bitTimeId = null;}}, aTime);this.tenTimeId = setInterval(_ => {j ;this.ten = j % 10;if (j - 10 === bLen) {clearInterval(this.tenTimeId);this.tenTimeId = null;}}, bTime);}
六、总结
这虽然是一个简单的效果,但是包含的知识点很多,这里又要强调了:基础很是重要,千万不要浮躁。(^_^)
喜欢本文的小伙伴们,欢迎关注我的订阅号超爱敲代码,查看更多内容.
更多专业前端知识,请上 【猿2048】www.mk2048.com