阅读 5877

JS 的平凡之路--学习人气眼中的效果(上)

最近看了看人气眼的界面,感觉到学习的地方有很多呀。这里先带大家看看人气值跳动的实现。本篇代码基于Vue2.x.x。

一、概要

  首先看一下效果图:

人气值动画

  要想实现上面的效果,我们分为这几个部分:

  • 判断元素是否在可视区域内;
  • 函数节流的使用;
  • 元素高度过渡动画;
  • 数字跳动动画;

二、判断元素是否在可视区域之内

  首先我们要先获取元素的位置信息,这里我们采用getBoundingClientRect方法,MDN上对于该方法的介绍。然后我们只要与可视区域做个比较,就OK了。

    // methods
    isElementInViewport (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);
    }
复制代码

六、总结

  这虽然是一个简单的效果,但是包含的知识点很多,这里又要强调了:基础很是重要,千万不要浮躁。(^_^)


  喜欢本文的小伙伴们,欢迎关注我的订阅号超爱敲代码,查看更多内容.

关注下面的标签,发现更多相似文章
评论

查看更多 >