diff --git a/package.json b/package.json index 0192588..cfadbc2 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "@typescript-eslint/parser": "^6.19.0", "autoprefixer": "^10.4.16", "conventional-changelog-eslint": "^5.0.0", + "dayjs": "^1.11.10", "dequal": "^2.0.3", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 95c7ff3..92bbe35 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,6 +41,9 @@ devDependencies: conventional-changelog-eslint: specifier: ^5.0.0 version: 5.0.0 + dayjs: + specifier: ^1.11.10 + version: 1.11.10 dequal: specifier: ^2.0.3 version: 2.0.3 @@ -1598,6 +1601,10 @@ packages: hasBin: true dev: true + /dayjs@1.11.10: + resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==} + dev: true + /debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} diff --git a/src/lib/Block.ts b/src/lib/Block.ts index 4da1eb9..ce80d87 100644 --- a/src/lib/Block.ts +++ b/src/lib/Block.ts @@ -26,7 +26,7 @@ export class Block implements ISequenceChild { data?: { [key: string]: unknown; }; - markers: { time: number; label: string }[] = []; + markers: { time: number; title?: string }[] = []; errors: { type: string; message: string }[] = []; private _inTime?: number; diff --git a/src/lib/components/Block.svelte b/src/lib/components/Block.svelte index 4d17639..5d648ee 100644 --- a/src/lib/components/Block.svelte +++ b/src/lib/components/Block.svelte @@ -250,7 +250,7 @@ {#if markers.length > 0}
{#each markers as marker, index} - {/each}
diff --git a/src/lib/components/BlockMarker.svelte b/src/lib/components/BlockMarker.svelte index f96ec8b..508c73a 100644 --- a/src/lib/components/BlockMarker.svelte +++ b/src/lib/components/BlockMarker.svelte @@ -4,13 +4,21 @@ export let time: number; export let index: number; + export let title = `Marker #${index + 1}`; export let disableSnapping = false; export let block: Block; export let tag = 'div'; - const { duration, width, scrubOverride, time: playheadTime } = getSequenceContext(); + const { duration, width, scrubOverride, time: playheadTime, formatTimeFn } = getSequenceContext(); + + //export let format = (value: number) => `${Math.round(value)}`; + + export let formatTitle = () => { + return `${title} (+${formatTimeFn(time)})`; + }; + $: timeToPixel = (1 / $duration) * $width; $: absoluteTime = time + block.absoluteInTime; @@ -25,7 +33,7 @@ >
`${Math.round(value)}`; + setSequenceContext({ time, duration, @@ -29,7 +31,8 @@ width, snapTimes, selectedHandle, - scrubOverride + scrubOverride, + formatTimeFn }); $: currentTime = $time; @@ -84,7 +87,7 @@ > - + diff --git a/src/lib/components/SequenceContext.ts b/src/lib/components/SequenceContext.ts index 9740562..034e22d 100644 --- a/src/lib/components/SequenceContext.ts +++ b/src/lib/components/SequenceContext.ts @@ -12,6 +12,7 @@ export type SequenceContext = { snapTimes: Writable; scrubOverride: Writable; sequence: Writable; + formatTimeFn: (time: number) => string; }; export const key = Symbol(); diff --git a/src/lib/components/Timebar.svelte b/src/lib/components/Timebar.svelte index 5b2511d..3145c81 100644 --- a/src/lib/components/Timebar.svelte +++ b/src/lib/components/Timebar.svelte @@ -4,9 +4,9 @@ import TimebarLabel from './TimebarLabel.svelte'; - const { time, duration, scrubOverride, selectedHandle } = getSequenceContext(); + const { time, duration, scrubOverride, selectedHandle, formatTimeFn: sequenceFormatTimeFn } = getSequenceContext(); - export let formatTimeFn = (value: number) => `${Math.round(value)}`; + export let formatTimeFn = sequenceFormatTimeFn; // We could instead have a store for timebarLabels that we loop over to allow showing n number of relevant times and control through context let extraTime: number | null = null; diff --git a/src/lib/components/TimebarLabel.svelte b/src/lib/components/TimebarLabel.svelte index 0b46b31..958a448 100644 --- a/src/lib/components/TimebarLabel.svelte +++ b/src/lib/components/TimebarLabel.svelte @@ -2,9 +2,9 @@ import { uniqueClasses } from '../utils'; import { getSequenceContext } from './SequenceContext'; - const { duration, width } = getSequenceContext(); + const { duration, width, formatTimeFn } = getSequenceContext(); - export let formatFn = (value: number) => `${Math.round(value)}`; + export let formatFn = formatTimeFn; export let time: number; let pos: number; diff --git a/src/routes/examples/markers/+page.svelte b/src/routes/examples/markers/+page.svelte index e1176e2..ee6ae27 100644 --- a/src/routes/examples/markers/+page.svelte +++ b/src/routes/examples/markers/+page.svelte @@ -5,6 +5,10 @@ import CustomLayer from './CustomLayer.svelte'; import { Label, Input } from 'flowbite-svelte'; + import dayjs from 'dayjs'; + import dayjsDuration from 'dayjs/plugin/duration'; + dayjs.extend(dayjsDuration); + const PromoterBlockTemplate = { key: 'promoter', type: 'promoter', @@ -32,43 +36,40 @@ markers: [ { time: 1000, - label: 'scene 1' + title: 'scene 1' }, { time: 1050, - label: 'scene 2' + title: 'scene 2' }, { time: 300, - label: 'scene 3' }, { time: 4000, - label: 'scene 4' }, { time: 5000, - label: 'scene 5' }, { time: 6000, - label: 'scene 6' + title: 'scene 6' }, { time: 7000, - label: 'scene 7' + title: 'scene 7' }, { time: 8000, - label: 'scene 8' + title: 'scene 8' }, { time: 9000, - label: 'scene 9' + title: 'scene 9' }, { time: 10000, - label: 'scene 10' + title: 'scene 10' } ] } @@ -181,6 +182,44 @@ /* TODO: toggle snapping */ + + const millisInSecond = 1000; + const millisInFrame = (framerate: number) => { + return (1 / framerate) * millisInSecond; + }; + + type formatTimeOptions = { + framerate?: number; + format?: string; + }; + const formatTimeFn = (time: number, options?: formatTimeOptions) => { + if (time === undefined || time === null) { + return ''; + } + + time = Math.floor(time); + + let format = options?.format ?? 'HH:mm:ss.SSS'; + const framerate = options?.framerate ?? 25; + + const duration = dayjs.duration(time, 'milliseconds'); + + if (format.includes('FF')) { + // calculate remaining frames after smallest unit in format string + + const millis = duration.milliseconds(); + const frames = Math.floor(millis / millisInFrame(framerate)); + + format = format.replace('FF', `${frames}`.padStart(2, '0')); + } + + if (format.includes('R')) { + format = format.replace('R', `${framerate}`); + } + + return `${duration.format(format)}`; +}; +
@@ -219,11 +258,11 @@ `${value}`} class="dark:bg-gray-900" />