• vue+帧动画 实现 获奖奖品列表滚动循环展示


    实现效果

     初级方法:

    实现原理:

    1. 由于列表的总数是变化的,所以不能用css把动画写死,通过定时器移动列表,实现动画效果
    2. 计算总高度,建一个变量存储移动距离,两者之前比较,当移动距离>=总高度 就把移动距离重置为0
    3. 在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%;
             100%;
            padding: 30px 0;
          }
    
          .prize_ul {
             100%;
            color: #fff;
            position: absolute;
            padding: 0px 80px;
            // animation: opacityShow linear 5s infinite;
            .prize_item {
               100%;
              display: flex;
              justify-content: space-between;
              margin-bottom: 12px;
            }
          }

    在changeAnimation函数中, let total = 25.5 * d/2  这个25.5是每行的高度,通过 every(移动距离) 与 total(总高度) 比较,实现循环

    缺点:

    1. 滚动的时候总是感觉一卡一卡的效果,不够流畅( 原因: 浏览器每秒刷新的次数 和 每次移动距离的时间频率 不一致,导致视觉上产生卡顿效果 )
    2. 通过top值进行移动,比较损耗性能,每次改变都会影响布局
    3. 获取总高度total,不要手动计算,应该通过 clientHeight 动态获取比较好
    高级方法:

    使用一个属性: 帧动画  window.requestAnimationFrame() 
    告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行
    这个方法挂载在window上,很方便我们的使用
     
    实现原理:
    1.  通过控制 transform 的 translateY值进行移动动画
    2. 动态获取列表高度,padding,margin去掉,否则会在计算高度的时候,有误差,循环衔接点会有卡顿
    3. 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%;
          100%;
    }
    
    .prize_ul {
          100%;
         color: #fff;
         // position: absolute;
         padding: 0px 80px;
         .prize_item {
             100%;
            display: flex;
            line-height: 58px;
            justify-content: space-between;
        }
    }

    这样就完美解决了循环滚动动画,

    使用帧动画还有一个优点:  

    每次切换页面的时候,帧动画会暂停执行,直到切换回该页面,才会继续执行动画,这样就比定时器更加保护性能

    其实还有一个方案:

    用css + js

    1. css中写动画,把动画属性分开写, animation-duration 动态计算:根据每行花费几秒 * 几行 这样动态赋值
    2. css中通过控制 transform 的 translateY值进行移动50%即可
    // 写动画
    @keyframes opacityShow {
            from {
              transform: translateY(0);          
            }
            to {
              transform: translateY(-50%);          
            }
    }
    .prize_ul {
             100%;
            color: #fff;
            padding: 0px 80px;
            animation-name: opacityShow;
            animation-iteration-count: infinite;
            .prize_item {
               100%;
              display: flex;
              line-height: 58px;
              justify-content: space-between;
            }
     }
     

     使用

  • 相关阅读:
    triangle
    Synchronizing timer
    jenkins 安装网址
    Java I/O编程思路
    SpEL快速入门
    Spring ApplicationContext的事件机制
    畅谈Spring设计哲学
    Spring ApplicationContext的国际化支持
    SQL中 WHERE与HAVING的区别
    Hibernate的查询语言之HQL(二)——Hibernate查询的from字句
  • 原文地址:https://www.cnblogs.com/huangaiya/p/12668345.html
Copyright © 2020-2023  润新知