vue-seamless-scroll 结束从头开始,加延时后滚动

news2025/6/6 2:48:33

今天遇到一个大屏需求:
1️⃣初始进入页面停留5秒,然后开始滚动

2️⃣最后一条数据出现在最后一行时候暂停5秒,然后返回1️⃣

依次循环,发现vue-seamless-scroll的方法 ScrollEnd是监测最后一条数据消失在第一行才回调,不能满足我的需求,于是在node_modules里面找到他的文件重写

主要优化点

// 上

 if (Math.abs(this.yPos) >= this.realBoxHeight - 520) 

this.realBoxHeight是你所有数据加起来的行高, 520是可视区域的行高, yPos是向上滚动的距离,是个负值,滚动了一行,假设行高40,那就是  -40

 // 到达底部时暂停5秒

              this._stopMove()

              setTimeout(() => {

                this.yPos = 0

                // 重置位置后也暂停5秒再开始滚动

                setTimeout(() => {

                  this._startMove()

                }, 5000)

              }, 5000)

我修改完成的文件

<template>
  <div ref="wrap">
    <div
      :style="leftSwitch"
      v-if="navigation"
      :class="leftSwitchClass"
      @click="leftSwitchClick"
    >
      <slot name="left-switch"></slot>
    </div>
    <div
      :style="rightSwitch"
      v-if="navigation"
      :class="rightSwitchClass"
      @click="rightSwitchClick"
    >
      <slot name="right-switch"></slot>
    </div>
    <div
      ref="realBox"
      :style="pos"
      @mouseenter="enter"
      @mouseleave="leave"
      @touchstart="touchStart"
      @touchmove="touchMove"
      @touchend="touchEnd"
    >
      <div ref="slotList" :style="float">
        <slot></slot>
      </div>
      <div v-html="copyHtml" :style="float"></div>
    </div>
  </div>
</template>

