x2 speed for v0ck

This commit is contained in:
2026-05-23 09:49:57 +02:00
parent 867304bcb1
commit fdf54e4513
2 changed files with 101 additions and 0 deletions

View File

@@ -509,4 +509,33 @@
@keyframes danmaku-fly { @keyframes danmaku-fly {
from { transform: translateX(calc(100vw + 100%)); } from { transform: translateX(calc(100vw + 100%)); }
to { transform: translateX(calc(-100% - 200px)); } to { transform: translateX(calc(-100% - 200px)); }
}
/* Speedup 2x HUD Pill */
.v0ck_speed_indicator {
position: absolute;
top: 20px;
left: 50%;
transform: translate(-50%, -10px);
background: rgba(0, 0, 0, 0.75);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
color: #fff;
padding: 8px 16px;
border-radius: 20px;
font-size: 14px;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
pointer-events: none;
opacity: 0;
transition: opacity 0.2s, transform 0.2s;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
}
.v0ck_speed_indicator:not(.v0ck_hidden) {
opacity: 1;
transform: translate(-50%, 0);
} }

View File

@@ -53,6 +53,12 @@ const tpl_player = (svg, size) => `<div class="v0ck_player_controls">
</button> </button>
</div> </div>
<div class="v0ck_loader v0ck_hidden"><div></div></div> <div class="v0ck_loader v0ck_hidden"><div></div></div>
<div class="v0ck_speed_indicator v0ck_hidden">
<svg viewBox="0 0 24 24" style="width: 16px; height: 16px; fill: currentColor; display: inline-block; vertical-align: middle; margin-right: 6px;">
<path d="M4 18l8.5-6L4 6v12zm9-12v12l8.5-6L13 6z"/>
</svg>
<span>2X Speed</span>
</div>
<div class="v0ck_overlay"> <div class="v0ck_overlay">
<svg style="width: 60px; height: 60px;"> <svg style="width: 60px; height: 60px;">
<use href="${svg}#play"></use> <use href="${svg}#play"></use>
@@ -159,6 +165,13 @@ class v0ck {
const defaultVolume = 0.5; const defaultVolume = 0.5;
let mousedown = false; let mousedown = false;
let _volume; let _volume;
// Hold to speedup (2x) states
let speedUpTimeout;
let isSpeedingUp = false;
let restorePlaybackRate = 1;
let ignoreNextClick = false;
const speedIndicator = player.querySelector('.v0ck_speed_indicator');
function handleVolumeButton(vol) { function handleVolumeButton(vol) {
[...volumeSymbols].forEach(s => !s.classList.contains('v0ck_hidden') ? s.classList.add('v0ck_hidden') : null); [...volumeSymbols].forEach(s => !s.classList.contains('v0ck_hidden') ? s.classList.add('v0ck_hidden') : null);
@@ -260,6 +273,12 @@ class v0ck {
} }
player.addEventListener('click', e => { player.addEventListener('click', e => {
if (ignoreNextClick) {
e.stopPropagation();
e.preventDefault();
ignoreNextClick = false;
return;
}
const path = e.path || (e.composedPath && e.composedPath()); const path = e.path || (e.composedPath && e.composedPath());
const isControls = !!path.filter(f => f.classList?.contains('v0ck_player_controls')).length; const isControls = !!path.filter(f => f.classList?.contains('v0ck_player_controls')).length;
if (!isControls) { if (!isControls) {
@@ -358,6 +377,8 @@ class v0ck {
if (gestureType === 'none') { if (gestureType === 'none') {
if (dy > dx && dy > 5) { if (dy > dx && dy > 5) {
gestureType = 'volume'; gestureType = 'volume';
clearTimeout(speedUpTimeout);
endSpeedUp();
} else if (dx > dy && dx > 5) { } else if (dx > dy && dx > 5) {
gestureType = 'other'; // Probably seeking or horizontal swipe gestureType = 'other'; // Probably seeking or horizontal swipe
return; return;
@@ -367,6 +388,9 @@ class v0ck {
} }
if (gestureType === 'volume') { if (gestureType === 'volume') {
clearTimeout(speedUpTimeout);
endSpeedUp();
const deltaY = startY - touch.clientY; // swipe up is positive const deltaY = startY - touch.clientY; // swipe up is positive
const sensitivity = 200; // pixels for 0 to 1 range (reverted to original) const sensitivity = 200; // pixels for 0 to 1 range (reverted to original)
let newVol = startVol + (deltaY / sensitivity); let newVol = startVol + (deltaY / sensitivity);
@@ -575,6 +599,54 @@ class v0ck {
video.addEventListener('playing', resetControlsTimer); video.addEventListener('playing', resetControlsTimer);
video.addEventListener('pause', () => clearTimeout(controlsTimer)); video.addEventListener('pause', () => clearTimeout(controlsTimer));
// Speedup 2x on Hold logic
function startSpeedUp(e) {
// Only speed up if the video is currently playing
if (video.paused) return;
// Only left mouse click or touch triggers speedup
if (e.type === 'mousedown' && e.button !== 0) return;
// Don't speed up if clicking on controls or settings panel
const path = e.path || (e.composedPath && e.composedPath());
const isControls = !!path.filter(f => f.classList?.contains('v0ck_player_controls')).length;
if (isControls) return;
clearTimeout(speedUpTimeout);
speedUpTimeout = setTimeout(() => {
isSpeedingUp = true;
ignoreNextClick = true;
restorePlaybackRate = video.playbackRate;
video.playbackRate = 2.0;
if (speedIndicator) {
speedIndicator.classList.remove('v0ck_hidden');
}
}, 500);
}
function endSpeedUp(e) {
clearTimeout(speedUpTimeout);
if (isSpeedingUp) {
isSpeedingUp = false;
video.playbackRate = restorePlaybackRate;
if (speedIndicator) {
speedIndicator.classList.add('v0ck_hidden');
}
// Brief timeout before allowing normal clicking again to bypass the immediate click event
setTimeout(() => {
ignoreNextClick = false;
}, 100);
}
}
player.addEventListener('mousedown', startSpeedUp);
player.addEventListener('touchstart', startSpeedUp, { passive: true });
player.addEventListener('mouseup', endSpeedUp);
player.addEventListener('mouseleave', endSpeedUp);
player.addEventListener('touchend', endSpeedUp);
player.addEventListener('touchcancel', endSpeedUp);
this.toggleFullScreen = toggleFullScreen; this.toggleFullScreen = toggleFullScreen;
this.enterFullScreen = enterFullScreen; this.enterFullScreen = enterFullScreen;