scrollbalken für wellenform
This commit is contained in:
@@ -288,6 +288,10 @@ h1 { margin:0 0 6px; font-size:28px; }
|
|||||||
.playhead { position:absolute; top:0; bottom:0; width:2px; margin-left:-1px; background:#ffcc33; box-shadow:0 0 5px #000; z-index:4; pointer-events:none; }
|
.playhead { position:absolute; top:0; bottom:0; width:2px; margin-left:-1px; background:#ffcc33; box-shadow:0 0 5px #000; z-index:4; pointer-events:none; }
|
||||||
.handle { position:absolute; top:0; bottom:0; width:18px; margin-left:-9px; cursor:ew-resize; z-index:3; touch-action:none; }
|
.handle { position:absolute; top:0; bottom:0; width:18px; margin-left:-9px; cursor:ew-resize; z-index:3; touch-action:none; }
|
||||||
.handle::after { content:""; position:absolute; left:7px; top:0; bottom:0; width:4px; background:#fff; border-radius:2px; box-shadow:0 0 5px #000; }
|
.handle::after { content:""; position:absolute; left:7px; top:0; bottom:0; width:4px; background:#fff; border-radius:2px; box-shadow:0 0 5px #000; }
|
||||||
|
.view-scroll { position:relative; width:100%; height:14px; margin-top:5px; background:#292929; border-radius:7px; overflow:hidden; touch-action:none; cursor:grab; }
|
||||||
|
.view-scroll-thumb { position:absolute; top:2px; bottom:2px; left:0; min-width:14px; background:#777; border-radius:5px; cursor:grab; touch-action:none; }
|
||||||
|
.view-scroll-thumb:hover { background:#999; }
|
||||||
|
.view-scroll-thumb.dragging { background:#aaa; cursor:grabbing; }
|
||||||
.time-row { display:flex; justify-content:space-between; margin-top:8px; color:#aaa; font-size:14px; }
|
.time-row { display:flex; justify-content:space-between; margin-top:8px; color:#aaa; font-size:14px; }
|
||||||
.controls { display:flex; flex-wrap:wrap; gap:8px; align-items:center; margin-top:18px; }
|
.controls { display:flex; flex-wrap:wrap; gap:8px; align-items:center; margin-top:18px; }
|
||||||
button, a.button { border:0; border-radius:6px; padding:11px 16px; background:#333; color:#fff; cursor:pointer; text-decoration:none; font-size:15px; }
|
button, a.button { border:0; border-radius:6px; padding:11px 16px; background:#333; color:#fff; cursor:pointer; text-decoration:none; font-size:15px; }
|
||||||
@@ -314,6 +318,8 @@ button:disabled { opacity:.45; cursor:not-allowed; }
|
|||||||
body { padding:12px; }
|
body { padding:12px; }
|
||||||
.editor { padding:12px; }
|
.editor { padding:12px; }
|
||||||
.wave-wrap { height:180px; }
|
.wave-wrap { height:180px; }
|
||||||
|
.view-scroll { height:18px; }
|
||||||
|
.view-scroll-thumb { min-width:28px; }
|
||||||
.zoom { width:100%; margin-left:0; }
|
.zoom { width:100%; margin-left:0; }
|
||||||
.values { grid-template-columns:1fr; }
|
.values { grid-template-columns:1fr; }
|
||||||
.position { width:100%; margin-left:0; }
|
.position { width:100%; margin-left:0; }
|
||||||
@@ -335,6 +341,9 @@ button:disabled { opacity:.45; cursor:not-allowed; }
|
|||||||
<div class="handle" id="startHandle" title="Anfang verschieben"></div>
|
<div class="handle" id="startHandle" title="Anfang verschieben"></div>
|
||||||
<div class="handle" id="endHandle" title="Ende verschieben"></div>
|
<div class="handle" id="endHandle" title="Ende verschieben"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="view-scroll" id="viewScroll" aria-label="Sichtbaren Bereich verschieben">
|
||||||
|
<div class="view-scroll-thumb" id="viewScrollThumb"></div>
|
||||||
|
</div>
|
||||||
<div class="time-row">
|
<div class="time-row">
|
||||||
<span id="viewStart">00:00:00</span>
|
<span id="viewStart">00:00:00</span>
|
||||||
<span id="viewEnd">{{ duration }}</span>
|
<span id="viewEnd">{{ duration }}</span>
|
||||||
@@ -392,6 +401,12 @@ const endHandle = document.getElementById('endHandle');
|
|||||||
const selection = document.getElementById('selection');
|
const selection = document.getElementById('selection');
|
||||||
const playhead = document.getElementById('playhead');
|
const playhead = document.getElementById('playhead');
|
||||||
const audio = document.getElementById('audio');
|
const audio = document.getElementById('audio');
|
||||||
|
const viewScroll = document.getElementById('viewScroll');
|
||||||
|
const viewScrollThumb = document.getElementById('viewScrollThumb');
|
||||||
|
let scrollbarDragging = false;
|
||||||
|
let scrollbarPointerId = null;
|
||||||
|
let scrollbarStartX = 0;
|
||||||
|
let scrollbarStartView = 0;
|
||||||
|
|
||||||
function fmt(sec) {
|
function fmt(sec) {
|
||||||
sec = Math.max(0, Math.round(sec));
|
sec = Math.max(0, Math.round(sec));
|
||||||
@@ -428,6 +443,14 @@ function render() {
|
|||||||
document.getElementById('viewStart').textContent = fmt(viewStart);
|
document.getElementById('viewStart').textContent = fmt(viewStart);
|
||||||
document.getElementById('viewEnd').textContent = fmt(viewEnd);
|
document.getElementById('viewEnd').textContent = fmt(viewEnd);
|
||||||
document.getElementById('zoomValue').textContent = 'Zoom: '+Math.round(zoom*100)+' %';
|
document.getElementById('zoomValue').textContent = 'Zoom: '+Math.round(zoom*100)+' %';
|
||||||
|
|
||||||
|
// The scrollbar thumb represents the currently visible part of the full recording.
|
||||||
|
const visiblePercent = Math.max(0, Math.min(100, ((viewEnd-viewStart)/duration)*100));
|
||||||
|
const offsetPercent = Math.max(0, Math.min(100-visiblePercent, (viewStart/duration)*100));
|
||||||
|
viewScrollThumb.style.width = visiblePercent+'%';
|
||||||
|
viewScrollThumb.style.left = offsetPercent+'%';
|
||||||
|
viewScrollThumb.style.pointerEvents = zoom > 1 ? 'auto' : 'none';
|
||||||
|
viewScrollThumb.classList.toggle('dragging', scrollbarDragging);
|
||||||
renderPlayhead();
|
renderPlayhead();
|
||||||
}
|
}
|
||||||
function renderPlayhead() {
|
function renderPlayhead() {
|
||||||
@@ -461,6 +484,60 @@ function resetZoom() {
|
|||||||
viewEnd = duration;
|
viewEnd = duration;
|
||||||
render();
|
render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function moveViewTo(newStart) {
|
||||||
|
const span = viewEnd-viewStart;
|
||||||
|
if (span >= duration) {
|
||||||
|
viewStart = 0;
|
||||||
|
viewEnd = duration;
|
||||||
|
} else {
|
||||||
|
viewStart = Math.max(0, Math.min(duration-span, newStart));
|
||||||
|
viewEnd = viewStart+span;
|
||||||
|
}
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
|
viewScrollThumb.addEventListener('pointerdown', e => {
|
||||||
|
if (zoom <= 1) return;
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
scrollbarDragging = true;
|
||||||
|
scrollbarPointerId = e.pointerId;
|
||||||
|
scrollbarStartX = e.clientX;
|
||||||
|
scrollbarStartView = viewStart;
|
||||||
|
viewScrollThumb.setPointerCapture(e.pointerId);
|
||||||
|
render();
|
||||||
|
});
|
||||||
|
viewScrollThumb.addEventListener('pointermove', e => {
|
||||||
|
if (!scrollbarDragging || e.pointerId !== scrollbarPointerId) return;
|
||||||
|
const trackWidth = viewScroll.clientWidth;
|
||||||
|
const thumbWidth = viewScrollThumb.offsetWidth;
|
||||||
|
const movableWidth = Math.max(1, trackWidth-thumbWidth);
|
||||||
|
const span = viewEnd-viewStart;
|
||||||
|
const deltaPixels = e.clientX-scrollbarStartX;
|
||||||
|
const deltaTime = (deltaPixels/movableWidth) * (duration-span);
|
||||||
|
moveViewTo(scrollbarStartView+deltaTime);
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
function endScrollbarDrag(e) {
|
||||||
|
if (!scrollbarDragging || e.pointerId !== scrollbarPointerId) return;
|
||||||
|
scrollbarDragging = false;
|
||||||
|
scrollbarPointerId = null;
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
viewScrollThumb.addEventListener('pointerup', endScrollbarDrag);
|
||||||
|
viewScrollThumb.addEventListener('pointercancel', endScrollbarDrag);
|
||||||
|
|
||||||
|
// Clicking/touching the empty part of the scrollbar moves the visible window
|
||||||
|
// so that the clicked position becomes its center.
|
||||||
|
viewScroll.addEventListener('pointerdown', e => {
|
||||||
|
if (zoom <= 1 || e.target === viewScrollThumb) return;
|
||||||
|
e.preventDefault();
|
||||||
|
const r = viewScroll.getBoundingClientRect();
|
||||||
|
const ratio = Math.max(0, Math.min(1, (e.clientX-r.left)/r.width));
|
||||||
|
const span = viewEnd-viewStart;
|
||||||
|
moveViewTo(ratio*duration-span/2);
|
||||||
|
});
|
||||||
wave.addEventListener('wheel', e => {
|
wave.addEventListener('wheel', e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const factor = e.deltaY < 0 ? ZOOM_FACTOR : 1/ZOOM_FACTOR;
|
const factor = e.deltaY < 0 ? ZOOM_FACTOR : 1/ZOOM_FACTOR;
|
||||||
|
|||||||
Reference in New Issue
Block a user