<script>
require('comutils/animationFrame')()
const arrayEqual = require('comutils/arrayEqual')
const copyObj = require('comutils/copyObj')
export default {
  name: 'vue-seamless-scroll',
  data() {
    return {
      xPos: 0,
      yPos: 0,
      delay: 0,
      copyHtml: '',
      height: 0,
      width: 0, // 外容器宽度
      realBoxWidth: 0 // 内容实际宽度
    }
  },
  props: {
    data: {
      type: Array,
      default: () => {
        return []
      }
    },
    classOption: {
      type: Object,
      default: () => {
        return {}
      }
    }
  },
  computed: {
    leftSwitchState() {
      return this.xPos < 0
    },
    rightSwitchState() {
      return Math.abs(this.xPos) < this.realBoxWidth - this.width
    },
    leftSwitchClass() {
      return this.leftSwitchState ? '' : this.options.switchDisabledClass
    },
    rightSwitchClass() {
      return this.rightSwitchState ? '' : this.options.switchDisabledClass
    },
    leftSwitch() {
      return {
        position: 'absolute',
        margin: `${this.height / 2}px 0 0 -${this.options.switchOffset}px`,
        transform: 'translate(-100%,-50%)'
      }
    },
    rightSwitch() {
      return {
        position: 'absolute',
        margin: `${this.height / 2}px 0 0 ${this.width +
          this.options.switchOffset}px`,
        transform: 'translateY(-50%)'
      }
    },
    float() {
      return this.isHorizontal
        ? { float: 'left', overflow: 'hidden' }
        : { overflow: 'hidden' }
    },
    pos() {
      return {
        transform: `translate(${this.xPos}px,${this.yPos}px)`,
        transition: `all ${this.ease} ${this.delay}ms`,
        overflow: 'hidden'
      }
    },
    defaultOption() {
      return {
        step: 0.25, //步长
        limitMoveNum: 14, //启动无缝滚动最小数据数
        hoverStop: true, //是否启用鼠标hover控制
        direction: 1, // 0 往下 1 往上 2向左 3向右
        openTouch: true, //开启移动端touch
        singleHeight: 0, //单条数据高度有值hoverStop关闭
        singleWidth: 0, //单条数据宽度有值hoverStop关闭
        waitTime: 1000, //单步停止等待时间
        switchOffset: 30,
        autoPlay: true,
        navigation: false,
        switchSingleStep: 134,
        switchDelay: 400,
        switchDisabledClass: 'disabled',
        isSingleRemUnit: false // singleWidth/singleHeight 是否开启rem度量
      }
    },
    options() {
      return copyObj({}, this.defaultOption, this.classOption)
    },
    navigation() {
      return this.options.navigation
    },
    autoPlay() {
      if (this.navigation) return false
      return this.options.autoPlay
    },
    scrollSwitch() {
      return this.data.length >= this.options.limitMoveNum
    },
    hoverStopSwitch() {
      return this.options.hoverStop && this.autoPlay && this.scrollSwitch
    },
    canTouchScroll() {
      return this.options.openTouch
    },
    isHorizontal() {
      return this.options.direction > 1
    },
    baseFontSize() {
      return this.options.isSingleRemUnit
        ? parseInt(
            window.getComputedStyle(document.documentElement, null).fontSize
          )
        : 1
    },
    realSingleStopWidth() {
      return this.options.singleWidth * this.baseFontSize
    },
    realSingleStopHeight() {
      return this.options.singleHeight * this.baseFontSize
    },
    step() {
      let singleStep
      let step = this.options.step
      if (this.isHorizontal) {
        singleStep = this.realSingleStopWidth
      } else {
        singleStep = this.realSingleStopHeight
      }
      if (singleStep > 0 && singleStep % step > 0) {
        console.error(
          '如果设置了单步滚动,step需是单步大小的约数,否则无法保证单步滚动结束的位置是否准确。~~~~~'
        )
      }
      return step
    }
  },
  methods: {
    reset() {
      this._cancle()
      this._initMove()
    },
    leftSwitchClick() {
      if (!this.leftSwitchState) return
      // 小于单步距离
      if (Math.abs(this.xPos) < this.options.switchSingleStep) {
        this.xPos = 0
        return
      }
      this.xPos += this.options.switchSingleStep
    },
    rightSwitchClick() {
      if (!this.rightSwitchState) return
      // 小于单步距离
      if (
        this.realBoxWidth - this.width + this.xPos <
        this.options.switchSingleStep
      ) {
        this.xPos = this.width - this.realBoxWidth
        return
      }
      this.xPos -= this.options.switchSingleStep
    },
    _cancle() {
      cancelAnimationFrame(this.reqFrame || '')
    },
    touchStart(e) {
      if (!this.canTouchScroll) return
      let timer
      const touch = e.targetTouches[0] //touches数组对象获得屏幕上所有的touch,取第一个touch
      const { waitTime, singleHeight, singleWidth } = this.options
      this.startPos = {
        x: touch.pageX,
        y: touch.pageY
      } //取第一个touch的坐标值
      this.startPosY = this.yPos //记录touchStart时候的posY
      this.startPosX = this.xPos //记录touchStart时候的posX
      if (!!singleHeight && !!singleWidth) {
        if (timer) clearTimeout(timer)
        timer = setTimeout(() => {
          this._cancle()
        }, waitTime + 20)
      } else {
        this._cancle()
      }
    },
    touchMove(e) {
      //当屏幕有多个touch或者页面被缩放过,就不执行move操作
      if (
        !this.canTouchScroll ||
        e.targetTouches.length > 1 ||
        (e.scale && e.scale !== 1)
      )
        return
      const touch = e.targetTouches[0]
      const { direction } = this.options
      this.endPos = {
        x: touch.pageX - this.startPos.x,
        y: touch.pageY - this.startPos.y
      }
      event.preventDefault() //阻止触摸事件的默认行为,即阻止滚屏
      const dir = Math.abs(this.endPos.x) < Math.abs(this.endPos.y) ? 1 : 0 //dir,1表示纵向滑动,0为横向滑动
      if (dir === 1 && direction < 2) {
        // 表示纵向滑动 && 运动方向为上下
        this.yPos = this.startPosY + this.endPos.y
      } else if (dir === 0 && direction > 1) {
        // 为横向滑动 && 运动方向为左右
        this.xPos = this.startPosX + this.endPos.x
      }
    },
    touchEnd() {
      if (!this.canTouchScroll) return
      let timer
      const direction = this.options.direction
      this.delay = 50
      if (direction === 1) {
        if (this.yPos > 0) this.yPos = 0
      } else if (direction === 0) {
        let h = (this.realBoxHeight / 2) * -1
        if (this.yPos < h) this.yPos = h
      } else if (direction === 2) {
        if (this.xPos > 0) this.xPos = 0
      } else if (direction === 3) {
        let w = this.realBoxWidth * -1
        if (this.xPos < w) this.xPos = w
      }
      if (timer) clearTimeout(timer)
      timer = setTimeout(() => {
        this.delay = 0
        this._move()
      }, this.delay)
    },
    enter() {
      if (this.hoverStopSwitch) this._stopMove()
    },
    leave() {
      if (this.hoverStopSwitch) this._startMove()
    },
    _move() {
      // 鼠标移入时拦截_move()
      if (this.isHover) return
      this._cancle() //进入move立即先清除动画 防止频繁touchMove导致多动画同时进行
      this.reqFrame = requestAnimationFrame(
        function() {
          const h = this.realBoxHeight / 2 //实际高度
          const w = this.realBoxWidth / 2 //宽度
          let { direction, waitTime } = this.options
          let { step } = this
          if (direction === 1) {
            // 上
            if (Math.abs(this.yPos) >= this.realBoxHeight - 520) {
              this.$emit('ScrollEnd')
              // 到达底部时暂停5秒
              this._stopMove()
              setTimeout(() => {
                this.yPos = 0
                // 重置位置后也暂停5秒再开始滚动
                setTimeout(() => {
                  this._startMove()
                }, 5000)
              }, 5000)
              return
            }
            this.yPos -= step
          } else if (direction === 0) {
            // 下
            if (this.yPos >= 0) {
              this.$emit('ScrollEnd')
              // 到达顶部时暂停5秒
              this._stopMove()
              setTimeout(() => {
                this.yPos = h * -1
                // 重置位置后也暂停5秒再开始滚动
                setTimeout(() => {
                  this._startMove()
                }, 5000)
              }, 5000)
              return
            }
            this.yPos += step
          } else if (direction === 2) {
            // 左
            if (Math.abs(this.xPos) >= w) {
              this.$emit('ScrollEnd')
              // 到达左边界时暂停5秒
              this._stopMove()
              setTimeout(() => {
                this.xPos = 0
                // 重置位置后也暂停5秒再开始滚动
                setTimeout(() => {
                  this._startMove()
                }, 5000)
              }, 5000)
              return
            }
            this.xPos -= step
          } else if (direction === 3) {
            // 右
            if (this.xPos >= 0) {
              this.$emit('ScrollEnd')
              // 到达右边界时暂停5秒
              this._stopMove()
              setTimeout(() => {
                this.xPos = w * -1
                // 重置位置后也暂停5秒再开始滚动
                setTimeout(() => {
                  this._startMove()
                }, 5000)
              }, 5000)
              return
            }
            this.xPos += step
          }

          // 移除单步滚动逻辑,改为连续滚动
          this._move()
        }.bind(this)
      )
    },
    _initMove() {
      this.$nextTick(() => {
        const { switchDelay } = this.options
        const { autoPlay, isHorizontal } = this
        this._dataWarm(this.data)
        this.copyHtml = '' //清空copy
        if (isHorizontal) {
          this.height = this.$refs.wrap.offsetHeight
          this.width = this.$refs.wrap.offsetWidth
          let slotListWidth = this.$refs.slotList.offsetWidth
          this.$refs.realBox.style.width = slotListWidth + 'px'
          this.realBoxWidth = slotListWidth
        }

        if (autoPlay) {
          this.ease = 'ease-in'
          this.delay = 0
        } else {
          this.ease = 'linear'
          this.delay = switchDelay
          return
        }

        // 是否可以滚动判断
        if (this.scrollSwitch) {
          let timer
          if (timer) clearTimeout(timer)
          this.realBoxHeight = this.$refs.realBox.offsetHeight
          // 初始化时暂停5秒后开始滚动
          setTimeout(() => {
            this._move()
          }, 5000)
        } else {
          this._cancle()
          this.yPos = this.xPos = 0
        }
      })
    },
    _dataWarm(data) {
      if (data.length > 100) {
        console.warn(
          `数据达到了${data.length}条有点多哦~,可能会造成部分老旧浏览器卡顿。`
        )
      }
    },
    _startMove() {
      this.isHover = false //开启_move
      this._move()
    },
    _stopMove() {
      this.isHover = true //关闭_move
      this._cancle()
    }
  },
  watch: {
    data(newData, oldData) {
      this._dataWarm(newData)
      // 每次数据更新都重置滚动
      this.yPos = 0
      this.xPos = 0
      this._stopMove()
      // 5秒后开始新的滚动
      setTimeout(() => {
        if (this.data.length >= this.options.limitMoveNum) {
          this._initMove()
          this._startMove()
        }
      }, 5000)
    },
    autoPlay(bol) {
      if (bol) {
        this.reset()
      } else {
        this._stopMove()
      }
    }
  },
  beforeCreate() {
    this.reqFrame = null // move动画的animationFrame定时器
    this.isHover = false // mouseenter mouseleave 控制this._move()的开关
    this.ease = 'ease-in'
  },
  beforeDestroy() {
    this._cancle()
  }
}
</script>

