Skip to content

Commit

Permalink
Merge pull request #415 from charlielee/378-seek-captured-frames
Browse files Browse the repository at this point in the history
Seek captured frames
  • Loading branch information
charlielee committed Aug 13, 2022
2 parents 47f9648 + 82e3d02 commit 1321ba2
Show file tree
Hide file tree
Showing 15 changed files with 190 additions and 138 deletions.
5 changes: 5 additions & 0 deletions src/common/Project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,8 @@ export const getHighlightedTrackItem = (
(_trackItem, index) =>
getTrackItemStartPosition(track, index) >= timelineIndex
);

export const getTrackItemTitle = (track: Track, trackItemIndex: number) =>
track.fileType === FileRefType.FRAME
? `Frame ${getTrackItemStartPosition(track, trackItemIndex) + 1}`
: track.trackItems[trackItemIndex].filePath;
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import "./CaptureButtonToolbar.css";

interface CaptureToolbarProps {
stopPlayback: () => void;
isPlaying: boolean;
liveViewVisible: boolean;
}

const CaptureButtonToolbar = ({
stopPlayback,
isPlaying,
liveViewVisible,
}: CaptureToolbarProps): JSX.Element => {
const dispatch = useDispatch();

Expand All @@ -28,7 +28,7 @@ const CaptureButtonToolbar = ({
icon={IconName.CAPTURE}
className="animation-toolbar__capture-button"
onClick={() => {
if (isPlaying) {
if (!liveViewVisible) {
stopPlayback();
}
dispatch(takePhoto());
Expand Down
11 changes: 5 additions & 6 deletions src/renderer/components/animator/Preview/Preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ interface PreviewWithContextProps {
}

interface PreviewProps extends PreviewWithContextProps {
isPlaying: boolean;
liveViewVisible: boolean;
timelineIndex: TimelineIndex | undefined;
}

const Preview = ({
take,
isPlaying,
liveViewVisible,
timelineIndex,
}: PreviewProps): JSX.Element => {
const dispatch = useDispatch();
Expand All @@ -32,8 +32,7 @@ const Preview = ({
hasCameraAccess: state.app.hasCameraAccess,
fileRefs: state.project.fileRefs,
}));

const [previewSrc, setPreviewSrc] = useState("");
const [previewSrc, setPreviewSrc] = useState<string | undefined>();

useEffect(() => {
const highlightedTrackItem = getHighlightedTrackItem(
Expand All @@ -56,7 +55,7 @@ const Preview = ({
/>
)}

{!isPlaying &&
{liveViewVisible &&
!currentDevice &&
(hasCameraAccess ? (
<h2>Select a Camera Source to begin!</h2>
Expand All @@ -69,7 +68,7 @@ const Preview = ({
</h2>
))}

<PreviewFrame src={previewSrc} hidden={!isPlaying} />
<PreviewFrame src={previewSrc} hidden={liveViewVisible} />
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,50 @@ import { useEffect, useRef, useState } from "react";
import "./PreviewFrame.css";

interface PreviewFrameProps {
src: string;
src: string | undefined;
hidden: boolean;
}

const PreviewFrame = ({ src, hidden }: PreviewFrameProps): JSX.Element => {
const canvasRef = useRef<HTMLCanvasElement>(null);
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
const imageRef = useRef<HTMLImageElement>(new Image());
const [imageWidth, setImageWidth] = useState(0);
const [imageHeight, setImageHeight] = useState(0);

useEffect(() => {
const drawImage = () => {
const context = canvasRef.current?.getContext("2d");
if (!context) {
return;
}

const image = new Image();
image.src = src;
context?.drawImage(image, 0, 0);
setDimensions({ width: image.naturalWidth, height: image.naturalHeight });
context?.drawImage(imageRef.current, 0, 0);
};

useEffect(() => {
if (src === undefined) {
return;
}

imageRef.current.src = src;
imageRef.current.addEventListener("load", drawImage);
setImageWidth(imageRef.current.naturalWidth);
setImageHeight(imageRef.current.naturalHeight);

return () => imageRef.current.removeEventListener("load", drawImage);
}, [src]);

useEffect(() => {
drawImage();
}, [imageWidth, imageHeight]);

return (
<canvas
className={classNames("preview-frame", {
"preview-frame--hidden": hidden,
})}
ref={canvasRef}
width={dimensions.width}
height={dimensions.height}
width={imageWidth}
height={imageHeight}
/>
);
};
Expand Down
20 changes: 18 additions & 2 deletions src/renderer/components/animator/Timeline/Timeline.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { useEffect, useRef } from "react";
import { TimelineIndex } from "../../../../common/Flavors";
import { Take } from "../../../../common/Project";
import {
getTrackItemStartPosition,
Take,
Track,
} from "../../../../common/Project";
import PlaybackContext from "../../../context/PlaybackContext/PlaybackContext";
import "./Timeline.css";
import TimelinePosition from "./TimelinePosition/TimelinePosition";
Expand All @@ -12,9 +16,14 @@ interface TimelineWithContextProps {

interface TimelineProps extends TimelineWithContextProps {
timelineIndex: TimelineIndex | undefined;
displayFrame: (timelineIndex: TimelineIndex | undefined) => void;
}

const Timeline = ({ take, timelineIndex }: TimelineProps): JSX.Element => {
const Timeline = ({
take,
timelineIndex,
displayFrame,
}: TimelineProps): JSX.Element => {
const frameTrack = take.frameTrack;
const timelineRef = useRef<HTMLDivElement>(null);

Expand All @@ -24,6 +33,9 @@ const Timeline = ({ take, timelineIndex }: TimelineProps): JSX.Element => {
}
}, [timelineIndex, frameTrack.trackItems]);

const onClickItem = (track: Track, trackItemIndex: number) =>
displayFrame(getTrackItemStartPosition(track, trackItemIndex));

return (
<div className="timeline" ref={timelineRef}>
<div className="timeline__inner">
Expand All @@ -35,6 +47,10 @@ const Timeline = ({ take, timelineIndex }: TimelineProps): JSX.Element => {
track={frameTrack}
key={frameTrack.id}
timelineIndex={timelineIndex}
onClickItem={(trackItemIndex) =>
onClickItem(frameTrack, trackItemIndex)
}
onClickLiveView={() => displayFrame(undefined)}
/>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
.timeline-live-view {
height: 100%;
min-width: calc(var(--width-16) * 8);
width: calc(var(--width-16) * 8);
padding: var(--width-16) var(--width-16) var(--width-16) 0;
min-width: calc(var(--width-16) * 7);
width: calc(var(--width-16) * 7);
margin-right: var(--width-16);
}

.timeline-live-view__button {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import "./TimelineLiveView.css";

interface TimelineLiveViewProps {
highlighted: boolean;
onClick: () => void;
}

const TimelineLiveView = ({ highlighted }: TimelineLiveViewProps) => {
const TimelineLiveView = ({ highlighted, onClick }: TimelineLiveViewProps) => {
return (
<div className="timeline-live-view">
<IconButton
Expand All @@ -17,7 +18,7 @@ const TimelineLiveView = ({ highlighted }: TimelineLiveViewProps) => {
})}
iconContainerClassName="timeline-live-view__button-icon-container"
title="Live View"
onClick={() => console.log("TODO")}
onClick={onClick}
active
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
display: flex;
height: var(--height-800);
width: max-content;
padding: var(--margin-100) 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { FileRefType, getFileRefById } from "../../../../../common/FileRef";
import { TimelineIndex } from "../../../../../common/Flavors";
import {
getHighlightedTrackItem,
getTrackItemTitle,
Track,
TrackItem,
} from "../../../../../common/Project";
Expand All @@ -17,11 +18,15 @@ import "./TimelineTrack.css";
interface TimelineTrackProps {
track: Track;
timelineIndex: TimelineIndex | undefined;
onClickItem: (trackItemIndex: number) => void;
onClickLiveView: () => void;
}

const TimelineTrack = ({
track,
timelineIndex,
onClickItem,
onClickLiveView,
}: TimelineTrackProps): JSX.Element => {
const { fileRefs } = useSelector((state: RootState) => state.project);
const [highlightedTrackItem, setHighlightedTrackItem] = useState<
Expand All @@ -38,18 +43,23 @@ const TimelineTrack = ({

{track.trackItems.length > 0 ? (
<>
{track.trackItems.map((trackItem) => {
{track.trackItems.map((trackItem, i) => {
return (
<TimelineTrackItem
title={getTrackItemTitle(track, i)}
dataUrl={getFileRefById(fileRefs, trackItem.id).location}
highlighted={highlightedTrackItem?.id === trackItem.id}
key={trackItem.id}
onClick={() => onClickItem(i)}
/>
);
})}

{track.fileType === FileRefType.FRAME && (
<TimelineLiveViewButton highlighted={timelineIndex === undefined} />
<TimelineLiveViewButton
highlighted={timelineIndex === undefined}
onClick={onClickLiveView}
/>
)}
</>
) : (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
.timeline-track-item {
position: relative;
height: 100%;
min-width: calc(var(--width-16) * 8);
width: calc(var(--width-16) * 8);
padding: var(--width-16) var(--width-16) var(--width-16) 0;
min-width: calc(var(--width-16) * 7);
width: calc(var(--width-16) * 7);
margin-right: var(--width-16);
}

.timeline-track-item__img {
position: absolute;
height: 100%;
width: 100%;
object-fit: cover;
Expand All @@ -15,3 +17,18 @@
.timeline-track-item__img--highlighted {
outline: var(--border-1) solid var(--ba-light);
}

.timeline-track-item__cover {
border-radius: var(--margin-025);
height: 100%;
position: absolute;
width: 100%;
}

.timeline-track-item__cover:hover {
background-color: var(--ba-light-20);
}

.timeline-track-item__cover:active {
background-color: var(--ba-light-30);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,42 @@ import { useEffect, useRef } from "react";
import "./TimelineTrackItem.css";

interface TimelineTrackItemProps {
title: string;
dataUrl: string;
highlighted: boolean;
onClick: () => void;
}

const TimelineTrackItem = ({
title,
dataUrl,
highlighted,
onClick,
}: TimelineTrackItemProps) => {
const trackItemRef = useRef<HTMLDivElement>(null);

useEffect(() => {
if (highlighted) {
trackItemRef.current?.scrollIntoView({
inline: "end",
});
// Note: this is a non-standard webkit method
(trackItemRef.current as any)?.scrollIntoViewIfNeeded();
}
}, [highlighted]);

return (
<div className="timeline-track-item" ref={trackItemRef}>
<div
className="timeline-track-item"
ref={trackItemRef}
onClick={onClick}
title={title}
>
<img
className={classNames("timeline-track-item__img", {
"timeline-track-item__img--highlighted": highlighted,
})}
src={dataUrl}
key={dataUrl}
/>
<div className="timeline-track-item__cover"></div>
</div>
);
};
Expand Down
6 changes: 4 additions & 2 deletions src/renderer/context/PlaybackContext/PlaybackContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@ import { TimelineIndex } from "../../../common/Flavors";
export interface PlaybackContextProps {
startPlayback: () => void;
stopPlayback: () => void;
displayFrame: (i: TimelineIndex | undefined) => void;
timelineIndex: TimelineIndex | undefined;
isPlaying: boolean;
liveViewVisible: boolean;
}

const defaultValue: PlaybackContextProps = {
startPlayback: () => undefined,
stopPlayback: () => undefined,
displayFrame: () => undefined,
timelineIndex: undefined,
isPlaying: false,
liveViewVisible: true,
};

const PlaybackContext = createContext<PlaybackContextProps>(defaultValue);
Expand Down

0 comments on commit 1321ba2

Please sign in to comment.