timeline display

This commit is contained in:
Lee-Orr
2024-03-26 13:50:35 -04:00
parent f67dbe6da0
commit 52295af442
+102 -52
View File
@@ -85,6 +85,7 @@ export class AnimTimelineElement extends LitElement {
* setting the cursor style to the grabby hand.
*/
@state() _panning = false;
@state() _scrubbing = false;
/** How many seconds are represented by each px of horizontal space. */
secondsPerPx = new Computed(() => (this.rangeEnd - this.rangeStart) / this._renderWidth);
@@ -93,13 +94,13 @@ export class AnimTimelineElement extends LitElement {
constructor () {
super();
this.#resizeObserver = new ResizeObserver(() => this.#updateRenderWidth());
this.#resizeObserver = new ResizeObserver(() => this.updateRenderWidth());
this.#resizeObserver.observe(this);
}
override connectedCallback(): void {
super.connectedCallback();
this.#updateRenderWidth();
this.updateRenderWidth();
}
override disconnectedCallback(): void {
@@ -125,12 +126,12 @@ export class AnimTimelineElement extends LitElement {
event.preventDefault();
event.stopImmediatePropagation();
if (event.ctrlKey) {
this.#zoomTimeline(event.clientX, event.deltaY);
if (event.ctrlKey || Math.abs(event.deltaY ) > 0) {
this.zoomTimeline(event.clientX, event.deltaY);
} else if (event.deltaX) {
this.#pan(-event.deltaX);
this.pan(-event.deltaX);
} else if (event.shiftKey && event.deltaY) {
this.#pan(-event.deltaY);
this.pan(-event.deltaY);
}
}
@@ -154,7 +155,7 @@ export class AnimTimelineElement extends LitElement {
studio.select(id);
}
#zoomTimeline(origin: number, delta: number): void {
zoomTimeline(origin: number, delta: number): void {
const currentRange = this.rangeEnd - this.rangeStart;
const elementRect = this.getBoundingClientRect();
const focusPoint = (origin - elementRect.x) / elementRect.width;
@@ -168,19 +169,37 @@ export class AnimTimelineElement extends LitElement {
this.dispatchEvent(new CustomEvent("rangeEnd-change", { detail: rangeEnd }));
}
onDragStart(): void {
onMidDragStart(): void {
this._panning = true;
}
onDragEnd(): void {
onMidDragEnd(): void {
this._panning = false;
}
onDrag(event: PointerEvent): void {
this.#pan(event.movementX);
onMidDrag(event: PointerEvent): void {
this.pan(event.movementX);
}
#pan(deltaPx: number): void {
onDragStart(): void {
this._scrubbing = true;
}
onDragEnd(): void {
this._scrubbing = false;
}
onDrag(event: PointerEvent): void {
let elementBounds = this.getBoundingClientRect();
let relativeX = event.clientX - elementBounds.left;
let ratioX = Math.max(0, Math.min(relativeX / elementBounds.width, 1));
const seconds = (this.rangeEnd - this.rangeStart) * ratioX + this.rangeStart;
const updated = Math.round(seconds * this.clipFps) / this.clipFps;
studio.seekTime(updated);
}
pan(deltaPx: number): void {
const deltaChange = deltaPx * 0.001;
const scaledChange = (this.rangeEnd - this.rangeStart) * deltaChange;
@@ -201,7 +220,7 @@ export class AnimTimelineElement extends LitElement {
let relativeX = event.clientX - elementBounds.left;
let ratioX = Math.max(0, Math.min(relativeX / elementBounds.width, 1));
const seconds = (this.rangeEnd - this.rangeStart) * ratioX + this.rangeStart;
const updated = seconds;
const updated = Math.round(seconds * this.clipFps) / this.clipFps;
studio.seekTime(updated);
}
@@ -211,7 +230,7 @@ export class AnimTimelineElement extends LitElement {
let relativeX = event.clientX - elementBounds.left;
let ratioX = Math.max(0, Math.min(relativeX / elementBounds.width, 1));
const seconds = (this.rangeEnd - this.rangeStart) * ratioX + this.rangeStart;
const updated = seconds;
const updated = Math.round(seconds * this.clipFps) / this.clipFps;
studio.seekTime(updated);
}
@@ -225,7 +244,7 @@ export class AnimTimelineElement extends LitElement {
studio.moveKeyframeTime(id, time);
}
#updateRenderWidth(): void {
updateRenderWidth(): void {
this._renderWidth = this.getBoundingClientRect().width;
}
@@ -255,56 +274,87 @@ export class AnimTimelineElement extends LitElement {
protected override render() {
const range = this.rangeEnd - this.rangeStart;
let subdivisionScale = 0.5;
let majorSubdivisionScale = 1;
let fps = this.clipFps;
let frameRange = Math.ceil(range * this.clipFps);
let spf = 1 / fps;
let majorSubdivisionSize = this.clipFps;
let minorSubdivisionSize = this.clipFps / 2;
let unlablelledSubdivisionSize = this.clipFps / 4;
if (range < 0.5) {
subdivisionScale = 1 / this.clipFps;
majorSubdivisionScale = 0.25 * this.clipFps;
majorSubdivisionSize = this.clipFps / 2;
minorSubdivisionSize = this.clipFps / 12;
unlablelledSubdivisionSize = 1;
} else if (range < 2) {
subdivisionScale = 0.25 * this.clipFps;
majorSubdivisionScale = 0.5;
} else if (range > 120) {
subdivisionScale = 30;
majorSubdivisionScale = 60;
} else if (range > 60) {
subdivisionScale = 10;
majorSubdivisionScale = 30;
} else if (range > 10) {
subdivisionScale = 5;
majorSubdivisionScale = 10;
} else if (range > 5) {
subdivisionScale = 1;
majorSubdivisionScale = 5;
majorSubdivisionSize = this.clipFps / 2;
minorSubdivisionSize = this.clipFps / 4;
unlablelledSubdivisionSize = 2;
} else if (range < 5) {
majorSubdivisionSize = this.clipFps;
minorSubdivisionSize = this.clipFps / 2;
unlablelledSubdivisionSize = this.clipFps / 4;
} else if (range < 10) {
majorSubdivisionSize = this.clipFps * 2;
minorSubdivisionSize = this.clipFps;
unlablelledSubdivisionSize = this.clipFps / 2;
} else if (range < 60) {
majorSubdivisionSize = this.clipFps * 10;
minorSubdivisionSize = this.clipFps * 5;
unlablelledSubdivisionSize = this.clipFps;
} else if (range < 120) {
majorSubdivisionSize = this.clipFps * 20;
minorSubdivisionSize = this.clipFps * 10;
unlablelledSubdivisionSize = this.clipFps * 2;
} else {
majorSubdivisionSize = Math.ceil(frameRange / 10);
minorSubdivisionSize = Math.ceil(frameRange / 20);
unlablelledSubdivisionSize = Math.ceil(frameRange / 40);
}
const scaledStart = this.rangeStart / subdivisionScale;
const scaledEnd = this.rangeEnd / subdivisionScale;
let subdivisions : Array<{seconds: number, fraction: number, isMajor: boolean}> = [];
let currentOffset = scaledStart;
let currentOffsetSeconds = this.rangeStart;
let subdivisions : Array<{seconds: number, fraction: number, isMajor: boolean, isLabelled: boolean}> = [];
while (currentOffset < scaledEnd) {
const currentSeconds = currentOffsetSeconds;
const isMajor = currentOffsetSeconds % majorSubdivisionScale === 0;
subdivisions.push({ seconds: currentSeconds, fraction: (currentSeconds - this.rangeStart) / range, isMajor });
let nextSubdiv = Math.ceil(currentOffset);
if (Math.abs(nextSubdiv - currentOffset) < 0.1) {
nextSubdiv += 1;
let initialFrame = Math.ceil(this.rangeStart * this.clipFps);
for (let i = 0; i < frameRange; i++) {
let frame = i + initialFrame;
let seconds = frame * spf;
let fraction = (seconds - this.rangeStart) / range;
if (frame % majorSubdivisionSize === 0) {
subdivisions.push({
seconds,
fraction,
isMajor: true,
isLabelled: true
});
} else if (frame % minorSubdivisionSize === 0) {
subdivisions.push({
seconds,
fraction,
isMajor: false,
isLabelled: true
});
} else if (frame % unlablelledSubdivisionSize === 0) {
subdivisions.push({seconds, fraction, isMajor: false, isLabelled: false });
}
currentOffset = nextSubdiv;
currentOffsetSeconds = currentOffset * subdivisionScale;
}
return html`
<div
class=${classMap({
wrapper: true,
panning: this._panning,
panning: this._panning || this._scrubbing,
})}
${drag({
button: 1,
start: this.onMidDragStart,
move: this.onMidDrag,
end: this.onMidDragEnd,
})}
${drag({
button: 0,
start: this.onDragStart,
move: this.onDrag,
end: this.onDragEnd,
@@ -313,7 +363,7 @@ export class AnimTimelineElement extends LitElement {
@click=${this.onSetPlayhead}
>
${subdivisions.map(({ seconds, fraction, isMajor}, idx) => {
${subdivisions.map(({ seconds, fraction, isMajor, isLabelled}, idx) => {
const secondsPercentage = fraction * 100;
return html`
@@ -326,7 +376,7 @@ export class AnimTimelineElement extends LitElement {
left: `${secondsPercentage}%`
})}
></div>
${true ? html`
${isMajor || isLabelled ? html`
<span class="timestamp"
style=${styleMap({
left: `calc(${secondsPercentage}% + 8px)`