let { default: styles } = require('./video.css') interface IComponent { templateContainer: HTMLElement; init: () => void; template: () => void; handle: () => void; } interface IOption { ele: string | HTMLElement; url: string; height?: string; width?: string; autoplay?: boolean; poster?: string; } class Video implements IComponent { templateContainer; constructor(private settings: IOption) { this.settings = Object.assign({ height: '100%', '100%', autoplay: false }, settings) this.init() } init() { this.template() this.handle() } template() { this.templateContainer = document.createElement('div') this.templateContainer.className = styles.video this.templateContainer.style.width = this.settings.width this.templateContainer.style.height = this.settings.height this.templateContainer.innerHTML = ` <video class="${styles['video-content']}" src="${this.settings.url}"></video> <div class="${styles['video-content-play']}"> <i class="iconfont iconplay"></i> </div> <div class="${styles['video-controls']}"> <div class="${styles['video-progress']}"> <div class="${styles['video-progress-now']}"></div> <div class="${styles['video-progress-suc']}"></div> <div class="${styles['video-progress-bar']}"></div> <div class="${styles['video-progress-track']}"></div> </div> <div class="${styles['video-play']}"> <i class="iconfont icon-iconplay"></i> </div> <div class="${styles['video-time']}"> <span>00:00</span> / <span>00:00</span> </div> <div class="${styles['video-full']}"> <i class="iconfont iconfullscreen"></i> </div> <div class="${styles['video-volume']}"> <i class="iconfont iconvolume"></i> <div class="${styles['video-volprogress']}"> <div class="${styles['video-volprogress-now']}"></div> <div class="${styles['video-volprogress-bar']}"></div> </div> </div> </div> ` if (typeof this.settings.ele === 'object') { this.settings.ele.appendChild(this.templateContainer) } else { document.querySelector(`.${this.settings.ele}`).appendChild(this.templateContainer) } if (this.settings.poster) { let videoNode: HTMLVideoElement = this.templateContainer.querySelector('video') videoNode.setAttribute('poster', this.settings.poster) } } handle() { let timer; let videoContent: HTMLVideoElement = this.templateContainer.querySelector(`.${styles['video-content']}`) let videoControls: HTMLElement = this.templateContainer.querySelector(`.${styles['video-controls']}`) let videoProgress = this.templateContainer.querySelectorAll(`.${styles['video-progress']} div`) let videoTime = this.templateContainer.querySelectorAll(`.${styles['video-time']} span`) let videoPlay = this.templateContainer.querySelector(`.${styles['video-play']} i`) let videoContentPlay = this.templateContainer.querySelector(`.${styles['video-content-play']} i`) let videoVolume = this.templateContainer.querySelectorAll(`.${styles['video-volprogress']} div`) let videoVolumeIcon: HTMLElement = this.templateContainer.querySelector(`.${styles['video-volume']} i`) let videoVolumeScale: number = 0.5; videoVolumeIcon.style.cursor = 'pointer' let videoFullScreen = this.templateContainer.querySelector(`.${styles['video-full']} i`) if (this.settings.autoplay) { timer = setInterval(playing, 1000) videoContent.play() videoContent.volume = videoVolumeScale } videoContent.addEventListener('canplay', () => { videoTime[1].innerHTML = formatTime(videoContent.duration) }) videoContent.addEventListener('play', () => { videoPlay.className = 'iconfont iconpause' videoContentPlay.parentNode.style.display = 'none' timer = setInterval(playing, 1000) }); videoContent.addEventListener('ended', (event) => { videoContentPlay.parentNode.style.display = 'block' clearInterval(timer) }) videoContent.addEventListener('pause', () => { videoPlay.className = 'iconfont iconplay' videoContentPlay.parentNode.style.display = 'block' clearInterval(timer) }); videoPlay.addEventListener('click', (e: MouseEvent) => { if (videoContent.paused) { videoContent.play() } else { videoContent.pause() } e.preventDefault() e.stopPropagation() }) videoContentPlay.addEventListener('click', () => { videoPlay.click() }) // 播放进度的控制 videoProgress[2].addEventListener('mousedown', function (e: MouseEvent) { let downX = e.pageX // 按下点的x坐标 let downL = this.offsetLeft // 到当前有定位的父元素节点的左偏移 document.onmousemove = (ev: MouseEvent) => { let scale = (ev.pageX - downX + downL + 8) / this.parentNode.offsetWidth; changeVideoProgress(scale) } document.onmouseup = () => { document.onmousemove = document.onmouseup = null } e.preventDefault() }) videoProgress[3].addEventListener('click', function (e: MouseEvent) { let scale = e.offsetX / this.parentNode.offsetWidth changeVideoProgress(scale) }) // 音量的控制 videoVolume[1].addEventListener('mousedown', function (e: MouseEvent) { let downX = e.pageX // 按下点的x坐标 let downL = this.offsetLeft + 7 // 到当前有定位的父节点的左偏移 document.onmousemove = (ev: MouseEvent) => { let pageX = ev.pageX videoVolumeScale = (pageX - downX + downL) / this.parentNode.offsetWidth videoVolumeScale > 1 && (videoVolumeScale = 1) if (videoVolumeScale <= 0) { videoVolumeScale = 0 videoVolumeIcon.className = 'iconfont iconmute' } else { videoVolumeIcon.className = 'iconfont iconvolume' } this.style.left = videoVolumeScale * 100 + '%' videoVolume[0].style.width = videoVolumeScale * 100 + '%' videoContent.volume = videoVolumeScale } document.onmouseup = () => { document.onmousemove = document.onmouseup = null } e.preventDefault() }) // 静音操作 videoVolumeIcon.addEventListener('click', function (e) { if (videoContent.volume > 0) { videoContent.volume = 0 this.className = 'iconfont iconmute' } else { this.className = 'iconfont iconvolume' videoContent.volume = videoVolumeScale } e.preventDefault() }) // 全屏操作 videoFullScreen.addEventListener('click', function (e: MouseEvent) { videoContent.requestFullscreen() }) this.templateContainer.addEventListener('mouseenter', (e: MouseEvent) => { videoControls.style.bottom = 0 + 'px' }) this.templateContainer.addEventListener('mouseleave', (e: MouseEvent) => { videoControls.style.bottom = -50 + 'px' }) // 点击视频区域改变播放状态 videoContent.addEventListener('click', (e: MouseEvent) => { if (videoContent.paused) { videoContent.play() } else { videoContent.pause() } e.preventDefault() e.stopPropagation() }) function playing() { let scale = videoContent.currentTime / videoContent.duration let scaleSuc = videoContent.buffered.end(0) / videoContent.duration; videoProgress[0].style.width = scale * 100 + '%'; videoProgress[1].style.width = scaleSuc * 100 + '%'; videoProgress[2].style.left = scale * 100 + '%'; videoTime[0].innerHTML = formatTime(videoContent.currentTime) } function changeVideoProgress(scale) { scale < 0 && (scale = 0); scale > 1 && (scale = 1); videoProgress[0].style.width = scale * 100 + '%' videoProgress[1].style.width = scale * 100 + '%' videoProgress[2].style.left = scale * 100 + '%' videoContent.currentTime = scale * videoContent.duration videoContent.paused && videoContent.play() } function formatTime(number: number): string { number = Math.round(number); let min = Math.floor(number / 60); let sec = Math.floor(number % 60); return setZero(min) + ':' + setZero(sec); } function setZero(number: number): string { if (number < 10) { return '0' + number; } return '' + number; } } } function video(option: IOption) { return new Video(option) } export default video
.video { position: relative; overflow: hidden; } .video-content { width: 100%; height: 100%; object-fit: cover; } .video-controls { position: absolute; width: 100%; bottom: -50px; left: 0; height: 50px; background-color: rgba(0, 0, 0, 0.8); transition: 0.5s; } .video-progress { position: relative; width: 100%; height: 5px; background-color: #222223; } .video-progress-now { width: 0; height: 100%; background: #ff6a03; position: absolute; left: 0; z-index: 1; } .video-progress-suc { width: 0; height: 100%; background: #666; position: absolute; left: 0; } .video-progress-bar { width: 14px; height: 14px; background: white; border-radius: 50%; position: absolute; left: 0; top: 0; margin-left: -7px; margin-top: -5px; z-index: 2; cursor: pointer; z-index: 12; } .video-progress-track { height: 9px; width: 100%; position: absolute; left: 0; top: -2px; z-index: 11; } .video-progress-now { width: 0; height: 100%; background: #ff6a03; position: absolute; left: 0; z-index: 1; } .video-play { float: left; height: 45px; line-height: 45px; margin-left: 35px; cursor: pointer; } .video-play i { color: #fff; font-size: 20px; } .video-content-play { width: 50px; height: 50px; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); cursor: pointer; } .video-content-play i { color: #fff; font-size: 30px; } .video-time { float: left; height: 45px; line-height: 45px; margin-left: 30px; color: #fff; } .video-full { float: right; height: 45px; line-height: 45px; margin-right: 20px; } .video-full i { font-size: 20px; color: #fff; cursor: pointer; } .video-volume { float: right; display: flex; align-items: center; margin-right: 30px; height: 45px; } .video-volume i { font-size: 20px; color: white; margin-right: 20px; } .video-volprogress { width: 100px; height: 5px; background: #222223; position: relative; } .video-volprogress-now { width: 50%; height: 100%; background: #ff6a03; } .video-volprogress-bar { height: 14px; width: 14px; background: #fff; border-radius: 50%; position: absolute; left: 50%; top: 0; margin-left: -7px; margin-top: -5px; z-index: 2; cursor: pointer; }