UNPKG

vvcomponent

Version:
319 lines (318 loc) 11.4 kB
class aVideoPlayer extends HTMLElement { #playIcon() { const size = innerWidth > 500 ? 50 : 30; return `<svg width="${size}" height="${size}" viewBox="0 0 48 48" fill="white" xmlns="http://www.w3.org/2000/svg"><path d="M15 24V11.8756L25.5 17.9378L36 24L25.5 30.0622L15 36.1244V24Z" fill="none" stroke="white" stroke-width="4" stroke-linejoin="round"/></svg>`; } #pauseIcon() { const size = innerWidth > 500 ? 50 : 30; return `<svg width="${size}" height="${size}" viewBox="0 0 48 48" fill="white" xmlns="http://www.w3.org/2000/svg"><path d="M16 12V36" stroke="white" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><path d="M32 12V36" stroke="white" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/></svg>`; } #isHover = false; getVideo() { return this.shadowRoot.querySelector('video'); } #getContainer() { return this.shadowRoot.querySelector(".container"); } set src(value) { this.getVideo().src = value; } get src() { if (!this.getVideo()) return null; return this.getVideo().src; } set bgcolor(value) { this.#getContainer().style.backgroundColor = value; } get bgcolor() { if (!this.#getContainer()) return null; return this.#getContainer().style.backgroundColor; } set progress(value) { this.getVideo().currentTime = value; } get progress() { return this.getVideo().currentTime; } constructor() { super(); this.attachShadow({ mode: 'open' }); this.render(); } static get observedAttributes() { return ['bgcolor', 'src']; } attributeChangedCallback(name, oldValue, newValue) { console.log(name, oldValue, newValue); if (name == "src") { this.shadowRoot.querySelector('video').src = newValue; } else if (name == "bgcolor") { this.#getContainer().style.backgroundColor = newValue; } } render() { let isShowIcon = true; const dispatchEvent = (name, detail) => { const Event = new CustomEvent(name, { detail: detail, }); this.dispatchEvent(Event); }; const style = document.createElement('style'); style.textContent = this.#getStyle(); style.setAttribute("iftc-style", "video-player"); this.shadowRoot.appendChild(style); const container = document.createElement('div'); container.classList.add('container'); container.style.backgroundColor = this.bgcolor; const icon = document.createElement('div'); icon.classList.add('icon'); icon.innerHTML = this.#playIcon; container.appendChild(icon); container.addEventListener('mouseover', e => { this.#isHover = true; icon.style.display = 'flex'; }); container.addEventListener('mouseout', e => { this.#isHover = false; if (video.paused) return; icon.style.display = 'none'; }); const video = document.createElement('video'); video.setAttribute('src', this.src); video.setAttribute('autoplay', ''); video.setAttribute('muted', ''); video.setAttribute('loop', ''); container.appendChild(video); if (globalThis.Slider) { const slider = document.createElement('iftc-slider'); slider.classList.add('slider'); slider.setAttribute('min', '0'); slider.setAttribute('max', '0'); slider.setAttribute('value', '0'); slider.setAttribute('step', '0.0001'); video.addEventListener('loadedmetadata', () => { slider.setAttribute('max', video.duration); slider.setAttribute('value', video.currentTime); }); video.addEventListener('timeupdate', e => { slider.setAttribute('value', video.currentTime); }); slider.addEventListener("start", e => { video.pause(); }); slider.addEventListener("change", e => { console.log(slider.value); video.currentTime = slider.value; video.play(); }); container.appendChild(slider); } else { console.warn(document.createElement("iftc-slider"), "is not defined, please import /class/Slider.js first."); } video.addEventListener('loadedmetadata', e => { dispatchEvent("loadedmetadata", e); }); video.addEventListener('play', e => { icon.innerHTML = this.#pauseIcon(); dispatchEvent("play", e); }); video.addEventListener('pause', e => { icon.innerHTML = this.#playIcon(); dispatchEvent("pause", e); }); video.addEventListener('ended', e => { dispatchEvent("ended", e); }); video.addEventListener('error', e => { dispatchEvent("error", e); }); video.addEventListener('timeupdate', e => { dispatchEvent("timeupdate", e); }); video.addEventListener('canplay', e => { dispatchEvent("canplay", e); }); video.addEventListener('canplaythrough', e => { dispatchEvent("canplaythrough", e); }); video.addEventListener('waiting', e => { dispatchEvent("waiting", e); }); video.addEventListener('playing', e => { dispatchEvent("playing", e); }); video.addEventListener('loadeddata', e => { dispatchEvent("loadeddata", e); }); addEventListener("keydown", e => { if (e.key === " ") { if (video.paused) { video.play(); } else { video.pause(); } } }); icon.addEventListener("click", e => { if (video.paused) { video.play(); } else { video.pause(); } }); this.shadowRoot.appendChild(container); this.addEventListener("contextmenu", e => { e.preventDefault(); const oldMenu = this.shadowRoot.querySelector(".menu"); if (oldMenu) { oldMenu.remove(); } const menu = document.createElement('div'); menu.classList.add('menu'); menu.style.position = "fixed"; menu.style.top = e.clientY + "px"; menu.style.left = e.clientX + "px"; menu.style.zIndex = "9999"; menu.style.backgroundColor = "white"; menu.style.borderRadius = "5px"; menu.style.padding = "10px"; menu.style.boxShadow = "0 0 10px rgba(0,0,0,0.5)"; menu.style.display = "flex"; menu.style.flexDirection = "column"; menu.style.gap = "10px"; menu.style.overflow = "auto"; menu.style.fontSize = "12px"; menu.style.fontFamily = "Arial"; menu.style.userSelect = "none"; const items = [{ text: "VV视频播放器", }, { text: "播放器版本: " + this.version, }, { text: "作者: " + this.author, }, { text: "全屏", click: () => { if (!this.fullscreen) { if (this.requestFullscreen) { this.requestFullscreen(); } else if (this.webkitRequestFullscreen) { this.webkitRequestFullscreen(); } else if (this.mozRequestFullScreen) { this.mozRequestFullScreen(); } else if (this.msRequestFullscreen) { this.msRequestFullscreen(); } this.fullscreen = true; return; } if (document.exitFullscreen) { document.exitFullscreen(); } else if (document.webkitExitFullscreen) { document.webkitExitFullscreen(); } else if (document.mozCancelFullScreen) { document.mozCancelFullScreen(); } else if (document.msExitFullscreen) { document.msExitFullscreen(); } this.fullscreen = false; } }]; for (const item of items) { const div = document.createElement('div'); div.textContent = item.text; if (item.click) { div.style.cursor = "pointer"; div.addEventListener('click', item.click); } menu.appendChild(div); } this.shadowRoot.appendChild(menu); }) this.shadowRoot.addEventListener("click", e => { const oldMenu = this.shadowRoot.querySelector(".menu"); if (oldMenu) { oldMenu.remove(); } }); addEventListener("resize", e => { icon.innerHTML = video.paused ? this.#playIcon() : this.#pauseIcon(); }); setInterval(() => { if (icon.style.display == "flex" && !video.paused && !this.#isHover) { icon.innerHTML = this.#pauseIcon(); icon.style.display = "none"; } }, 5000); } #getStyle() { return `.container { position: relative; display: flex; justify-content: center; align-items: center; width: 100%; height: 100%; } video { width: 100%; height: 100%; } .slider { position: absolute; bottom: 10px; left: 0; width: calc(100% - 22px); } .icon { width: auto; height: auto; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); display: flex; justify-content: center; align-items: center; cursor: pointer; background: rgba(0, 0, 0, 0.5); border-radius: 50%; padding: 10px; z-index: 10; }`; } get version() { return "2.0.0"; } play() { this.getVideo().play(); } pause() { this.getVideo().pause(); } stop() { this.getVideo().pause(); this.getVideo().currentTime = 0; } set volume(value) { this.getVideo().volume = value; } get volume() { return this.getVideo().volume; } get author() { return "IFTC"; } toString(type) { if (type == "progress") return this.#formatTime(this.progress); } #formatTime(time) { const hours = Math.floor(time / 3600).toString().padStart(2, "0"); const minutes = Math.floor(time / 60).toString().padStart(2, "0"); const seconds = Math.floor(time % 60).toString().padStart(2, "0"); const millisecond = Math.floor((time % 1) * 1000).toString().padStart(3, "0"); return `${hours}:${minutes}:${seconds}.${millisecond}`; } } customElements.define("iftc-video", aVideoPlayer);