源文件

<template>
  <div ref="wrap">
    <div
      :style="leftSwitch"
      v-if="navigation"
      :class="leftSwitchClass"
      @click="leftSwitchClick"
    >
      <slot name="left-switch"></slot>
    </div>
    <div
      :style="rightSwitch"
      v-if="navigation"
      :class="rightSwitchClass"
      @click="rightSwitchClick"
    >
      <slot name="right-switch"></slot>
    </div>
    <div
      ref="realBox"
      :style="pos"
      @mouseenter="enter"
      @mouseleave="leave"
      @touchstart="touchStart"
      @touchmove="touchMove"
      @touchend="touchEnd"
    >
      <div ref="slotList" :style="float">
        <slot></slot>
      </div>
      <div v-html="copyHtml" :style="float"></div>
    </div>
  </div>
</template>

<script>
  require('comutils/animationFrame')()
  const arrayEqual = require('comutils/arrayEqual')
  const copyObj = require('comutils/copyObj')
  export default {
    name: 'vue-seamless-scroll',
    data () {
      return {
        xPos: 0,
        yPos: 0,
        delay: 0,
        copyHtml: '',
        height: 0,
        width: 0, // 外容器宽度
        realBoxWidth: 0, // 内容实际宽度
      }
    },
    props: {
      data: {
        type: Array,
        default: () => {
          return []
        }
      },
      classOption: {
        type: Object,
        default: () => {
          return {}
        }
      }
    },
    computed: {
      leftSwitchState () {
        return this.xPos < 0
      },
      rightSwitchState () {
        return Math.abs(this.xPos) < (this.realBoxWidth - this.width)
      },
      leftSwitchClass () {
        return this.leftSwitchState ? '' : this.options.switchDisabledClass
      },
      rightSwitchClass () {
        return this.rightSwitchState ? '' : this.options.switchDisabledClass
      },
      leftSwitch () {
        return {
          position: 'absolute',
          margin: `${this.height / 2}px 0 0 -${this.options.switchOffset}px`,
          transform: 'translate(-100%,-50%)'
        }
      },
      rightSwitch () {
        return {
          position: 'absolute',
          margin: `${this.height / 2}px 0 0 ${this.width + this.options.switchOffset}px`,
          transform: 'translateY(-50%)'
        }
      },
      float () {
        return this.isHorizontal ? { float: 'left', overflow: 'hidden' } : { overflow: 'hidden' }
      },
      pos () {
        return {
          transform: `translate(${this.xPos}px,${this.yPos}px)`,
          transition: `all ${this.ease} ${this.delay}ms`,
          overflow: 'hidden'
        }
      },
      defaultOption () {
        return {
          step: 1, //步长
          limitMoveNum: 5, //启动无缝滚动最小数据数
          hoverStop: true, //是否启用鼠标hover控制
          direction: 1, // 0 往下 1 往上 2向左 3向右
          openTouch: true, //开启移动端touch
          singleHeight: 0, //单条数据高度有值hoverStop关闭
          singleWidth: 0, //单条数据宽度有值hoverStop关闭
          waitTime: 1000, //单步停止等待时间
          switchOffset: 30,
          autoPlay: true,
          navigation: false,
          switchSingleStep: 134,
          switchDelay: 400,
          switchDisabledClass: 'disabled',
          isSingleRemUnit: false // singleWidth/singleHeight 是否开启rem度量
        }
      },
      options () {
        return copyObj({}, this.defaultOption, this.classOption)
      },
      navigation () {
        return this.options.navigation
      },
      autoPlay () {
        if (this.navigation) return false
        return this.options.autoPlay
      },
      scrollSwitch () {
        return this.data.length >= this.options.limitMoveNum
      },
      hoverStopSwitch () {
        return this.options.hoverStop && this.autoPlay && this.scrollSwitch
      },
      canTouchScroll () {
        return this.options.openTouch
      },
      isHorizontal () {
        return this.options.direction > 1
      },
      baseFontSize () {
        return this.options.isSingleRemUnit ? parseInt(window.getComputedStyle(document.documentElement, null).fontSize) : 1
      },
      realSingleStopWidth () {
        return this.options.singleWidth * this.baseFontSize
      },
      realSingleStopHeight () {
        return this.options.singleHeight * this.baseFontSize
      },
      step () {
        let singleStep
        let step = this.options.step
        if (this.isHorizontal) {
          singleStep = this.realSingleStopWidth
        } else {
          singleStep = this.realSingleStopHeight
        }
        if (singleStep > 0 && singleStep % step > 0) {
          console.error('如果设置了单步滚动,step需是单步大小的约数,否则无法保证单步滚动结束的位置是否准确。~~~~~')
        }
        return step
      }
    },
    methods: {
      reset () {
        this._cancle()
        this._initMove()
      },
      leftSwitchClick () {
        if (!this.leftSwitchState) return
        // 小于单步距离
        if (Math.abs(this.xPos) < this.options.switchSingleStep) {
          this.xPos = 0
          return
        }
        this.xPos += this.options.switchSingleStep
      },
      rightSwitchClick () {
        if (!this.rightSwitchState) return
        // 小于单步距离
        if ((this.realBoxWidth - this.width + this.xPos) < this.options.switchSingleStep) {
          this.xPos = this.width - this.realBoxWidth
          return
        }
        this.xPos -= this.options.switchSingleStep
      },
      _cancle () {
        cancelAnimationFrame(this.reqFrame || '')
      },
      touchStart (e) {
        if (!this.canTouchScroll) return
        let timer
        const touch = e.targetTouches[0] //touches数组对象获得屏幕上所有的touch,取第一个touch
        const { waitTime, singleHeight, singleWidth } = this.options
        this.startPos = {
          x: touch.pageX,
          y: touch.pageY
        } //取第一个touch的坐标值
        this.startPosY = this.yPos //记录touchStart时候的posY
        this.startPosX = this.xPos //记录touchStart时候的posX
        if (!!singleHeight && !!singleWidth) {
          if (timer) clearTimeout(timer)
          timer = setTimeout(() => {
            this._cancle()
          }, waitTime + 20)
        } else {
          this._cancle()
        }
      },
      touchMove (e) {
        //当屏幕有多个touch或者页面被缩放过,就不执行move操作
        if (!this.canTouchScroll || e.targetTouches.length > 1 || e.scale && e.scale !== 1) return
        const touch = e.targetTouches[0]
        const { direction } = this.options
        this.endPos = {
          x: touch.pageX - this.startPos.x,
          y: touch.pageY - this.startPos.y
        }
        event.preventDefault(); //阻止触摸事件的默认行为,即阻止滚屏
        const dir = Math.abs(this.endPos.x) < Math.abs(this.endPos.y) ? 1 : 0 //dir,1表示纵向滑动,0为横向滑动
        if (dir === 1 && direction < 2) {  // 表示纵向滑动 && 运动方向为上下
          this.yPos = this.startPosY + this.endPos.y
        } else if (dir === 0 && direction > 1) { // 为横向滑动 && 运动方向为左右
          this.xPos = this.startPosX + this.endPos.x
        }
      },
      touchEnd () {
        if (!this.canTouchScroll) return
        let timer
        const direction = this.options.direction
        this.delay = 50
        if (direction === 1) {
          if (this.yPos > 0) this.yPos = 0
        } else if (direction === 0) {
          let h = this.realBoxHeight / 2 * -1
          if (this.yPos < h) this.yPos = h
        } else if (direction === 2) {
          if (this.xPos > 0) this.xPos = 0
        } else if (direction === 3) {
          let w = this.realBoxWidth * -1
          if (this.xPos < w) this.xPos = w
        }
        if (timer) clearTimeout(timer)
        timer = setTimeout(() => {
          this.delay = 0
          this._move()
        }, this.delay)
      },
      enter () {
        if (this.hoverStopSwitch) this._stopMove()
      },
      leave () {
        if (this.hoverStopSwitch) this._startMove()
      },
      _move () {
        // 鼠标移入时拦截_move()
        if (this.isHover) return
        this._cancle() //进入move立即先清除动画 防止频繁touchMove导致多动画同时进行
        this.reqFrame = requestAnimationFrame(
          function () {
            const h = this.realBoxHeight / 2  //实际高度
            const w = this.realBoxWidth / 2 //宽度
            let { direction, waitTime } = this.options
            let { step } = this
            if (direction === 1) { // 上
              if (Math.abs(this.yPos) >= h) {
                this.$emit('ScrollEnd')
                this.yPos = 0
              }
              this.yPos -= step
            } else if (direction === 0) { // 下
              if (this.yPos >= 0) {
                this.$emit('ScrollEnd')
                this.yPos = h * -1
              }
              this.yPos += step
            } else if (direction === 2) { // 左
              if (Math.abs(this.xPos) >= w) {
                this.$emit('ScrollEnd')
                this.xPos = 0
              }
              this.xPos -= step
            } else if (direction === 3) { // 右
              if (this.xPos >= 0) {
                this.$emit('ScrollEnd')
                this.xPos = w * -1
              }
              this.xPos += step
            }
            if (this.singleWaitTime) clearTimeout(this.singleWaitTime)
            if (!!this.realSingleStopHeight) { //是否启动了单行暂停配置
              if (Math.abs(this.yPos) % this.realSingleStopHeight < step) { // 符合条件暂停waitTime
                this.singleWaitTime = setTimeout(() => {
                  this._move()
                }, waitTime)
              } else {
                this._move()
              }
            } else if (!!this.realSingleStopWidth) {
              if (Math.abs(this.xPos) % this.realSingleStopWidth < step) { // 符合条件暂停waitTime
                this.singleWaitTime = setTimeout(() => {
                  this._move()
                }, waitTime)
              } else {
                this._move()
              }
            } else {
              this._move()
            }
          }.bind(this)
        )
      },
      _initMove () {
        this.$nextTick(() => {
          const { switchDelay } = this.options
          const { autoPlay, isHorizontal } = this
          this._dataWarm(this.data)
          this.copyHtml = '' //清空copy
          if (isHorizontal) {
            this.height = this.$refs.wrap.offsetHeight
            this.width = this.$refs.wrap.offsetWidth
            let slotListWidth = this.$refs.slotList.offsetWidth
            // 水平滚动设置warp width
            if (autoPlay) {
              // 修正offsetWidth四舍五入
              slotListWidth = slotListWidth * 2 + 1
            }
            this.$refs.realBox.style.width = slotListWidth + 'px'
            this.realBoxWidth = slotListWidth
          }

          if (autoPlay) {
            this.ease = 'ease-in'
            this.delay = 0
          } else {
            this.ease = 'linear'
            this.delay = switchDelay
            return
          }

          // 是否可以滚动判断
          if (this.scrollSwitch) {
            let timer
            if (timer) clearTimeout(timer)
            this.copyHtml = this.$refs.slotList.innerHTML
            setTimeout(() => {
              this.realBoxHeight = this.$refs.realBox.offsetHeight
              this._move()
            }, 0);
          } else {
            this._cancle()
            this.yPos = this.xPos = 0
          }
        })
      },
      _dataWarm (data) {
        if (data.length > 100) {
          console.warn(`数据达到了${data.length}条有点多哦~,可能会造成部分老旧浏览器卡顿。`);
        }
      },
      _startMove () {
        this.isHover = false //开启_move
        this._move()
      },
      _stopMove () {
        this.isHover = true //关闭_move
        // 防止频频hover进出单步滚动,导致定时器乱掉
        if (this.singleWaitTime) clearTimeout(this.singleWaitTime)
        this._cancle()
      },
    },
    mounted () {
      this._initMove()
    },
    watch: {
      data (newData, oldData) {
        this._dataWarm(newData)
        //监听data是否有变更
        if (!arrayEqual(newData, oldData)) {
          this.reset()
        }
      },
      autoPlay (bol) {
        if (bol) {
          this.reset()
        } else {
          this._stopMove()
        }
      }
    },
    beforeCreate () {
      this.reqFrame = null // move动画的animationFrame定时器
      this.singleWaitTime = null // single 单步滚动的定时器
      this.isHover = false // mouseenter mouseleave 控制this._move()的开关
      this.ease = 'ease-in'
    },
    beforeDestroy () {
      this._cancle()
      clearTimeout(this.singleWaitTime)
    }
  }
