如何根据轮播视频时长动态更新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
相关产品推荐
相关产品推荐

