This commit is contained in:
team3
2026-10-01 00:09:16 +02:00
parent 8ddd8208f5
commit bd1694528a
3 changed files with 52 additions and 17 deletions

View File

@@ -1,14 +1,15 @@
<script setup>
// Der Zeitstrahl der Aufnahme rund um die Marke. Ein Balken zeigt das
// Fenster des Clips; an seinen Enden zieht man Anfang und Ende, in der
// Mitte schiebt man das ganze Fenster. Zeiten in Sekunden relativ zur
// Marke: 0 ist der Moment, an dem C gedrückt wurde.
// Mitte schiebt man das ganze Fenster. Daneben zieht man den Strahl
// selbst nach links und rechts. Zeiten in Sekunden relativ zur Marke:
// 0 ist der Moment, an dem C gedrückt wurde.
import { computed, ref } from 'vue'
const props = defineProps({
// Stücke der Aufnahme: [{ von, bis }] relativ zur Marke
segmente: { type: Array, default: () => [] },
maxVorher: { type: Number, default: 600 },
maxVorher: { type: Number, default: 4 * 3600 },
minDauer: { type: Number, default: 3 },
maxDauer: { type: Number, default: 180 },
// Uhrzeit der Marke, für die Beschriftung
@@ -36,15 +37,28 @@ function prozent(t) {
const start = computed(() => -vorher.value)
const ende = computed(() => -vorher.value + dauer.value)
// Wo die Aufnahme überhaupt Material hat
// Wo die Aufnahme überhaupt Material hat — so weit lässt sich der Strahl schieben
const grenzen = computed(() => {
if (!props.segmente.length) return { von: -props.maxVorher, bis: props.maxDauer }
if (!props.segmente.length) return { von: -props.maxVorher, bis: props.maxVorher }
return {
von: Math.min(...props.segmente.map((s) => s.von)),
bis: Math.max(...props.segmente.map((s) => s.bis)),
}
})
// Das Stück der Aufnahme, in dem das Fenster liegt. Zwischen zwei
// Stücken ist Loch — dort gibt es kein Bild, dorthin darf das Fenster
// nicht. Liegt es in einem Loch, gilt das nähere Stück.
function abstand(s, t) {
return t < s.von ? s.von - t : t > s.bis ? t - s.bis : 0
}
function stueckUm(t) {
if (!props.segmente.length) return { von: -props.maxVorher, bis: props.maxVorher }
const s = props.segmente.reduce((a, b) => (abstand(a, t) <= abstand(b, t) ? a : b))
return { von: Math.max(s.von, -props.maxVorher), bis: Math.min(s.bis, props.maxVorher) }
}
function zoomen(schritt) {
zoom.value = Math.max(0, Math.min(ZOOMS.length - 1, zoom.value + schritt))
mitte.value = (start.value + ende.value) / 2
@@ -72,19 +86,30 @@ let ausgang = null
function anfassen(e, was) {
griff = was
ausgang = { x: e.clientX, vorher: vorher.value, dauer: dauer.value }
ausgang = {
x: e.clientX,
vorher: vorher.value,
dauer: dauer.value,
mitte: mitte.value,
// Das Stück, in dem das Fenster beim Anfassen liegt — darin bleibt es
stueck: stueckUm(-vorher.value + dauer.value / 2),
}
e.currentTarget.setPointerCapture(e.pointerId)
}
function ziehen(e) {
if (!griff || !strahl.value) return
const dt = ((e.clientX - ausgang.x) / strahl.value.clientWidth) * spanne.value
const g = grenzen.value
if (griff === 'links') {
const g = ausgang.stueck
if (griff === 'bahn') {
// Den Strahl selbst schieben — die Zeiten laufen der Hand entgegen
const r = grenzen.value
mitte.value = Math.max(r.von, Math.min(r.bis, ausgang.mitte - dt))
} else if (griff === 'links') {
// Der Anfang wandert, das Ende bleibt stehen
const altesEnde = -ausgang.vorher + ausgang.dauer
let neuerStart = -ausgang.vorher + dt
neuerStart = Math.max(g.von, -props.maxVorher, neuerStart)
neuerStart = Math.max(g.von, neuerStart)
neuerStart = Math.min(
altesEnde - props.minDauer,
Math.max(altesEnde - props.maxDauer, neuerStart),
@@ -97,18 +122,20 @@ function ziehen(e) {
const d = Math.max(props.minDauer, Math.min(props.maxDauer, neuesEnde + ausgang.vorher))
dauer.value = Math.round(d)
} else {
// Das ganze Fenster verschieben
// Das ganze Fenster verschieben, aber nicht aus dem Stück hinaus
let neuerStart = -ausgang.vorher + dt
neuerStart = Math.max(g.von, -props.maxVorher, Math.min(g.bis - ausgang.dauer, neuerStart))
neuerStart = Math.max(g.von, Math.min(g.bis - ausgang.dauer, neuerStart))
vorher.value = Math.round(-neuerStart)
}
}
function loslassen(e) {
if (!griff) return
const war = griff
griff = null
e.currentTarget.releasePointerCapture?.(e.pointerId)
emit('ende')
// Nur ein verändertes Fenster braucht eine neue Vorschau
if (war !== 'bahn') emit('ende')
}
</script>
@@ -134,6 +161,7 @@ function loslassen(e) {
<div
ref="strahl"
class="strahl"
@pointerdown="anfassen($event, 'bahn')"
@pointermove="ziehen"
@pointerup="loslassen"
@pointercancel="loslassen"
@@ -153,7 +181,7 @@ function loslassen(e) {
<span
class="bereich"
:style="{ left: prozent(start) + '%', width: prozent(ende) - prozent(start) + '%' }"
@pointerdown="anfassen($event, 'mitte')"
@pointerdown.stop="anfassen($event, 'mitte')"
>
<span class="griff links" @pointerdown.stop="anfassen($event, 'links')"></span>
<span class="griff rechts" @pointerdown.stop="anfassen($event, 'rechts')"></span>
@@ -206,6 +234,7 @@ function loslassen(e) {
background: var(--bg);
border: 1px solid var(--line);
border-radius: 8px;
cursor: grab;
touch-action: none;
}