</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2397867.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

0-EATSA-GNN:基于图节点分类师生机制的边缘感知和两阶段注意力增强图神经网络(code)

code:https://github.com/afofanah/EATSA-GNN. 文章目录 Abstract1. Introduction1.1.动态图场景1.2.EATSA-GNN框架的背景化2. Background2.1.GNN边缘感知挑战2.2.GNN的可解释性问题2.3.EATSA-GNN可解释性3. Related worksAbstract 图神经网络(GNNs)从根本上改变了我们处理和…

配置前端控制器

一、DispatcherServlet 详解 在使用 Spring MVC 框架构建 Web 应用时&#xff0c;DispatcherServlet是整个请求处理流程的核心。本文将深入解析DispatcherServlet的作用、工作原理及其在 Spring MVC 架构中的关键地位。 1.DispatcherServlet 是什么&#xff1f; DispatcherS…

lua注意事项

感觉是lua的一大坑啊&#xff0c;它还不如函数内部就局部变量呢 注意函数等内部&#xff0c;全部给加上local得了

Git的三种合并方式

在 Gitee&#xff08;码云&#xff09;中合并分支主要有三种方式&#xff1a;​普通合并&#xff08;Merge Commit&#xff09;、压缩合并&#xff08;Squash Merge&#xff09;​和变基合并&#xff08;Rebase Merge&#xff09;​。每种方式适用于不同的场景&#xff0c;各有…

