🚀 Init
This commit is contained in:
parent
9fd0228a37
commit
59e871a96f
16 changed files with 818 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# Generated
|
||||||
|
node_modules
|
||||||
|
test/.astro
|
||||||
|
test/node_modules
|
1
index.ts
Normal file
1
index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { default as Zorn } from './src/Player.astro'
|
20
package.json
Normal file
20
package.json
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"name": "@minpluto/zorn",
|
||||||
|
"author": "SudoVanilla",
|
||||||
|
"type": "module",
|
||||||
|
"version": "0.4.3",
|
||||||
|
"exports": {
|
||||||
|
".": "./index.ts"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"index.ts",
|
||||||
|
"src/*"
|
||||||
|
],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@iconoir/vue": "^7.8.0",
|
||||||
|
"astro": "^4.14.2"
|
||||||
|
}
|
||||||
|
}
|
36
src/Controls.astro
Normal file
36
src/Controls.astro
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
---
|
||||||
|
// Icons
|
||||||
|
import {
|
||||||
|
Backward15Seconds,
|
||||||
|
Enlarge,
|
||||||
|
Forward15Seconds,
|
||||||
|
PlaySolid,
|
||||||
|
} from "@iconoir/vue";
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="video-controls">
|
||||||
|
<div class="vc-top">
|
||||||
|
<p>The Mark On The Wall</p>
|
||||||
|
</div>
|
||||||
|
<div class="vc-bottom">
|
||||||
|
<div class="vc-start">
|
||||||
|
<button id="vc-playpause"><PlaySolid /></button>
|
||||||
|
<button id="vc-backwards"><Backward15Seconds /></button>
|
||||||
|
<button id="vc-forwards"><Forward15Seconds /></button>
|
||||||
|
</div>
|
||||||
|
<div class="vc-center">
|
||||||
|
<div class="vc-seek">
|
||||||
|
<span class="vc-progress-bar"></span>
|
||||||
|
<input class="seek" id="seek" value="0" min="0" type="range" step="1">
|
||||||
|
</div>
|
||||||
|
<p class="timestamp">
|
||||||
|
<span class="seek-tooltip" id="seek-tooltip">00:00</span>
|
||||||
|
<span id="current">00:00</span>
|
||||||
|
<span id="duration">00:00</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="vc-end">
|
||||||
|
<button id="vc-fullscreen"><Enlarge /></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
195
src/Controls/Controller.astro
Normal file
195
src/Controls/Controller.astro
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
<script is:inline>
|
||||||
|
var VideoContainer = document.querySelector('.video-container')
|
||||||
|
var VideoControls = document.querySelector('.video-controls')
|
||||||
|
var Player = document.querySelector('video')
|
||||||
|
|
||||||
|
// Icons
|
||||||
|
var play_solid_default = '<?xml version="1.0" encoding="UTF-8"?><svg width="24px" height="24px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="#ffffff" stroke-width="1.5"><path d="M6.90588 4.53682C6.50592 4.2998 6 4.58808 6 5.05299V18.947C6 19.4119 6.50592 19.7002 6.90588 19.4632L18.629 12.5162C19.0211 12.2838 19.0211 11.7162 18.629 11.4838L6.90588 4.53682Z" fill="#ffffff" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></svg>';
|
||||||
|
var pause_solid_default = '<?xml version="1.0" encoding="UTF-8"?><svg width="24px" height="24px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="#ffffff" stroke-width="1.5" data-darkreader-inline-color="" style="--darkreader-inline-color: #e8e6e3;"><path d="M6 18.4V5.6C6 5.26863 6.26863 5 6.6 5H9.4C9.73137 5 10 5.26863 10 5.6V18.4C10 18.7314 9.73137 19 9.4 19H6.6C6.26863 19 6 18.7314 6 18.4Z" fill="#ffffff" stroke="#ffffff" stroke-width="1.5" data-darkreader-inline-fill="" data-darkreader-inline-stroke="" style="--darkreader-inline-fill: #ffffff; --darkreader-inline-stroke: #ffffff;"></path><path d="M14 18.4V5.6C14 5.26863 14.2686 5 14.6 5H17.4C17.7314 5 18 5.26863 18 5.6V18.4C18 18.7314 17.7314 19 17.4 19H14.6C14.2686 19 14 18.7314 14 18.4Z" fill="#ffffff" stroke="#ffffff" stroke-width="1.5" data-darkreader-inline-fill="" data-darkreader-inline-stroke="" style="--darkreader-inline-fill: #ffffff; --darkreader-inline-stroke: #ffffff;"></path></svg>';
|
||||||
|
var PlayIcon = play_solid_default;
|
||||||
|
var PauseIcon = pause_solid_default;
|
||||||
|
|
||||||
|
// Fullscreen
|
||||||
|
function Fullscreen() {
|
||||||
|
const Button_Fullscreen = document.getElementById("vc-fullscreen");
|
||||||
|
function Toggle_Fullscreen() {
|
||||||
|
if (document.fullscreenElement) {
|
||||||
|
document.querySelector('.vc-top').style.opacity = '0'
|
||||||
|
document.exitFullscreen();
|
||||||
|
} else if (document.webkitFullscreenElement) {
|
||||||
|
document.querySelector('.vc-top').style.opacity = '0'
|
||||||
|
document.webkitExitFullscreen();
|
||||||
|
} else if (VideoContainer.webkitRequestFullscreen) {
|
||||||
|
document.querySelector('.vc-top').style.opacity = '1'
|
||||||
|
VideoContainer.webkitRequestFullscreen();
|
||||||
|
} else {
|
||||||
|
document.querySelector('.vc-top').style.opacity = '1'
|
||||||
|
VideoContainer.requestFullscreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button_Fullscreen.onclick = Toggle_Fullscreen;
|
||||||
|
function Update_FullscreenButton() {
|
||||||
|
if (document.fullscreenElement) {
|
||||||
|
Button_Fullscreen.setAttribute("data-title", "Exit full screen (f)");
|
||||||
|
} else {
|
||||||
|
Button_Fullscreen.setAttribute("data-title", "Full screen (f)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Player.addEventListener("dblclick", () => {
|
||||||
|
Toggle_Fullscreen()
|
||||||
|
Update_FullscreenButton()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Play/Pause
|
||||||
|
function PlayPause() {
|
||||||
|
const Button_PlayPause = document.querySelector(".video-controls #vc-playpause");
|
||||||
|
Button_PlayPause.addEventListener("click", Toggle_PlayPause);
|
||||||
|
Player.addEventListener("click", Toggle_PlayPause);
|
||||||
|
Player.addEventListener("play", Update_PlayPauseButton);
|
||||||
|
Player.addEventListener("pause", Update_PlayPauseButton);
|
||||||
|
function Toggle_PlayPause() {
|
||||||
|
if (Player.paused || Player.ended) {
|
||||||
|
Player.play();
|
||||||
|
} else {
|
||||||
|
Player.pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function Update_PlayPauseButton() {
|
||||||
|
if (Player.paused) {
|
||||||
|
Button_PlayPause.setAttribute("data-title", "Play (K)");
|
||||||
|
Button_PlayPause.innerHTML = `${PlayIcon}`;
|
||||||
|
} else {
|
||||||
|
Button_PlayPause.setAttribute("data-title", "Pause (K)");
|
||||||
|
Button_PlayPause.innerHTML = `${PauseIcon}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip Around
|
||||||
|
function SkipAround() {
|
||||||
|
const Button_SkipBack = document.querySelector(".video-controls #vc-backwards");
|
||||||
|
const Button_SkipForth = document.querySelector(".video-controls #vc-forwards");
|
||||||
|
Button_SkipBack.addEventListener("click", Toggle_SkipBack);
|
||||||
|
Button_SkipForth.addEventListener("click", Toggle_SkipForth);
|
||||||
|
function Toggle_SkipBack() {
|
||||||
|
Skip(-10);
|
||||||
|
}
|
||||||
|
function Toggle_SkipForth() {
|
||||||
|
Skip(10);
|
||||||
|
}
|
||||||
|
function Skip(value) {
|
||||||
|
Player.currentTime += value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide Controls
|
||||||
|
function AutoToggleControls() {
|
||||||
|
function Hide_Controls2() {
|
||||||
|
if (Player.paused) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
document.querySelector(".video-controls").classList.add("hide");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function Show_Controls2() {
|
||||||
|
document.querySelector(".video-controls").classList.remove("hide");
|
||||||
|
}
|
||||||
|
VideoControls.addEventListener("mouseenter", Show_Controls2);
|
||||||
|
VideoControls.addEventListener("mouseleave", Hide_Controls2);
|
||||||
|
var mouseTimer = null, cursorVisible = true;
|
||||||
|
function Hide_Cursor() {
|
||||||
|
mouseTimer = null;
|
||||||
|
VideoContainer.style.cursor = "none";
|
||||||
|
cursorVisible = false;
|
||||||
|
Hide_Controls2();
|
||||||
|
}
|
||||||
|
document.onmousemove = function () {
|
||||||
|
if (mouseTimer) {
|
||||||
|
window.clearTimeout(mouseTimer);
|
||||||
|
Show_Controls2();
|
||||||
|
}
|
||||||
|
if (!cursorVisible) {
|
||||||
|
VideoContainer.style.cursor = "default";
|
||||||
|
cursorVisible = true;
|
||||||
|
}
|
||||||
|
mouseTimer = window.setTimeout(Hide_Cursor, 3200);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keyboard Shortcuts
|
||||||
|
function KeyboardShortcuts(events) {
|
||||||
|
if (Player.hasAttribute("keyboard-shortcut-fullscreen")) {
|
||||||
|
var Fullscreen_KeyboardShortcut = Player.getAttribute("keyboard-shortcut-fullscreen");
|
||||||
|
} else {
|
||||||
|
var Fullscreen_KeyboardShortcut = "f";
|
||||||
|
}
|
||||||
|
if (Player.hasAttribute("keyboard-shortcut-mute")) {
|
||||||
|
var Mute_KeyboardShortcut = Player.getAttribute("keyboard-shortcut-mute");
|
||||||
|
} else {
|
||||||
|
var Mute_KeyboardShortcut = "m";
|
||||||
|
}
|
||||||
|
if (Player.hasAttribute("keyboard-shortcut-playpause")) {
|
||||||
|
var PlayPause_KeyboardShortcut = Player.getAttribute("keyboard-shortcut-playpause");
|
||||||
|
} else {
|
||||||
|
var PlayPause_KeyboardShortcut = "k";
|
||||||
|
}
|
||||||
|
if (Player.hasAttribute("keyboard-shortcut-skipback")) {
|
||||||
|
var SkipBack_KeyboardShortcut = Player.getAttribute("keyboard-shortcut-skipback");
|
||||||
|
} else {
|
||||||
|
var SkipBack_KeyboardShortcut = "j";
|
||||||
|
}
|
||||||
|
if (Player.hasAttribute("keyboard-shortcut-skipforth")) {
|
||||||
|
var SkipForth_KeyboardShortcut = Player.getAttribute("keyboard-shortcut-skipforth");
|
||||||
|
} else {
|
||||||
|
var SkipForth_KeyboardShortcut = "l";
|
||||||
|
}
|
||||||
|
function keyboardShortcuts(event) {
|
||||||
|
const { key } = event;
|
||||||
|
if (key === PlayPause_KeyboardShortcut) {
|
||||||
|
if (Player.paused || Player.ended) {
|
||||||
|
Player.play();
|
||||||
|
} else {
|
||||||
|
Player.pause();
|
||||||
|
}
|
||||||
|
if (Player.paused) {
|
||||||
|
Show_Controls();
|
||||||
|
} else {
|
||||||
|
setTimeout(() => {
|
||||||
|
Hide_Controls();
|
||||||
|
}, 1200);
|
||||||
|
}
|
||||||
|
} else if (key === Mute_KeyboardShortcut) {
|
||||||
|
Player.muted = !Player.muted;
|
||||||
|
if (Player.muted) {
|
||||||
|
volume.setAttribute("data-volume", volume.value);
|
||||||
|
volume.value = 0;
|
||||||
|
} else {
|
||||||
|
volume.value = volume.dataset.volume;
|
||||||
|
}
|
||||||
|
} else if (key === Fullscreen_KeyboardShortcut) {
|
||||||
|
if (document.fullscreenElement) {
|
||||||
|
document.exitFullscreen();
|
||||||
|
} else if (document.webkitFullscreenElement) {
|
||||||
|
document.webkitExitFullscreen();
|
||||||
|
} else if (VideoContainer.webkitRequestFullscreen) {
|
||||||
|
VideoContainer.webkitRequestFullscreen();
|
||||||
|
} else {
|
||||||
|
VideoContainer.requestFullscreen();
|
||||||
|
}
|
||||||
|
} else if (key === SkipBack_KeyboardShortcut) {
|
||||||
|
Player.currentTime += -10;
|
||||||
|
} else if (key === SkipForth_KeyboardShortcut) {
|
||||||
|
Player.currentTime += 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener("keyup", keyboardShortcuts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init All Functions
|
||||||
|
AutoToggleControls()
|
||||||
|
Fullscreen()
|
||||||
|
KeyboardShortcuts()
|
||||||
|
PlayPause()
|
||||||
|
SkipAround()
|
||||||
|
</script>
|
83
src/Controls/Seek.astro
Normal file
83
src/Controls/Seek.astro
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
<script is:inline>
|
||||||
|
function Seek() {
|
||||||
|
var Player = document.querySelector('video')
|
||||||
|
const timeElapsed = document.getElementById("current");
|
||||||
|
const duration = document.getElementById("duration");
|
||||||
|
function formatTime(timeInSeconds) {
|
||||||
|
const result = new Date(timeInSeconds * 1e3)
|
||||||
|
.toISOString()
|
||||||
|
.substr(11, 8);
|
||||||
|
return {
|
||||||
|
minutes: result.substr(3, 2),
|
||||||
|
seconds: result.substr(6, 2),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function initializeVideo() {
|
||||||
|
const videoDuration = Math.round(Player.duration);
|
||||||
|
const time = formatTime(videoDuration);
|
||||||
|
duration.innerText = `${time.minutes}:${time.seconds}`;
|
||||||
|
duration.setAttribute(
|
||||||
|
"datetime",
|
||||||
|
`${time.minutes}m ${time.seconds}s`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Player.addEventListener("loadedmetadata", initializeVideo);
|
||||||
|
function updateTimeElapsed() {
|
||||||
|
const time = formatTime(Math.round(Player.currentTime));
|
||||||
|
timeElapsed.innerText = `${time.minutes}:${time.seconds}`;
|
||||||
|
timeElapsed.setAttribute(
|
||||||
|
"datetime",
|
||||||
|
`${time.minutes}m ${time.seconds}s`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Player.addEventListener("timeupdate", updateTimeElapsed);
|
||||||
|
const progressBar = document.querySelector(".vc-progress-bar");
|
||||||
|
const seek = document.getElementById("seek");
|
||||||
|
function initializeVideo() {
|
||||||
|
const videoDuration = Math.round(Player.duration);
|
||||||
|
seek.setAttribute("max", videoDuration);
|
||||||
|
progressBar.setAttribute("max", videoDuration);
|
||||||
|
const time = formatTime(videoDuration);
|
||||||
|
duration.innerText = `${time.minutes}:${time.seconds}`;
|
||||||
|
duration.setAttribute(
|
||||||
|
"datetime",
|
||||||
|
`${time.minutes}m ${time.seconds}s`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
function updateProgress() {
|
||||||
|
seek.value = Math.floor(Player.currentTime);
|
||||||
|
document.querySelector('.vc-progress-bar').style.width = Player.currentTime / Player.duration * 100 + '%'
|
||||||
|
}
|
||||||
|
Player.addEventListener("timeupdate", updateProgress);
|
||||||
|
const seekTooltip = document.getElementById("seek-tooltip");
|
||||||
|
function updateSeekTooltip(event) {
|
||||||
|
const skipTo = Math.round(
|
||||||
|
(event.offsetX / event.target.clientWidth) *
|
||||||
|
parseInt(event.target.getAttribute("max"), 10),
|
||||||
|
);
|
||||||
|
seek.setAttribute("data-seek", skipTo);
|
||||||
|
const t = formatTime(skipTo);
|
||||||
|
seekTooltip.textContent = `${t.minutes}:${t.seconds}`;
|
||||||
|
const rect = Player.getBoundingClientRect();
|
||||||
|
seekTooltip.style.left = `${event.pageX - rect.left}px`;
|
||||||
|
seekTooltip.style.opacity = '1'
|
||||||
|
document.querySelector('.vc-progress-bar').style.width = Player.currentTime / Player.duration * 100 + '%'
|
||||||
|
seek.addEventListener('mouseleave', () => {
|
||||||
|
seekTooltip.style.opacity = '0'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
seek.addEventListener("mousemove", updateSeekTooltip);
|
||||||
|
function skipAhead(event) {
|
||||||
|
const skipTo = event.target.dataset.seek
|
||||||
|
? event.target.dataset.seek
|
||||||
|
: event.target.value;
|
||||||
|
Player.currentTime = skipTo;
|
||||||
|
progressBar.value = skipTo;
|
||||||
|
seek.value = skipTo;
|
||||||
|
}
|
||||||
|
seek.addEventListener("input", skipAhead);
|
||||||
|
|
||||||
|
initializeVideo();
|
||||||
|
}
|
||||||
|
Seek();
|
||||||
|
</script>
|
66
src/Controls/Sync.astro
Normal file
66
src/Controls/Sync.astro
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<script is:inline>
|
||||||
|
// https://gist.github.com/michancio/59b9f3dc54b3ff4f6a84
|
||||||
|
// Find elements
|
||||||
|
var SyncVideo = document.querySelector(".main-video");
|
||||||
|
var SyncAudio = document.querySelector(".main-audio");
|
||||||
|
|
||||||
|
// Object for synchronization of multiple media/sources
|
||||||
|
if (typeof window.MediaController === "function") {
|
||||||
|
var controller = new MediaController();
|
||||||
|
SyncVideo.controller = controller;
|
||||||
|
SyncAudio.controller = controller;
|
||||||
|
} else {
|
||||||
|
controller = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run SyncAudio and SyncVideo simultaneously
|
||||||
|
SyncVideo.addEventListener(
|
||||||
|
"play",
|
||||||
|
function () {
|
||||||
|
if (!controller && SyncAudio.paused) {
|
||||||
|
SyncAudio.play();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Pause/Play and Buffering
|
||||||
|
SyncVideo.addEventListener("waiting", () => {
|
||||||
|
// If SyncVideo is buffering
|
||||||
|
SyncAudio.pause();
|
||||||
|
});
|
||||||
|
SyncVideo.addEventListener("playing", () => {
|
||||||
|
// If SyncVideo is done buffering
|
||||||
|
SyncAudio.play();
|
||||||
|
SyncTimestamp();
|
||||||
|
});
|
||||||
|
|
||||||
|
SyncVideo.addEventListener(
|
||||||
|
"pause",
|
||||||
|
function () {
|
||||||
|
if (!controller && !SyncAudio.paused) {
|
||||||
|
SyncAudio.pause();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
// When Media Ends
|
||||||
|
SyncVideo.addEventListener(
|
||||||
|
"ended",
|
||||||
|
function () {
|
||||||
|
if (controller) {
|
||||||
|
controller.pause();
|
||||||
|
} else {
|
||||||
|
SyncVideo.pause();
|
||||||
|
SyncAudio.pause();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Seekbar
|
||||||
|
function SyncTimestamp() {
|
||||||
|
SyncAudio.currentTime = SyncVideo.currentTime;
|
||||||
|
}
|
||||||
|
</script>
|
221
src/Index.scss
Normal file
221
src/Index.scss
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
.video-container {
|
||||||
|
position: relative;
|
||||||
|
.video-controls, video {
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
*:focus {
|
||||||
|
border: 2px white solid;
|
||||||
|
transition: 1s border;
|
||||||
|
}
|
||||||
|
video {
|
||||||
|
width: 100%;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
canvas {
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
z-index: -1;
|
||||||
|
filter: blur(24px);
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
.video-controls {
|
||||||
|
background: linear-gradient(0deg, rgba(0,0,0,0.7523460067620799) 0%, rgba(0,0,0,0) 15%, rgba(0,0,0,0) 94%, rgba(0,0,0,0.7495448863139005) 100%);;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 4px;
|
||||||
|
left: 0px;
|
||||||
|
width: calc(100% - 24px);
|
||||||
|
padding: 12px;
|
||||||
|
z-index: 5;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: calc(100% - 28px);
|
||||||
|
transition: 0.3s opacity;
|
||||||
|
button {
|
||||||
|
color: white;
|
||||||
|
border-radius: 3rem;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.vc-top {
|
||||||
|
margin-top: 12px;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0;
|
||||||
|
transition: 0.3s opacity;
|
||||||
|
}
|
||||||
|
.vc-bottom {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
.vc-start,
|
||||||
|
.vc-end {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
.vc-center {
|
||||||
|
width: calc(100% - 220px);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
p {
|
||||||
|
width: max-content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.vc-seek {
|
||||||
|
background: rgb(255 255 255 / 10%);
|
||||||
|
width: 100%;
|
||||||
|
height: 6px;
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
border-radius: 3rem;
|
||||||
|
.vc-progress-bar {
|
||||||
|
width: 1%;
|
||||||
|
background: #ff274d;
|
||||||
|
position: absolute;
|
||||||
|
left: 0px;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 3rem;
|
||||||
|
}
|
||||||
|
input#seek {
|
||||||
|
position: absolute;
|
||||||
|
top: -8px;
|
||||||
|
left: 0px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
}
|
||||||
|
#seek[type="range"] {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
#seek[type="range"]:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
#seek[type="range"]::-webkit-slider-runnable-track {
|
||||||
|
background-color: transparent;
|
||||||
|
border-radius: 3rem;
|
||||||
|
height: 1rem;
|
||||||
|
}
|
||||||
|
#seek[type="range"]::-webkit-slider-thumb {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
margin-top: -4px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 3rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
width: 1.5rem;
|
||||||
|
}
|
||||||
|
#seek[type="range"]:focus::-webkit-slider-thumb {
|
||||||
|
outline: 3px solid #ffffff;
|
||||||
|
outline-offset: 0.125rem;
|
||||||
|
}
|
||||||
|
#seek[type="range"]::-moz-range-track {
|
||||||
|
background-color: transparent;
|
||||||
|
border-radius: 3rem;
|
||||||
|
height: 1rem;
|
||||||
|
}
|
||||||
|
#seek[type="range"]::-moz-range-thumb {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 3rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
width: 1.5rem;
|
||||||
|
}
|
||||||
|
#seek[type="range"]:focus::-moz-range-thumb{
|
||||||
|
outline: 3px solid transparent;
|
||||||
|
outline-offset: 0.125rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.timestamp {
|
||||||
|
display: flex;
|
||||||
|
background: rgb(255 255 255 / 10%);
|
||||||
|
border-radius: 3rem;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
pointer-events: none;
|
||||||
|
color: white;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
#seek-tooltip {
|
||||||
|
z-index: 10;
|
||||||
|
background: #464646;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 3rem 0px 0px 3rem;
|
||||||
|
margin-right: -64px;
|
||||||
|
opacity: 0;
|
||||||
|
transition: 0.3s opacity;
|
||||||
|
}
|
||||||
|
#current {
|
||||||
|
background: rgb(255 255 255 / 15%);
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 3rem 0px 0px 3rem;
|
||||||
|
}
|
||||||
|
#duration {
|
||||||
|
padding: 6px 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-controls.hide {
|
||||||
|
opacity: 0;
|
||||||
|
transition: 0.3s opacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-menu {
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
bottom: 0px;
|
||||||
|
background: rgb(0 0 0 / 25%);
|
||||||
|
border-radius: 6px;
|
||||||
|
margin: 0px 24px 90px 0px;
|
||||||
|
width: max-content;
|
||||||
|
min-width: 180px;
|
||||||
|
display: none;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 4px;
|
||||||
|
backdrop-filter: blur(24px) contrast(0.8) brightness(0.8);
|
||||||
|
hr {
|
||||||
|
margin: 0px;
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
padding: 6px 12px !important;
|
||||||
|
margin: 0px !important;
|
||||||
|
aspect-ratio: inherit !important;
|
||||||
|
background: transparent !important;
|
||||||
|
border-radius: 4px !important;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 14px !important;
|
||||||
|
min-height: 36px;
|
||||||
|
&:hover {
|
||||||
|
background: rgb(95 95 95 / 51%) !important;
|
||||||
|
}
|
||||||
|
svg {
|
||||||
|
width: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button#has-switch svg {
|
||||||
|
width: 24px;
|
||||||
|
}
|
95
src/Milieu.astro
Normal file
95
src/Milieu.astro
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
|
||||||
|
<canvas id="ambient-canvas-1"/>
|
||||||
|
<canvas id="ambient-canvas-2"/>
|
||||||
|
|
||||||
|
<script is:inline>
|
||||||
|
/**
|
||||||
|
* @licstart The following is the entire license notice for the
|
||||||
|
* JavaScript code in this page.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 SudoVanilla
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* The JavaScript code in this page is free software: you can
|
||||||
|
* redistribute it and/or modify it under the terms of the GNU
|
||||||
|
* General Public License (GNU GPL) as published by the Free Software
|
||||||
|
* Foundation, either version 3 of the License, or (at your option)
|
||||||
|
* any later version. The code is distributed WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
|
||||||
|
*
|
||||||
|
* As additional permission under GNU GPL version 3 section 7, you
|
||||||
|
* may distribute non-source (e.g., minimized or compacted) forms of
|
||||||
|
* that code without the copy of the GNU GPL normally required by
|
||||||
|
* section 4, provided you include this license notice and a URL
|
||||||
|
* through which recipients can access the Corresponding Source.
|
||||||
|
*
|
||||||
|
* @licend The above is the entire license notice
|
||||||
|
* for the JavaScript code in this page.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const AMvideo = document.querySelector("video")
|
||||||
|
const oddCanvas = document.getElementById("ambient-canvas-1")
|
||||||
|
const evenCanvas = document.getElementById("ambient-canvas-2")
|
||||||
|
const oddCtx = oddCanvas.getContext("2d")
|
||||||
|
const evenCtx = evenCanvas.getContext("2d")
|
||||||
|
|
||||||
|
const frameIntervalMs = 2000
|
||||||
|
const canvasOpacity = "0.4"
|
||||||
|
|
||||||
|
let intervalId
|
||||||
|
let oddFrame = true
|
||||||
|
const drawFrame = () => {
|
||||||
|
if (oddFrame) {
|
||||||
|
oddCtx.drawImage(AMvideo, 0, 0, oddCanvas.width, oddCanvas.height)
|
||||||
|
transitionToOddCanvas()
|
||||||
|
} else {
|
||||||
|
evenCtx.drawImage(AMvideo, 0, 0, evenCanvas.width, evenCanvas.height)
|
||||||
|
transitionToEvenCanvas()
|
||||||
|
}
|
||||||
|
oddFrame = !oddFrame
|
||||||
|
};
|
||||||
|
|
||||||
|
const transitionToOddCanvas = () => {
|
||||||
|
oddCanvas.style.opacity = canvasOpacity
|
||||||
|
evenCanvas.style.opacity = "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
const transitionToEvenCanvas = () => {
|
||||||
|
evenCanvas.style.opacity = canvasOpacity
|
||||||
|
oddCanvas.style.opacity = "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
const drawStart = () => {
|
||||||
|
intervalId = window.setInterval(drawFrame, frameIntervalMs)
|
||||||
|
}
|
||||||
|
|
||||||
|
const drawPause = () => {
|
||||||
|
if (intervalId) window.clearInterval(intervalId)
|
||||||
|
}
|
||||||
|
|
||||||
|
const init = () => {
|
||||||
|
|
||||||
|
//fixes a issue where firefox/chromium fails to load the ambinet mode and doesnt load it. - please dont remove this line lmao
|
||||||
|
|
||||||
|
AMvideo.pause();AMvideo.play();
|
||||||
|
|
||||||
|
// DO NOT REMOVE
|
||||||
|
|
||||||
|
AMvideo.addEventListener("play", drawStart, false)
|
||||||
|
AMvideo.addEventListener("pause", drawPause, false)
|
||||||
|
AMvideo.addEventListener("ended", drawPause, false)
|
||||||
|
|
||||||
|
oddCanvas.style.transition = evenCanvas.style.transition = `opacity ${frameIntervalMs}ms`
|
||||||
|
}
|
||||||
|
|
||||||
|
const cleanup = () => {
|
||||||
|
AMvideo.removeEventListener("play", drawStart)
|
||||||
|
AMvideo.removeEventListener("pause", drawPause)
|
||||||
|
AMvideo.removeEventListener("ended", drawPause)
|
||||||
|
drawPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("load", init)
|
||||||
|
window.addEventListener("unload", cleanup)
|
||||||
|
</script>
|
30
src/Player.astro
Normal file
30
src/Player.astro
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
---
|
||||||
|
// Properties
|
||||||
|
const {
|
||||||
|
Poster,
|
||||||
|
Video,
|
||||||
|
Audio, // For YouTube Frontends that wants to support 1080p and higher
|
||||||
|
CustomControls = true,
|
||||||
|
VideoAttributes,
|
||||||
|
AudioAttributes,
|
||||||
|
Milieu
|
||||||
|
} = Astro.props
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import Controls from './Controls.astro'
|
||||||
|
import MilieuEffect from './Milieu.astro'
|
||||||
|
import Controller from './Controls/Controller.astro'
|
||||||
|
import Seek from './Controls/Seek.astro'
|
||||||
|
import Sync from './Controls/Sync.astro'
|
||||||
|
|
||||||
|
// Styles
|
||||||
|
import './Index.scss'
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="video-container">
|
||||||
|
<video class="main-video" {VideoAttributes} src={Video} poster={Poster} preload="auto"></video>
|
||||||
|
{Audio ? <audio class="main-audio"><source {AudioAttributes} src={Audio} type="audio/mp3"/></audio> : null }
|
||||||
|
{Milieu ? <MilieuEffect/> : null }
|
||||||
|
{Audio ? <Sync/> : null }
|
||||||
|
{CustomControls ? <Controls/> <Controller/> <Seek/> : null }
|
||||||
|
</div>
|
4
test/astro.config.mjs
Normal file
4
test/astro.config.mjs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import { defineConfig } from 'astro/config';
|
||||||
|
import vue from '@astrojs/vue';
|
||||||
|
|
||||||
|
export default defineConfig({integrations: [vue()]});
|
23
test/package.json
Normal file
23
test/package.json
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"name": "test",
|
||||||
|
"type": "module",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "astro dev",
|
||||||
|
"start": "astro dev",
|
||||||
|
"build": "astro check && astro build",
|
||||||
|
"preview": "astro preview",
|
||||||
|
"astro": "astro"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@astrojs/check": "^0.9.2",
|
||||||
|
"@astrojs/vue": "^4.5.0",
|
||||||
|
"@minpluto/zorn": "../",
|
||||||
|
"astro": "^4.14.2",
|
||||||
|
"typescript": "^5.5.4",
|
||||||
|
"vue": "^3.4.38"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"sass-embedded": "^1.77.8"
|
||||||
|
}
|
||||||
|
}
|
1
test/src/env.d.ts
vendored
Normal file
1
test/src/env.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/// <reference path="../.astro/types.d.ts" />
|
27
test/src/pages/index.astro
Normal file
27
test/src/pages/index.astro
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
---
|
||||||
|
// Components
|
||||||
|
import {Zorn} from '@minpluto/zorn'
|
||||||
|
---
|
||||||
|
<meta charset="utf-8">
|
||||||
|
|
||||||
|
<Zorn
|
||||||
|
Poster="https://md.sudovanilla.org/images/eay-p-v.jpg"
|
||||||
|
Video="https://md.sudovanilla.org/videos/webm/Ennie-and-Yoyki.webm"
|
||||||
|
CustomControls
|
||||||
|
Milieu
|
||||||
|
/>
|
||||||
|
|
||||||
|
<h2>Эни и Йойки: Недевчачьи игры. 1 серия. Мультфильмы для детей</h2>
|
||||||
|
<sup style="color: gray">Translation: "Non-girly games"</sup>
|
||||||
|
<p>Created by Daniyar Yambushev</p>
|
||||||
|
<p>Source: <a href="https://www.youtube.com/watch?v=MuyJtxzyU3o">https://www.youtube.com/watch?v=MuyJtxzyU3o</a></p>
|
||||||
|
<br/>
|
||||||
|
<a href="/with-separate-audio">View with separated audio</a>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background: #010101;
|
||||||
|
color: white;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
}
|
||||||
|
</style>
|
3
test/tsconfig.json
Normal file
3
test/tsconfig.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": "astro/tsconfigs/strict"
|
||||||
|
}
|
9
tsconfig.json
Normal file
9
tsconfig.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"include": [
|
||||||
|
"./src",
|
||||||
|
"index.ts"
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"jsx": "react"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue