aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pages/editor.tsx47
-rw-r--r--pages/present.tsx6
-rw-r--r--styles/editor.css7
3 files changed, 54 insertions, 6 deletions
diff --git a/pages/editor.tsx b/pages/editor.tsx
index 085b508..4f49dbb 100644
--- a/pages/editor.tsx
+++ b/pages/editor.tsx
@@ -1,4 +1,4 @@
-import { CSSProperties, useEffect, useState } from 'react';
+import { CSSProperties, ReactNode, useEffect, useState } from 'react';
import create from 'zustand';
import { loopSlide } from '../timeline';
import { TimedVideoPlayer } from './present';
@@ -29,6 +29,11 @@ var getTimelineZoom = create(set => ({
setZoom: (newValue: number) => set(() => ({ zoom: newValue })),
}));
+var useTimelineLabels = create(set => ({
+ labels: [],
+ setLabels: (newLabels: Array<ReactNode>) => set(() => ({ labels: newLabels })),
+}));
+
function TimelineEditor(props: {
player: TimedVideoPlayer;
}) {
@@ -42,6 +47,9 @@ function TimelineEditor(props: {
</div>
);
+ var timelineLabels = useTimelineLabels((st: any) => st.labels);
+ var setTimelineLabels = useTimelineLabels((st: any) => st.setLabels);
+
useEffect(() => {
var canvas = document.getElementById('timeScaleCanvas') as HTMLCanvasElement;
var ctx = canvas.getContext('2d');
@@ -63,20 +71,24 @@ function TimelineEditor(props: {
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
+ var labels: Array<ReactNode> = [];
+
var offset = document.querySelector('.timeline .timelineInner').scrollLeft;
- var d = true;
var frameWidth = Number(
getComputedStyle(document.querySelector('.timeline')).getPropertyValue('--zoom').trim(),
);
+
+ var d = true;
var a = 1;
- var ns = [300, 150, 120, 90, 60, 30, 30, 30, 20, 20, 20, 20, 20];
+ var ns = [300, 150, 120, 90, 60, 30, 30, 30, 15, 15, 10, 10, 10];
var everyN = ns[Math.floor(frameWidth)];
for (var x = -offset; x < canvas.width + offset; x += frameWidth) {
ctx.fillStyle = baseColor;
var rect = [Math.round(x + (frameWidth - 2) / 2), 28, 2, canvas.height];
var drawFrame = false;
+ var marker = false;
if (frameWidth >= 3) {
ctx.fillStyle = d ? baseColor : frameColor;
rect = [x, 28, frameWidth, canvas.height];
@@ -89,14 +101,33 @@ function TimelineEditor(props: {
if (a % everyN == 0) {
ctx.fillStyle = markerFrame;
drawFrame = true;
+ marker = true;
}
- if (drawFrame) ctx.fillRect(rect[0], rect[1], rect[2], rect[3]);
+ if (drawFrame) {
+ ctx.fillRect(rect[0], rect[1], rect[2], rect[3]);
+
+ if (marker) {
+ var frame = Math.round(x / frameWidth + offset / frameWidth + 1);
+ labels.push(
+ <span
+ className='label numbers posabs nosel'
+ style={{
+ left: Math.round(rect[0] + frameWidth / 2),
+ top: rect[1],
+ }}
+ children={props.player.frameToTimestampString(frame)}
+ />,
+ );
+ }
+ }
d = !d;
a++;
}
+ setTimelineLabels(labels);
+
requestAnimationFrame(draw);
}
draw();
@@ -112,6 +143,7 @@ function TimelineEditor(props: {
return <>
<canvas className='timeScale posabs a0' id='timeScaleCanvas' />
+ <div className='labels'>{timelineLabels}</div>
<div className='timelineInner posabs a0'>
<div className='scrubber posabs v0'>
<svg
@@ -128,7 +160,12 @@ function TimelineEditor(props: {
<div className='needle posabs a0' />
<div className='frameOverlay posabs v0' />
</div>
- <div className='keyframes'>{keyframes}</div>
+ <div
+ className='keyframes'
+ style={{ '--total-frames': props.player?.timeline?.framecount.toString() } as CSSProperties}
+ >
+ {keyframes}
+ </div>
</div>
</>;
}
diff --git a/pages/present.tsx b/pages/present.tsx
index 19ef111..d1d18a2 100644
--- a/pages/present.tsx
+++ b/pages/present.tsx
@@ -33,7 +33,11 @@ export class TimedVideoPlayer {
frameToTimestampString(frame: number) {
var timecodeString = new Timecode(frame, this.framerate).toString();
- return timecodeString.replace(/^(00:)+/, '') + 'f';
+ return timecodeString
+ .replace(/^(00:)+/, '')
+ .replace(';', '.')
+ .replace(/(:)(\d+?)$/, '.$2')
+ + 'f';
}
timestampToFrame(timestamp: number): number {
diff --git a/styles/editor.css b/styles/editor.css
index 4b1e006..d1a6256 100644
--- a/styles/editor.css
+++ b/styles/editor.css
@@ -100,6 +100,7 @@
grid-column: 2;
grid-row: 3;
background-color: var(--c100);
+ overflow: hidden;
}
.appGrid .viewer {
@@ -158,6 +159,7 @@
.appGrid .timeline .keyframes {
height: 100%;
+ width: calc(var(--zoom) * var(--total-frames) * 1px);
}
.appGrid .timeline .keyframes .frame {
@@ -223,3 +225,8 @@
.timeline .scrubber .head { fill: var(--blue); }
.timeline .scrubber .needle { background-color: var(--blue); }
+.timeline .labels .label {
+ transform: translate(-50%, -100%);
+ color: var(--c700);
+}
+