从零到一:我的技术博客导航(持续更新)

作者&#xff1a;冰茶 最后更新&#xff1a;2025年6月3日 本文收录了我的C#编程学习心得与技术探索&#xff0c;将持续更新 前言 作为一名.NET开发者&#xff0c;C#语言的学习与探索一直是我技术成长的核心路径。本文集整理了我在C#学习过程中的思考与实践&#xff0c;希望能够…

SpringBoot整合Flowable【08】- 前后端如何交互

引子 在第02篇中&#xff0c;我通过 Flowable-UI 绘制了一个简单的绩效流程&#xff0c;并在后续章节中基于这个流程演示了 Flowable 的各种API调用。然而&#xff0c;在实际业务场景中&#xff0c;如果要求前端将用户绘制的流程文件发送给后端再进行解析处理&#xff0c;这种…

【五模型时间序列预测对比】Transformer-LSTM、Transformer、CNN-LSTM、LSTM、CNN

【五模型时间序列预测对比】Transformer-LSTM、Transformer、CNN-LSTM、LSTM、CNN 目录 【五模型时间序列预测对比】Transformer-LSTM、Transformer、CNN-LSTM、LSTM、CNN预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Transformer-LSTM、Transformer、CNN-LSTM、LSTM、…

深入了解MCP基础与架构

