如何利用css实现文字滚动

使用js实现文字滚动总感觉不是那么顺畅,使用VUE使用css动画做了一下,动态的添加@keyframes。

HorizontalTextScroll.vue 横向文本滚动

/* 横向文本滚动 */
<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>

IntervalTextScroll.vue间歇文本滚动

/* 间歇滚动 */
<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>

SeamlessTextScroll.vue无缝滚动

/* 无缝滚动 */
<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>
© 版权声明
THE END
喜欢就支持一下吧
点赞0赞助作者
分享
评论 抢沙发

请登录后发表评论