vue文字滾動元件

青檸咖啡發表於2021-03-26

看了好多網上的文字元件,發現好多都有這樣那樣的問題;特別是滾動的時候失真的感覺,今天整合了文字滾動的方式用CSS的

animation寫出一套元件;VUE專案直接用。感覺有用的朋友關注下
 
效果圖,gif製作有卡頓,實際效果我是沒發現卡頓
 

使用方法

 
<template>
  <div id="app">
    <h5>橫向文字滾動</h5>
    <div class="inner2">
      <horizontal-text-scroll>
        <ul class="scroll">
          <li v-for="item in arr2" :key="item">
            {{item}}
          </li>
        </ul>
      </horizontal-text-scroll>
    </div>

    <div class="inner2">
      <horizontal-text-scroll>
        <ul class="scroll">
          <li v-for="item in arr" :key="item">
            {{item}}
          </li>
        </ul>
      </horizontal-text-scroll>
    </div>

    <h5>多行間歇滾動</h5>
    <div class="inner">
      <interval-text-scroll :scrollArr="arr"></interval-text-scroll>
    </div>

    <h5>單行間歇滾動</h5>
    <div class="inner2">
      <interval-text-scroll :scrollArr="arr2"></interval-text-scroll>
    </div>

    <h5>無縫滾動</h5>
    <div class="inner">
      <seamless-text-scroll :scrollArr="arr"></seamless-text-scroll>
    </div>

    <h5>無縫滾動內容不足停止滾動</h5>
    <div class="inner3">
      <seamless-text-scroll :scrollArr="arr2"></seamless-text-scroll>
    </div>

    
  </div>
</template>

<script>
import IntervalTextScroll from './components/IntervalTextScroll'
import SeamlessTextScroll from './components/SeamlessTextScroll'
import HorizontalTextScroll from './components/HorizontalTextScroll'
export default {
  data () {
    return {
      arr: [
        '最美的相遇,不言過往;最好的離別,不問歸期。',
         '一個人,一座城,一生心疼',
        '靜水流深,滄笙踏歌;三生陰晴圓缺,一朝悲歡離合。',
        '原來地久天長,只是誤會一場。',
        '生能盡歡,死亦無憾。',
        '大概是一起吹過晚風的人會比較難忘。'
      ],
      arr2: ['許我浮生一世安,還你笑顏承你歡。']
    }
  },
  components: {
    IntervalTextScroll,
    SeamlessTextScroll,
    HorizontalTextScroll
  }
}
</script>

<style lang="less" scoped>
.inner {
  height: 90px;
  overflow: hidden;
  margin: 10px;
  border: 1px solid red;
  padding: 0 20px;
  margin-bottom: 20px;
}
.inner2 {
  height: 30px;
  overflow: hidden;
  margin: 10px;
  border: 1px solid red;
  padding: 0 20px;
  margin-bottom: 20px;
}
.inner3 {
  height: 150px;
  overflow: hidden;
  margin: 10px;
  border: 1px solid red;
  padding: 0 20px;
  margin-bottom: 20px;
}
</style>

 

1. 橫向文字滾動

/* 橫向文字滾動 */
<template>
  <div class="scroll-inner" ref="scrollInner">
    <div class="scroll-wrap" ref="scrollWrap" :style="wrapStyle">
      <slot></slot>
    </div>
    <div class="scroll-wrap">
      <slot></slot>
    </div>
    <!-- <ul class="scroll-wrap">
      <li v-for="item in runArr" :key="item">
        {{item}}
      </li>
    </ul> -->
  </div>
</template>

<script>
/**
 * speed               1           速度
 * 
 * 格式要求類名scroll、ul、li格式勿變,li內排版可自定義
 * <horizontal-text-scroll>
      <ul class="scroll">
        <li v-for="item in arr2" :key="item">
          {{item}}
        </li>
      </ul>
    </horizontal-text-scroll>
 */
export default {
  data () {
    return {
      runArr: [],
      wrapWidth: 0,
      innerWidth: 0,
      retry: 0,
      tim: Math.floor(Math.random() * (99999 - 10000) + 10000)
    }
  },
  props: {
    // 滾動速度
    speed: {
      type: Number,
      default: 1
    }
  },
  computed: {
    wrapStyle () {
      const s = this.wrapWidth / (30 + this.speed * 5)
      return {
        animationDuration: `${s}s`,
        animationIterationCount: 'infinite',
        animationName: `move${this.tim}`,
        animationTimingFunction: 'linear'
      }
    }
  },
  mounted () {
    this.getWrapWidth()
  },
  methods: {
    getWrapWidth () {
      this.$nextTick(() => {
        this.wrapWidth = this.$refs.scrollWrap.clientWidth
        this.innerWidth = this.$refs.scrollInner.clientWidth
        console.log(this.wrapWidth)
        console.log(this.innerWidth)
        if (!this.wrapWidth && this.retry < 3) {
          this.getWrapWidth()
          this.retry++
          return
        } else if (!this.wrapWidth && this.retry === 3){
          console.error('獲取元素高度失敗或高度為0')
          return
        }
        this.createStyle()
      })
    },
    createStyle () {
      const style = `
        @keyframes move${this.tim} {
          from {margin-left: 0;}
          to {margin-left: -${this.wrapWidth}px;}
        }
      `
      let el = document.createElement('style')
      el.innerHTML = style
      document.head.appendChild(el)
    }
  }
}
</script>

<style scoped>
*{
  padding: 0;
  margin: 0;
}
.scroll-inner {
  width: 100%;
  white-space: nowrap;
  overflow: hidden;
  height: 100%;
}
.scroll-wrap {
  box-sizing: border-box;
  min-width: 100%;
  height: 100%;
  font-size: 0;
  white-space: nowrap;
  display: inline-block;
}
.scroll-wrap li {
  height: 30px;
  line-height: 30px;
  list-style: none;
  display: inline-block;
  font-size: 16px;
  margin-right: 20px;
}
.scroll {
  display: inline-block;
}
</style>

 