一、引言 在人工智能技术以指数级速度渗透各行业领域的今天&#xff0c;我们正站在一个关键的技术拐点。当ChatGPT月活突破亿级、Gemini Pro实现多模态实时交互、Claude 3.5 Sonnet突破百万上下文长度&#xff0c;这些里程碑事件背后&#xff0c;一个崭新的大门逐步打开&#…

实验设计与分析(第6版,Montgomery)第5章析因设计引导5.7节思考题5.13 R语言解题

本文是实验设计与分析&#xff08;第6版&#xff0c;Montgomery著&#xff0c;傅珏生译) 第5章析因设计引导5.7节思考题5.13 R语言解题。主要涉及方差分析&#xff0c;正态假设检验&#xff0c;残差分析&#xff0c;交互作用图。 dataframe<-data.frame( yc(36,18,30,39,20…

【java面试】MySQL篇

MySQL篇 一、总体结构二、优化&#xff08;一&#xff09;定位慢查询1.1 开源工具1.2Mysql自带的慢日志查询1.3 总结 &#xff08;二&#xff09;定位后优化2.1 优化2.2 总结 &#xff08;三&#xff09;索引3.1 索引3.2 索引底层数据结构——B树3.3 总结 &#xff08;四&#…

贪心算法应用:欧拉路径(Fleury算法)详解

