document.addEventListener('DOMContentLoaded', function () { const sv = { containerCls: '.mainBnr', containerEl: null, swiper: null, swiperLength: 0, swiperDuration: 5000, swiperPaginationType: 'bullets', frame: {}, device: 'pc', volume: { status: 'mute', val: 50, min: 0, max: 100 }, init: () => { sv.containerEl = document.querySelector(sv.containerCls) if (sv.containerEl === null) { return } sv.utils.checkDevice() sv.methods.initSwiper(); sv.swiperLength = sv.swiper.slides.length sv.methods.buildPlayer(); sv.methods.resize() }, methods: { initSwiper: () => { sv.swiper = new Swiper(sv.containerCls + ' .swiper-container', { slidesPerView: 1, /*effect : 'fade',*/ loop: true, loopAdditionalSlides: 1, spaceBetween: 16, observer: true, observeParents: true, on: { init: () => { const aSlide= document.querySelectorAll('.swiper-slide'); aSlide.forEach(slide=>{ const oTxt = slide.querySelector('.swiper-slide-text') if (! oTxt || ! oTxt.getAttribute('animation-name')) { return } const animationName = oTxt.getAttribute('animation-name') if (slide.classList.contains('swiper-slide-active')){ oTxt.classList.add(animationName) oTxt.classList.add('animated') } else { oTxt.classList.remove(animationName) oTxt.classList.remove('animated') } }) }, resize: () => { console.log('resize') }, slideChange: () => { sv.methods.startPlayer() }, slideChangeTransitionEnd: () =>{ const aSlide= document.querySelectorAll('.swiper-slide'); aSlide.forEach(slide=>{ const oTxt = slide.querySelector('.swiper-slide-text') if (! oTxt || ! oTxt.getAttribute('animation-name')) { return } const animationName = oTxt.getAttribute('animation-name') if (slide.classList.contains('swiper-slide-active')){ oTxt.classList.add(animationName) oTxt.classList.add('animated') } else { oTxt.classList.remove(animationName) oTxt.classList.remove('animated') } }) }, paginationRender: () => { const aBullet = document.querySelectorAll('.swiper-pagination-bullet'); aBullet.forEach(bullet => { const observer = new MutationObserver(function(mutationsList) { for (const mutation of mutationsList) { if (mutation.type === 'attributes' && mutation.attributeName === 'class' && bullet.classList.contains('swiper-pagination-bullet-active')) { sv.utils.handlePaginationChange() } } }); observer.observe(bullet, { attributes: true, attributeOldValue: true }); }) const aProgress = document.querySelectorAll('.swiper-pagination-bullet-progress') aProgress.forEach(progress => { const observer = new MutationObserver(function(mutationsList) { for (const mutation of mutationsList) { if (mutation.type === 'attributes' && mutation.attributeName === 'data-duration' && progress.closest('.swiper-pagination-bullet').classList.contains('swiper-pagination-bullet-active')) { sv.utils.handlePaginationChange() } } }); observer.observe(progress, { attributes: true, attributeOldValue: true }); }) } }, autoplay: { delay: sv.swiperDuration, disableOnInteraction: false }, navigation: { nextEl: sv.containerCls + ' .swiper-button-next', prevEl: sv.containerCls + ' .swiper-button-prev', }, pagination: { el: sv.containerCls + ' .swiper-pagination', clickable: true, type: sv.swiperPaginationType, renderBullet: function (index, className) { return '' + '' + '' + '' + '' } } }) }, buildPlayer: () => { let hasVideo = false const oVol = sv.containerEl.querySelector('.video-player-volume') sv.swiper.wrapperEl.querySelectorAll('.swiper-slide').forEach((oSlide, oSlideIdx) => { const videoEl = oSlide.querySelector('.video') const aVd = oSlide.querySelectorAll('video') if (aVd.length === 0) { return } // first slide contains video if (oSlide.classList.contains('swiper-slide-active')) { sv.swiper.autoplay.stop() oVol.classList.add('show') } hasVideo = true const aCls = aVd.length === 2 ? ['pc', 'mo'] : ['normal'] oSlide.classList.add('video-slide') aVd.forEach((vd, idx) => { const id = vd.innerHTML.trim(); const type = sv.utils.detectPlatform(id) const oFrame = sv.utils.createVideoEl(aCls[idx]) const device = vd.getAttribute('device') const previewImg = oSlide.querySelector(`img.${device}`) videoEl.appendChild(oFrame) const videoFrameId = `video-frame-id-${oSlideIdx}-${device}` switch(type) { case 'youtube': sv.utils.buildYoutube(id, oFrame, videoFrameId) //previewImg.setAttribute('src', `https://img.youtube.com/vi/${id}/maxresdefault.jpg`) oSlide.setAttribute('video-type', 'youtube') break; case 'vimeo': sv.utils.buildVimeo(id, oFrame, videoFrameId) /* fetch(`https://vimeo.com/api/v2/video/${id}.json`) .then(response => response.json()) .then(data => { console.log(data[0].thumbnail_large) previewImg.setAttribute('src', data[0].thumbnail_large) }).catch(error => console.error('Error:', error)); */ oSlide.setAttribute('video-type', 'vimeo') break; } }) }) // add volume btn if (hasVideo) { oVol.querySelector('.video-player-volume-down')?.addEventListener('click', () => { sv.utils.setPlayerVol('down') }) oVol.querySelector('.video-player-volume-toggle')?.addEventListener('click', () => { oVol.querySelector('.video-player-volume-toggle').classList.toggle('unmute') sv.utils.setPlayerVol('toggle') }) oVol.querySelector('.video-player-volume-up')?.addEventListener('click', () => { sv.utils.setPlayerVol('up') }) } }, startPlayer: () => { const slide = sv.swiper.slides[sv.swiper.activeIndex] const oVol = sv.containerEl.querySelector('.video-player-volume') sv.methods.stopPlayer() if (slide.classList.contains('video-slide') === false) { sv.swiper.autoplay.start() oVol.classList.remove('show') return } oVol.classList.add('show') sv.swiper.autoplay.stop() const videoType = slide.getAttribute('video-type') const aVideo = slide.querySelectorAll('.video_frame') let targetVideo = null; if (aVideo.length === 2) { targetVideo = Object.values(slide.querySelectorAll('.video_frame')).find(a => a.getAttribute('data-device') === sv.device) } else { targetVideo = Object.values(slide.querySelectorAll('.video_frame'))[0] } const player = sv.frame[targetVideo.getAttribute('video-id')] switch (videoType) { case 'youtube': sv.utils.startYoutube(player) break; case 'vimeo': sv.utils.startVimeo(player) break; } }, stopPlayer: () => { Object.keys(sv.frame).forEach(key => { const player = sv.frame[key] const stream = player.player_type switch(stream) { case 'youtube': sv.utils.stopYoutube(player) break; case 'vimeo': sv.utils.stopVimeo(player) break; } }) }, resize: () => { window.addEventListener('resize', function () { sv.utils.checkDevice() }) } }, utils: { buildVimeo: (id, element, videoFrameId) => { const videoFrame = element element.setAttribute('video-id', videoFrameId) const options = { id: id, width: sv.swiper.width, height: sv.swiper.height, loop: false, autoplay: true, muted: true, background: true }; sv.frame[videoFrameId] = new Vimeo.Player(element, options); const widthPromise = sv.frame[videoFrameId].getVideoWidth(); const heightPromise = sv.frame[videoFrameId].getVideoHeight(); const durationPromise = sv.frame[videoFrameId].getDuration(); Promise.all([ widthPromise, heightPromise, durationPromise ]).then(([width, height, duration]) => { sv.utils.calcFrameSize(element, { width, height, duration }) sv.utils.setVideoDuration(element.closest('.swiper-slide'), duration, element.getAttribute('data-device')) }).catch(function(error) { console.error("Error occurred:", error); }); sv.frame[videoFrameId].pause().then(() => { const slide = element.closest('.swiper-slide') sv.frame[videoFrameId].player_type = 'vimeo' //sv.utils.changePlayerVolStatus(sv.frame[videoFrameId], 'vimeo') if (slide.classList.contains('swiper-slide-active') === false || [sv.device, 'normal'].includes(videoFrame.getAttribute('data-device')) === false) { return } sv.utils.startVimeo(sv.frame[videoFrameId]) }) sv.frame[videoFrameId].on('ended', () => { const nextIdx = (sv.swiper.activeIndex + 1) % sv.swiperLength sv.frame[videoFrameId].setCurrentTime(1) sv.swiper.slideTo(nextIdx); }) }, buildYoutube: (id, element, videoFrameId) => { const youtubeFrame = document.createElement('div') element.appendChild(youtubeFrame) element.setAttribute('video-id', videoFrameId) sv.frame[videoFrameId] = new YT.Player(youtubeFrame, { width: sv.swiper.width, height: sv.swiper.height, videoId: id, playerVars: { controls: 0, modestbranding: 1, disablekb: 1, rel: 0, showinfo: 0, fs: 0, }, events: { onReady: (event) => { const videoFrame = event.target['g'].closest('.video_frame') sv.frame[videoFrameId].player_type = 'youtube' //sv.utils.changePlayerVolStatus(event.target, 'youtube') const slide = element.closest('.swiper-slide') event.target.player_type = 'youtube' const duration = sv.frame[videoFrameId].getDuration() sv.utils.setVideoDuration(slide, duration, videoFrame.getAttribute('data-device')) const { width, height } = sv.frame[videoFrameId].getSize() sv.utils.calcFrameSize(videoFrame, { width, height, duration }) if (slide.classList.contains('swiper-slide-active') === false || [sv.device, 'normal'].includes(videoFrame.getAttribute('data-device')) === false) { return } event.target.mute() sv.utils.startYoutube(event.target) }, onStateChange: (event) => { if (event.data === YT.PlayerState.ENDED) { const nextIdx = (sv.swiper.activeIndex + 1) % sv.swiperLength event.target.seekTo(1) sv.swiper.slideTo(nextIdx); } if (event.data === YT.PlayerState.PLAYING) { setTimeout(() => { const videoFrame = event.target['g'].closest('.video_frame') const { width, height } = sv.frame[videoFrameId]['playerInfo']['videoContentRect'] const duration = sv.frame[videoFrameId].getDuration() sv.utils.calcFrameSize(videoFrame, { width, height, duration }) }, 2e2) } } } }) }, removeAllEl: (target, tagName) => { target.querySelectorAll(tagName).forEach(el => { el.remove() }) }, createVideoEl: device => { const oDiv = document.createElement('div') oDiv.classList.add('video_frame') oDiv.setAttribute('data-device', device) return oDiv }, setVideoDuration: (slide, duration, device) => { const slideIdx = slide.getAttribute('data-swiper-slide-index') const pagination = sv.containerEl.querySelector(`.swiper-pagination-bullet[slide-index="${slideIdx}"] .swiper-pagination-bullet-progress[data-device="${device}"]`) if (! pagination) { return } pagination.setAttribute('data-duration', parseInt(duration) * 1000) }, calcFrameSize: (target, data) => { const { width, height, duration } = data const iframe = target.querySelector('iframe'); const videoEl = target.closest('.video') const iSwiperWidth = parseInt(sv.swiper.wrapperEl.offsetWidth) const iSwiperHeight = parseInt(sv.swiper.wrapperEl.offsetHeight) const iSwiperAspectRatio = iSwiperWidth / iSwiperHeight; videoEl.style.width = `100%` videoEl.style.aspectRatio = `${iSwiperWidth} / ${iSwiperHeight}` const iVideoAspectRatio = width / height; let newWidth, newHeight if (iSwiperAspectRatio > iVideoAspectRatio) { newWidth = '100%'; newHeight = 'auto' } else { newHeight = '100%'; newWidth = 'auto' } iframe.style.width = `${newWidth}` iframe.style.height = `${newHeight}` iframe.style.aspectRatio = `${width} / ${height}` }, stopVimeo: player => { player.pause() }, stopYoutube: player => { player.pauseVideo() }, startVimeo: player => { player.setCurrentTime(1) player.play().then(function() { sv.utils.removeAllEl(player.element.closest('.swiper-slide'), 'img') }).catch(function(error) { console.error('vimeo player play error', error) }) }, startYoutube: player => { sv.utils.removeAllEl(player.g.closest('.swiper-slide'), 'img') player.seekTo(0); player.playVideo() console.log('start play') }, checkDevice: () => { sv.device = document.documentElement.clientWidth > 768 ? 'pc' : 'mo' console.log('device', sv.device) sv.containerEl.setAttribute('data-device', sv.device) }, setPlayerVol: (type = 'switch') => { let iVol = 0 let sStatus = sv.volume.status switch(type) { case 'toggle': if (sStatus === 'mute') { sStatus = 'unmute' iVol = sv.volume.val } else { sStatus = 'mute' } break; case 'up': iVol = sv.volume.val if (iVol === sv.volume.max) { return } iVol += 10 if (iVol >= sv.volume.max) { iVol = sv.volume.max } break; case 'down': iVol = sv.volume.val if (iVol === sv.volume.min) { return } iVol -= 10 if (iVol <= sv.volume.min) { iVol = sv.volume.min } break; } Object.keys(sv.frame).forEach(key => { const player = sv.frame[key] const playerType = player.player_type sv.utils.changePlayerVol(player, playerType, iVol, sStatus) }) }, changePlayerVol: (player, playerType, vol, status) => { const statusParam = sv.volume.status const bMute = status === 'mute' switch (playerType) { case 'vimeo': if (status !== statusParam) { player.setMuted(bMute).then(function (muted) { if (!muted) { //sv.utils.changePlayerVol(player, playerType) } // muted was turned on }).catch(function (error) { // an error occurred }); } player.setVolume(vol / 100).then(() => { sv.volume.val = vol console.log(`Volume increased to ${sv.volume.val}`) }) break; case 'youtube': if (status !== statusParam) { if (status === 'mute') { player.mute() } else { player.unMute() } } player.setVolume(vol); sv.volume.val = vol break; } }, changePlayerVolStatus: (player, playerType) => { const status = sv.volume.status switch (playerType) { case 'vimeo': const bMute = status === 'mute' console.log('bMute', bMute) player.setMuted(bMute).then(function(muted) { if (! muted) { sv.utils.changePlayerVol(player, playerType) } // muted was turned on }).catch(function(error) { // an error occurred }); break; case 'youtube': if (status === 'mute') { player.mute() } else { sv.utils.changePlayerVol(player, playerType) player.unMute() } break; } }, detectPlatform: (videoId) => { if (/^[a-zA-Z0-9_-]{11}$/.test(videoId)) { return 'youtube'; } else if (/^\d{8,9}$/.test(videoId)) { return 'vimeo'; } else { return 'unknown'; } }, handlePaginationChange: () => { switch (sv.swiperPaginationType) { case 'bullets': const aProgress = sv.containerEl.querySelectorAll('.swiper-pagination-bullet-progress') aProgress.forEach(progress => { progress.style.transition = 'none' progress.style.width = '0%' }) const oProgress = sv.containerEl.querySelector('.swiper-pagination-bullet-active .swiper-pagination-bullet-progress[data-device="' + sv.device + '"]') if (oProgress) { setTimeout(() => { // fix first render oProgress.style.transition = 'width ' + oProgress.getAttribute('data-duration') + 'ms linear' oProgress.style.width = '100%' }, 50) } break; } } } } sv.init(); });