From 2334f305f1f73a6a2153702b779337add33d50ed Mon Sep 17 00:00:00 2001 From: Anton Date: Thu, 8 Feb 2024 22:23:34 +0300 Subject: [PATCH] video piece under cursor selection --- src/components/VideoEditorOverlay.tsx | 4 +-- src/components/VideoProgress.tsx | 39 ++++++++++++++++++------- src/state/videoEditor.ts | 42 ++++++++++++++++++--------- src/state/videoEditorProgress.ts | 35 ++++++++++++++++++++++ 4 files changed, 94 insertions(+), 26 deletions(-) create mode 100644 src/state/videoEditorProgress.ts diff --git a/src/components/VideoEditorOverlay.tsx b/src/components/VideoEditorOverlay.tsx index accc324..20902d9 100644 --- a/src/components/VideoEditorOverlay.tsx +++ b/src/components/VideoEditorOverlay.tsx @@ -9,8 +9,8 @@ export const VideoEditorOverlay: React.FC = observer(() => { return (
- {timeToStr(state.video.currentTime)}
- {state.video.currentFrame}{state.video.isAtMark ? '(mark)' : ''}
+ {timeToStr(state.video.frameToTime(state.video.progress.start))}+{state.video.progress.start} to {timeToStr(state.video.frameToTime(state.video.progress.end))}+{state.video.progress.end}
+ {timeToStr(state.video.currentTime)}+{state.video.currentFrame}{state.video.isAtMark ? '(mark)' : ''}
); }); \ No newline at end of file diff --git a/src/components/VideoProgress.tsx b/src/components/VideoProgress.tsx index 4660308..a408746 100644 --- a/src/components/VideoProgress.tsx +++ b/src/components/VideoProgress.tsx @@ -8,35 +8,54 @@ import { timeToStr } from '../utils/time'; export const VideoProgress: React.FC = observer(() => { + const values = state.video.progress.values; + + // console.dir(JSON.stringify({ + // count: state.video.progress.count, + // values, + // })); + + function findChangedValue(changedValues: number[]): number { + // we never know which handle rc-slider decides to move + // so whatever handle has changed, it must be our target + for (const value of changedValues as number[]) { + if (!values.includes(value)) + return value; + } + return changedValues[0]; + } + return ( <> {/*
*/} { - state.video.seekToTime(values as number); - // console.log('Change:', JSON.stringify(values)); + range + allowCross={true} + onChange={(changedValues) => { + const changedValue = findChangedValue(changedValues as number[]); + console.log(`Change: ${JSON.stringify(changedValues)}, ${changedValue}`); + state.video.seekToFrame(changedValue); }} // onChangeComplete={(v) => { // console.log('AfterChange:', v); // }} min={0} - max={state.video.metadata ? state.video.metadata?.duration : 0} - value={state.video.currentTime} - included={false} + max={state.video.lastFrame} + count={state.video.progress.count} + value={values} + // included={false} keyboard={false} - step={state.video.metadata ? 1 / state.video.metadata?.frameRate : undefined} + step={1} marks={ state.video.marks.reduce( (acc, mark) => { const time = state.video.frameToTime(mark); - acc[time] = <>{timeToStr(time)}
{mark}; return acc; + acc[mark] = <>{timeToStr(time)}
{mark}; return acc; }, {} as Record ) } - trackStyle={{ backgroundColor: 'red', height: 10 }} /> - {/*
*/} ); }); diff --git a/src/state/videoEditor.ts b/src/state/videoEditor.ts index 9b1aeff..b3ccaba 100644 --- a/src/state/videoEditor.ts +++ b/src/state/videoEditor.ts @@ -1,4 +1,5 @@ import { action, computed, makeObservable, observable } from 'mobx'; +import { VideoEditorProgressState } from './videoEditorProgress'; export type VideoMetadata = { frameRate: number; @@ -7,6 +8,9 @@ export type VideoMetadata = { }; export class VideoEditorState { + + public progress = new VideoEditorProgressState(this); + @observable public url: string | undefined; @@ -40,6 +44,11 @@ export class VideoEditorState { return (frame + 0.4) / this.metadata.frameRate + this.metadata.firstFrameTime; } + @computed + get lastFrame(): number { + return this.metadata ? this.timeToFrame(this.metadata.duration) : 0; + } + @computed get currentFrame(): number { return this.timeToFrame(this.currentTime); @@ -66,7 +75,7 @@ export class VideoEditorState { @action setMetadata(metadata: VideoMetadata) { this.metadata = metadata; - this.marks = [0, this.timeToFrame(metadata.duration)]; + this.marks = [0, this.lastFrame]; } @action @@ -97,12 +106,7 @@ export class VideoEditorState { if (!this.metadata) return; - const currentFrame = this.currentFrame.valueOf(); - const marks = this.marks.filter((m) => m < currentFrame); - const prevMark = marks.length ? marks[marks.length - 1] : 0; - // console.dir({ currentFrame, marks, prevMark }); - if (marks) - this.seekToFrame(prevMark); + this.seekToFrame(this.getPrevMark(this.currentFrame)); } @action @@ -110,12 +114,22 @@ export class VideoEditorState { if (!this.metadata) return; - const currentFrame = this.currentFrame.valueOf(); - const marks = this.marks.filter((m) => m > currentFrame); - // console.dir({ currentFrame, marks }); - if (marks.length) - this.seekToFrame(marks[0]); - else - this.seekToTime(this.metadata?.duration); + this.seekToFrame(this.getNextMark(this.currentFrame)); + } + + public getPrevMark(frame: number): number { + if (!this.metadata) + return frame; + + const marks = this.marks.filter((m) => m < frame); + return marks.length ? marks[marks.length - 1] : 0; + } + + public getNextMark(frame: number): number { + if (!this.metadata) + return frame; + + const marks = this.marks.filter((m) => m > frame); + return marks.length ? marks[0] : this.lastFrame; } } \ No newline at end of file diff --git a/src/state/videoEditorProgress.ts b/src/state/videoEditorProgress.ts new file mode 100644 index 0000000..ff535f0 --- /dev/null +++ b/src/state/videoEditorProgress.ts @@ -0,0 +1,35 @@ +import { action, computed, makeObservable, observable } from 'mobx'; +import { VideoEditorState } from './videoEditor'; + +export type VideoMetadata = { + frameRate: number; + firstFrameTime: number; + duration: number; +}; + +export class VideoEditorProgressState { + + @observable + public count: number = 2; + + constructor( + private readonly editor: VideoEditorState + ) { + makeObservable(this); + } + + @computed + get values(): number[] { + return [this.start, this.editor.currentFrame, this.end]; + } + + @computed + public get start(): number { + return this.editor.getPrevMark(this.editor.currentFrame); + } + + @computed + public get end(): number { + return this.editor.getNextMark(this.editor.currentFrame); + } +} \ No newline at end of file