Java中的贪心算法应用&#xff1a;欧拉路径&#xff08;Fleury算法&#xff09;详解 一、欧拉路径与欧拉回路基础 1.1 基本概念 欧拉路径&#xff08;Eulerian Path&#xff09;是指在一个图中&#xff0c;经过图中每一条边且每一条边只经过一次的路径。如果这条路径的起点和…

【算法设计与分析】实验——二维0-1背包问题(算法分析题:算法思路),独立任务最优调度问题(算法实现题:实验过程,描述,小结)

说明&#xff1a;博主是大学生&#xff0c;有一门课是算法设计与分析&#xff0c;这是博主记录课程实验报告的内容&#xff0c;题目是老师给的&#xff0c;其他内容和代码均为原创&#xff0c;可以参考学习&#xff0c;转载和搬运需评论吱声并注明出处哦。 要求&#xff1a;3-…

【Git】View Submitted Updates——diff、show、log

在 Git 中查看更新的内容&#xff08;即工作区、暂存区或提交之间的差异&#xff09;是日常开发中的常见操作。以下是常用的命令和场景说明&#xff1a; 文章目录 1、查看工作区与暂存区的差异2、查看提交历史中的差异3、查看工作区与最新提交的差异4、查看两个提交之间的差异5…

deepseek原理和项目实战笔记2 -- deepseek核心架构

