vvcomponent
Version:
VV组件
319 lines (318 loc) • 11.4 kB
JavaScript
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);