Vue實現跑馬燈效果以及封裝為元件釋出

heyushuo發表於2019-03-11

Vue 實現跑馬燈效果

前言

最近做活動需要做跑馬燈效果,其他同事也有實現,本來打算複製他們程式碼,發現都是使用setInterval實現了,也沒有封裝為元件,所以自己用CSS3實現了一下跑馬燈效果,並封裝為元件,這樣以後在需要寫的時候,只需要引入元件就可以了。

實現的效果如下

Vue實現跑馬燈效果以及封裝為元件釋出

實現思路

  • HTML 結構父盒子固定,子盒子移動,幷包含需要效果的內容
  • 跑馬燈效果 CSS3 實現肯定需要 infinite (迴圈執行動畫)
  • 運動前需要計算父盒子的寬度(wrapWidth),還有子盒子的總寬度(offsetWidth)
  • 需要給定速度通過寬度計算出CSS3動畫需要的時間duration

元件設計

向外暴露三個引數

  • content: 因為滾動的資料大部分都是後臺獲取的,監聽 content,當 content 有值的時候開始滾動(content 可以是任何型別資料)
  • delay: 多久時開始執行動畫只有第一次滾動時候生效(預設是 0.3s)
  • speed: 速度(預設 100)

github 地址原始碼地址

原始碼實現如下

原始碼中大部分都新增了註釋,如有問題請指出謝謝

<template>
  <div ref="wrap" class="wrap">
    <div ref="content" class="content" :class="animationClass" :style="contentStyle" @animationend="onAnimationEnd" @webkitAnimationEnd="onAnimationEnd">
      <slot></slot>
    </div>
  </div>
</template>
<script>
export default {
  props: {
    content: {
      default: ''
    },
    delay: {
      type: Number,
      default: 0.5
    },
    speed: {
      type: Number,
      default: 100
    }
  },
  mounted() {},
  data() {
    return {
      wrapWidth: 0,     //父盒子寬度
      firstRound: true, //判斷是否
      duration: 0,      //css3一次動畫需要的時間
      offsetWidth: 0,    //子盒子的寬度
      animationClass: '' //新增animate動畫
    };
  },
  computed: {
    contentStyle() {
      return {
        //第一次從頭開始,第二次動畫的時候需要從最右邊出來所以寬度需要多出父盒子的寬度
        paddingLeft: (this.firstRound ? 0 : this.wrapWidth) + 'px',
        //只有第一次的時候需要延遲
        animationDelay: (this.firstRound ? this.delay : 0) + 's',
        animationDuration: this.duration + 's'
      };
    }
  },
  watch: {
    content: {
      //監聽到有內容,從後臺獲取到資料了,開始計算寬度,並計算時間,新增動畫
      handler() {
        this.$nextTick(() => {
          const { wrap, content } = this.$refs;
          const wrapWidth = wrap.getBoundingClientRect().width;
          const offsetWidth = content.getBoundingClientRect().width;
          this.wrapWidth = wrapWidth;
          this.offsetWidth = offsetWidth;
          this.duration = offsetWidth / this.speed;
          this.animationClass = 'animate';
        });
      }
    }
  },
  methods: {
    //這個函式是第一次動畫結束的時候,第一次沒有使用infinite,第一次動畫執行完成後開始使用新增animate-infinite動畫
    onAnimationEnd() {
      this.firstRound = false;
      //這是時候樣式多出了padding-left:this.wrapWidth;所以要想速度一樣需要重新計算時間
      this.duration = (this.offsetWidth + this.wrapWidth) / this.speed;
      this.animationClass = 'animate-infinite';
    }
  }
};
</script>
<style scoped>
.wrap {
  width: 100%;
  height: 24px;
  overflow: hidden;
  position: relative;
  background: rgba(211, 125, 066, 1);
  position: relative;
  padding: 0;
}

.wrap .content {
  position: absolute;
  white-space: nowrap;
}

.animate {
  animation: paomadeng linear;
}

.animate-infinite {
  animation: paomadeng-infinite linear infinite;
}

@keyframes paomadeng {
  to {
    transform: translate3d(-100%, 0, 0);
  }
}

@keyframes paomadeng-infinite {
  to {
    transform: translate3d(-100%, 0, 0);
  }
}
</style>
複製程式碼

把原始碼釋出到 npm 上

請參考我的另外一篇文章,如何將自己的 vue 元件釋出為 npm 包

使用方法

github 如何使用地址和原始碼地址

npm install heyushuo-marquee --save
import PaoMaDeng from 'heyushuo-marquee';
<PaoMaDeng :delay="0.5" :speed="100" :content="arr">
      <span v-for="(item, index) in arr" :key="index">{{item}}</span>
</PaoMaDeng>
複製程式碼

github 如何使用地址和原始碼地址

相關文章