混合专家&#xff08;MoE&#xff09; ​​混合专家&#xff08;Mixture of Experts, MoE&#xff09;​​ 是一种机器学习模型架构&#xff0c;其核心思想是通过组合多个“专家”子模型&#xff08;通常为小型神经网络&#xff09;来处理不同输入&#xff0c;从而提高模型的容…

在 MATLAB 2015a 中如何调用 Python

在 MATLAB 2015a 中调用 Python 可通过系统命令调用、.NET 交互层包装、MEX 接口间接桥接、环境变量配置四种方式&#xff0c;但因该版本对 Python 支持有限&#xff0c;主要依赖的是系统命令调用与间接脚本交互。其中&#xff0c;通过 system() 函数调用 Python 脚本是最简单且…

房屋租赁系统 Java+Vue.js+SpringBoot,包括房屋类型、房屋信息、预约看房、合同信息、房屋报修、房屋评价、房主管理模块

房屋租赁系统 JavaVue.jsSpringBoot&#xff0c;包括房屋类型、房屋信息、预约看房、合同信息、房屋报修、房屋评价、房主管理模块 百度云盘链接&#xff1a;https://pan.baidu.com/s/1KmwOFzN9qogyaLQei3b6qw 密码&#xff1a;l2yn 摘 要 社会的发展和科学技术的进步&#xf…

华为OD机试真题——生成哈夫曼树(2025B卷:100分)Java/python/JavaScript/C/C++/GO六种最佳实现

2025 B卷 100分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析; 并提供Java、python、JavaScript、C++、C语言、GO六种语言的最佳实现方式! 本文收录于专栏:《2025华为OD真题目录+全流程解析/备考攻略/经验分享》 华为OD机试真题《生成…

Redis最佳实践——性能优化技巧之监控与告警详解

Redis 在电商应用的性能优化技巧之监控与告警全面详解 一、监控体系构建 1. 核心监控指标矩阵 指标类别关键指标计算方式/说明健康阈值&#xff08;参考值&#xff09;内存相关used_memoryINFO Memory 获取不超过 maxmemory 的 80%mem_fragmentation_ratio内存碎片率 used_m…

R3GAN训练自己的数据集

简介 简介&#xff1a;这篇论文挑战了"GANs难以训练"的广泛观点&#xff0c;通过提出一个更稳定的损失函数和现代化的网络架构&#xff0c;构建了一个简洁而高效的GAN基线模型R3GAN。作者证明了通过合适的理论基础和架构设计&#xff0c;GANs可以稳定训练并达到优异…

【容器docker】启动容器kibana报错:“message“:“Error: Cannot find module ‘./logs‘

说明&#xff1a; 1、服务器数据盘挂了&#xff0c;然后将以前的数据用rsync拷贝过去&#xff0c;启动容器kibana服务&#xff0c;报错信息如下图所示&#xff1a; 2、可能是拷贝docker文件夹&#xff0c;有些文件没有拷贝过去&#xff0c;导致无论是给文件夹授权用户kibana或者…