2. 斷點滾動

/* 間歇滾動 */
<template>
  <div class="scroll-inner">
    <ul class="scroll-wrap" :style="scrollWrapClass" v-bind="$attrs">
      <li v-for="(item, index) in runArr" :key="index">
        {{item}}
      </li>
    </ul>
  </div>
</template>

<script>
/**
 * 根據inner標籤的高度可控制單行多行
 * 
 * scrollArr                       滾動陣列
 * time                 2000       滾動間隔
 * animationTime        500        滾動動畫時間
 * distance             30         滾動距離
 * 
 */
export default {
  data () {
    return {
      isRun: false,
      runArr: []
    }
  },
  computed: {
    scrollWrapClass () {
      let c
      if (this.isRun) {
        c = {
          transition: `margin ${this.animationTime / 1000}s`,
          marginTop: `-${this.distance}`
        }
      } else {
        c = {
          transition: '',
          marginTop: ''
        } 
      }
      return c
    }
  },
  props: {
    // 滾動陣列
    scrollArr: {
      required: true
    },
    // 滾動間隔
    time: {
      type: Number,
      default: 2000
    },
    // 滾動動畫時間
    animationTime: {
      type: Number,
      default: 500
    },
    // 滾動距離
    distance: {
      type: String,
      default: '30px'
    }
  },
  mounted() {
    this.runArr = JSON.parse(JSON.stringify(this.scrollArr ))
    document.addEventListener('visibilitychange', this.handleVisiable)
    this.startScroll()
  },
  methods: {
    startScroll () {
      this.interval = setInterval (() => {
        this.isRun = true
        this.runArr.push(this.runArr[0])
        this.timeOut = setTimeout (() => {
          this.runArr.shift()
          this.isRun = false
        }, this.animationTime)
      }, this.time)
    },
    handleVisiable (e) {
      if (e.target.visibilityState === 'visible') {  
        // 要執行的方法
        this.startScroll()
      } else {
        this.interval && clearInterval(this.interval)
      }
    }
  },
  destroyed () {
    this.interval && clearInterval(this.interval)
    this.timeOut && clearTimeout(this.timeOut)
  }
}
</script>

<style scoped>
*{
  padding: 0;
  margin: 0;
}
.scroll-wrap {
  box-sizing: border-box;
}
.scroll-wrap li {
  height: 30px;
  line-height: 30px;
  list-style: none;
}
.scroll-inner {
  overflow: hidden;
}
.scroll-wrap.active {
  transition: margin 0.5s;
  margin-top: -30px;
}
</style>

3. 無縫滾動

/* 無縫滾動 */
<template>
  <div class="scroll-inner" ref="scrollInner">
    <ul class="scroll-wrap" v-bind="$attrs" :class="{canPause: canPause}" :style="wrapStyle" ref="scrollWrap">
      <li v-for="item in runArr" :key="item">
        {{item}}
      </li>
    </ul>
    <ul class="scroll-wrap" v-if="canRun">
      <li v-for="item in runArr" :key="item">
        {{item}}
      </li>
    </ul>
  </div>
</template>

<script>
/**
 * scrollArr              滾動陣列
 * speed          1       滾動速度
 * canPause       false    滑鼠劃過停止
 */
export default {
  data () {
    return {
      runArr: [],
      wrapHeight: 0,
      innerHeight: 0,
      retry: 0,
      canRun: true,
      tim: Math.floor(Math.random() * (99999 - 10000) + 10000)
    }
  },
  props: {
    // 滾動陣列
    scrollArr: {
      required: true
    },
    // 滾動速度
    speed: {
      type: Number,
      default: 1
    },
    // 滑鼠劃過停止
    canPause: {
      type: Boolean,
      default: false
    }
  },
  computed: {
    wrapStyle () {
      const s = this.wrapHeight / (20 + this.speed * 5)
      return {
        animationDuration: `${s}s`,
        animationIterationCount: 'infinite',
        animationName: `move${this.tim}`,
        animationTimingFunction: 'linear'
      }
    }
  },
  mounted () {
    this.runArr = JSON.parse(JSON.stringify(this.scrollArr))
    this.getWrapHeight()
  },
  methods: {
    getWrapHeight () {
      this.$nextTick(() => {
        this.wrapHeight = this.$refs.scrollWrap.clientHeight
        this.innerHeight = this.$refs.scrollInner.clientHeight
        if (!this.wrapHeight && this.retry < 3) {
          this.getWrapHeight()
          this.retry++
          return
        } else if (!this.wrapHeight && this.retry === 3){
          console.error('獲取元素高度失敗或高度為0')
          return
        }
        if (this.innerHeight >= this.wrapHeight) {
          this.canRun = false
          return
        }
        this.createStyle()
      })
    },
    createStyle () {
      const style = `
        @keyframes move${this.tim} {
          from {margin-top: 0;}
          to {margin-top: -${this.wrapHeight}px;}
        }
      `
      let el = document.createElement('style')
      el.innerHTML = style
      document.head.appendChild(el)
    }
  }
}
</script>

<style lang="less" scoped>
*{
  padding: 0;
  margin: 0;
}
.scroll-wrap {
  box-sizing: border-box;
}
.canPause {
  &:hover{
    animation-play-state: paused;
  }
}
.scroll-wrap li {
  height: 30px;
  line-height: 30px;
  list-style: none;
}
.scroll-inner {
  overflow: hidden;
  position: relative;
  height: 100%;
}


</style>

 

相關文章