You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何根据轮播视频时长动态更新setTimeout的timer值?

轮播组件自动切换时长匹配当前视频时长的解决方案

问题概述

实现了一个包含7个轮播项的轮播组件,每个轮播项以视频作为背景,轮播项依据timer值自动切换。目前timer值固定为第一个视频的时长,需要在切换轮播项时更新timer,使其匹配当前显示视频的时长。

解决步骤

  • 获取所有视频元素:替换单个视频变量为所有轮播项内的视频集合,方便切换时获取对应视频的时长。
  • 将timer改为可更新变量:把const timer改为let timer,允许后续动态修改值。
  • 动态更新timer值:在轮播切换到新项时,获取当前项对应视频的duration,并将其转换为毫秒赋值给timer。
  • 处理视频加载状态:确保视频元数据加载完成后再获取时长,避免出现NaN的情况。

修改后的完整代码

import '../styles/index.scss'

import '@/utils/easings'
import '@/utils/polyfill'
import '@/utils/sw'

import Application from '@/classes/App'

import gsap from 'gsap'

import Draggable from 'gsap/Draggable'

window.App = new Application()

document.querySelectorAll('.header__slider').forEach((container) => {
  // Setup variables
  const items = container.querySelectorAll('.header__slide')
  const itemsMax = items.length - 1
  const thumbnail = document.querySelector('.thumbnails')

  console.log(thumbnail)

  const svgContainer = document.querySelector('.header__thumbnail__svgs')

  const svg = document.querySelector('.header__thumbnail__svg')

  // 获取所有轮播项内的视频
  const allVideos = container.querySelectorAll('.header__slider__slide__video')
  let timer = 0 // 改为let,初始值设为0

  const duration = 1
  let autoplay
  let index = 0
  const move = 100

  // 监听第一个视频的元数据加载,设置初始timer
  if (allVideos.length > 0) {
    const firstVideo = allVideos[0]
    firstVideo.addEventListener('loadedmetadata', () => {
      timer = firstVideo.duration * 1000
      // 初始autoplay要在视频加载完成后设置
      if (!autoplay) {
        autoplay = window.setTimeout(slideLogic, timer)
      }
    })
    // 如果视频已经加载完成,直接设置
    if (firstVideo.readyState >= firstVideo.HAVE_METADATA) {
      timer = firstVideo.duration * 1000
    }
  }

  // --------------------------------//
  // 💾 Inital setup
  // --------------------------------//
  function svgActive (index) {
    const svgAll = svgContainer.querySelectorAll('svg')
    svgAll.forEach((svg) => {
      svg.classList.remove('active')
    })
    svgAll[index].classList.add('active')
  }

  function init () {
    // Create 🟣 Dots
    items.forEach((item, itemIndex) => {
      const number = document.createElement('button')
      number.innerHTML = itemIndex + 1

      if (itemIndex === 0) number.classList.add('active') // Set the first dot to active

      if (itemIndex === 0) svg.classList.add('active')

      // Allow clicking the dots
      number.addEventListener('click', () => {
        slideLogic(false, itemIndex)
      })

      if (thumbnail) thumbnail.appendChild(number)
    })
    // Animate the first element in
    gsap.set(items, {
      autoAlpha: 0
    })
    gsap.set(['.slider', items[index]], {
      autoAlpha: 1
    })
    gsap.from(items[index], {
      autoAlpha: 0,
      x: move
    })
  }
  // Run inital setup
  init()
  // END 💾 Inital setup --------------//

  // --------------------------------//
  // ☝️ Touch
  // --------------------------------//
  if (window.Draggable) {
    Draggable.create(items, {
      type: 'x', // Drag only on the X axis
      zIndexBoost: false,
      onDragStart: function () {
        window.clearTimeout(autoplay) // disable autoplay
        slideLogic(this.getDirection() === 'right')
      }
    })
  }
  // END ☝️ Touch  --------------//

  // --------------------------------//
  // Set active 🟣 dot
  // --------------------------------//
  function dotActive (currentIndex) {
    const dotsAll = thumbnail.querySelectorAll('button')
    dotsAll.forEach((dot) => {
      dot.classList.remove('active')
    })
    dotsAll[currentIndex].classList.add('active')

    const currentVideo = allVideos[currentIndex]
    // 播放当前视频
    currentVideo.play()
    // 更新timer为当前视频的时长
    currentVideo.addEventListener('loadedmetadata', () => {
      timer = currentVideo.duration * 1000
    })
    // 如果视频已加载完成,直接更新
    if (currentVideo.readyState >= currentVideo.HAVE_METADATA) {
      timer = currentVideo.duration * 1000
    }
  }
  // END Set active 🟣 dot --------------//

  // --------------------------------//
  // 🖼️ Slide animation
  // --------------------------------//
  function slideAnimation (currentIndex, moveIn, outIn) {
    // Reset the properties you are animating below
    // Ohter wise they animate from that positon (probalby not visable)
    gsap.set(items, {
      xPercent: 0,
      scale: 1
    })
    gsap.set(items[moveIn], {
      autoAlpha: 1
    })

    // The animation
    const tl = gsap.timeline({
      defaults: {
        duration
      },
      onStart: animationStart,
      onComplete: animationDone
    })
    // Move out slide
    tl.to(items[currentIndex], {
      xPercent: outIn[0]
    })
    // move in slide
    tl.from(items[moveIn], {
      xPercent: outIn[1]
    }, '<')
    // Animate text
    tl.set(items[currentIndex], {
      autoAlpha: 0
    }) // Hide moved out slide
  }

  function animationStart () {
    container.classList.add('running')
    // 停止上一个视频的播放并重置
    allVideos[index].pause()
    allVideos[index].currentTime = 0
  }

  function animationDone () {
    // 使用更新后的timer设置autoplay
    autoplay = window.setTimeout(slideLogic, timer)
    container.classList.remove('running')
    gsap.set(items, {
      x: 0
    })
  }
  // END 🖼️ Slide animation --------------//

  // --------------------------------//
  // Slider 🎛️ logic
  // --------------------------------//
  function slideLogic (prev, customMoveIn) {
    window.clearTimeout(autoplay) // disable autoplay
    const outIn = [-move, move]
    if (prev) outIn.reverse()
    let moveIn
    // Check if moveIn is passed with the function
    if (typeof customMoveIn === 'undefined') {
      if (prev) {
        moveIn = index === 0 ? itemsMax : index - 1
      } else {
        moveIn = index === itemsMax ? 0 : index + 1
      }
    } else {
      moveIn = customMoveIn
    }
    if (thumbnail) dotActive(moveIn) // Set active dot
    if (thumbnail) svgActive(moveIn) // Set active svg
    slideAnimation(index, moveIn, outIn) // Animation function
    // Changing the next index
    if (typeof customMoveIn === 'undefined') {
      if (prev) {
        index = index === 0 ? itemsMax : index - 1
      } else {
        index = index === itemsMax ? 0 : index + 1
      }
    } else {
      index = customMoveIn
    }
  }
})

关键修改点说明

  • 获取所有视频:使用allVideos变量存储所有轮播项内的视频元素,替代原来的单个video变量。
  • 动态更新timer:在dotActive函数中,获取当前轮播项对应的视频,监听其loadedmetadata事件确保能拿到正确的时长,同时处理视频已加载完成的情况,直接更新timer。
  • 初始timer设置:第一个视频的timer需要等其元数据加载完成后再设置,避免初始值为NaN。
  • 动画开始时处理旧视频:在animationStart中停止上一个视频的播放并重置进度,避免多个视频同时播放。

内容的提问来源于stack exchange,提问作者Raj Shukla

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 11:45:32