Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(web): drag in timeline blog #802

Merged
merged 11 commits into from
Nov 16, 2023
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,10 @@ const TimelineEditor = ({
toggleIsPlaying,
toggleIsPlayingReversed,
toggleIsPause,
handleOnMouseDown,
handleOnClick,
handleOnMouseMove,
handleOnEndMove,
handleOnStartMove,
} = useHooks({
currentTime,
range,
Expand Down Expand Up @@ -127,7 +129,10 @@ const TimelineEditor = ({
<CurrentTime>{currentTime && formattedCurrentTime}</CurrentTime>
</TimelineControl>
<TimelineSlider>
<ScaleList onClick={handleOnClick} onMouseDown={handleOnMouseDown}>
<ScaleList
onClick={handleOnClick}
onMouseMove={handleOnMouseMove}
onMouseUp={handleOnEndMove}>
{[...Array(11)].map((_, idx) => (
<Scale key={idx}>
{idx === 0 ? (
Expand All @@ -150,13 +155,15 @@ const TimelineEditor = ({
))}
</ScaleList>
<IconWrapper
onMouseDown={handleOnStartMove}
isPlaying={isPlaying || isPlayingReversed || isPause}
style={{
left: `${sliderPosition}px`,
}}>
<Icon icon="slider" />
</IconWrapper>
</TimelineSlider>
<div />
</Wrapper>
);
};
Expand Down Expand Up @@ -253,19 +260,22 @@ const TimelineSlider = styled.div`
width: 100%;
border-radius: 0px 0 8px 8px;
position: relative;
overflow: hidden;
`;

const ScaleList = styled.div`
width: 99%;
display: flex;
height: 38px;
align-items: flex-end;
padding-left: 17px;
position: absolute;
left: 18px;
right: -12px;
`;

const IconWrapper = styled.div<{ isPlaying: boolean }>`
position: absolute;
top: 4px;
user-select: none;
color: ${({ isPlaying, theme }) => (isPlaying ? theme.select.main : "")};
`;

Expand All @@ -275,6 +285,7 @@ const Scale = styled.div`
margin: 0 auto;
flex: 1;
text-align: center;
width: calc(100% / 11);
`;

const ScaleLabel = styled.div`
Expand Down
120 changes: 67 additions & 53 deletions web/src/beta/lib/core/StoryPanel/Block/builtin/Timeline/hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";

Expand Down Expand Up @@ -69,13 +70,13 @@ export default ({
const [activeBlock, setActiveBlock] = useState("");
const [isPlayingReversed, setIsPlayingReversed] = useState(false);
const [isPlaying, setIsPlaying] = useState(false);

const [committer, setCommiter] = useState<TimelineCommitter>({
source: "storyTimelineBlock",
id: blockId,
});

const [isOpen, setIsOpen] = useState(false);
const [isDragging, setIsDragging] = useState(false);

const [selected, setSelected] = useState("1min/sec");
const formattedCurrentTime = useMemo(() => {
Expand Down Expand Up @@ -188,9 +189,9 @@ export default ({
setIsPlaying?.(false);
}
committer?.id && onPause?.(committer.id);
setIsPause(!isPause);
setIsPause(true);
}
}, [committer.id, inEditor, isPause, isPlaying, isPlayingReversed, onPause]);
}, [committer.id, inEditor, isPlaying, isPlayingReversed, onPause]);

const handleTick = useCallback(
(current: Date) => {
Expand All @@ -214,6 +215,63 @@ export default ({
if (isPlayingReversed) onTimeChange?.(new Date(range?.end), committer.id);
}, [range, onTimeChange, committer.id, isPlayingReversed]);

const handleOnDrag: TimeEventHandler = useCallback(
(time: Date, committerId: string) => {
onTimeChange?.(time, committerId);
setCurrentTime?.(getNewDate(new Date(time)).getTime());
handleOnPause();
},
[handleOnPause, onTimeChange, setCurrentTime],
);

const [target, setTarget] = useState<HTMLElement | null>(null);
const distX = useRef<number>(0);
const distY = useRef<number>(0);

const handleOnStartMove = (e: React.MouseEvent) => {
const targetElement = e.currentTarget as HTMLElement;
setTarget(targetElement);
const evt = e;
distX.current = Math.abs(targetElement.offsetLeft - evt.clientX);
distY.current = Math.abs(targetElement.offsetTop - evt.clientY);
targetElement.style.pointerEvents = "none";
};

const handleOnEndMove = useCallback(() => {
if (target) {
target.style.pointerEvents = "initial";
setTarget(null);
setIsPause(false);
}
}, [target]);

const handleOnMouseMove = useCallback(
(e: React.MouseEvent) => {
if (!handleOnDrag || !e.target || !range) {
return;
}
if (target && target.style.pointerEvents === "none") {
const evt = e;
let newPosition = evt.clientX - distX.current;
newPosition = Math.max(newPosition, 16);
newPosition = Math.min(newPosition, 372);
target.style.left = `${newPosition}px`;
const conv = convertPositionToTime(e as unknown as MouseEvent, range.start, range.end);
committer?.id && handleOnDrag(new Date(conv), committer?.id);
}
},
[committer?.id, handleOnDrag, range, target],
);

const handleOnClick: MouseEventHandler = useCallback(
e => {
if (range) {
const conv = convertPositionToTime(e as unknown as MouseEvent, range.start, range.end);
committer?.id && handleOnDrag(new Date(conv), committer?.id);
}
},
[range, committer?.id, handleOnDrag],
);
const handleTimelineCommitterChange = useCallback(
(committer: TimelineCommitter) => {
if (
Expand All @@ -226,7 +284,6 @@ export default ({
setIsPause(false);
setIsPlaying(false);
setIsPlayingReversed(false);

const currentTimeValue = timelineValues?.currentTime ?? "";
timelineValues
? setCurrentTime?.(getNewDate(new Date(currentTimeValue.substring(0, 19))).getTime())
Expand Down Expand Up @@ -278,61 +335,17 @@ export default ({
removeTickEventListener,
]);

const handleOnDrag: TimeEventHandler = useCallback(
(time: Date, committerId: string) => {
onTimeChange?.(time, committerId);
setCurrentTime?.(currentTime);
},
[currentTime, onTimeChange, setCurrentTime],
);

const handleOnMouseDown = useCallback(() => {
setIsDragging(true);
}, []);

const handleOnMouseUp = useCallback(() => {
setIsDragging(false);
}, []);

const handleOnMouseMove: MouseEventHandler = useCallback(
e => {
if (isDragging || !handleOnDrag || !e.target || !range) {
return;
}
if (isPlaying || isPlayingReversed) {
const conv = convertPositionToTime(e as unknown as MouseEvent, range.start, range.end);
committer?.id && handleOnDrag(new Date(conv), committer?.id);
}
},
[isDragging, handleOnDrag, range, isPlaying, isPlayingReversed, committer?.id],
);

useEffect(() => {
window.addEventListener("mouseup", handleOnMouseUp, { passive: true });
return () => {
window.removeEventListener("mouseup", handleOnMouseUp);
};
}, [handleOnMouseUp]);

const handleOnClick: MouseEventHandler = useCallback(
e => {
if ((isPlaying || isPlayingReversed || isPause) && range) {
const conv = convertPositionToTime(e as unknown as MouseEvent, range.start, range.end);
committer?.id && handleOnDrag(new Date(conv), committer?.id);
}
},
[isPlaying, isPlayingReversed, isPause, range, committer?.id, handleOnDrag],
);

const sliderPosition = useMemo(() => {
if (range) {
if (!inEditor) {
const totalRange = range?.end - range.start;
const currentPosition = currentTime - range.start;
let positionPercentage = (currentPosition / totalRange) * 370;
let positionPercentage = (currentPosition / totalRange) * 356 + 16;

positionPercentage = Math.max(positionPercentage, 16);
positionPercentage = Math.round(positionPercentage);
positionPercentage = Math.max(positionPercentage, 16);
positionPercentage = Math.min(positionPercentage, 372);

return positionPercentage;
}
}
Expand All @@ -355,8 +368,9 @@ export default ({
toggleIsPlayingReversed: handleOnPlayReversed,
toggleIsPause: handleOnPause,
handleOnSpeedChange,
handleOnMouseDown,
handleOnMouseMove,
handleOnClick,
handleOnEndMove,
handleOnStartMove,
};
};
Loading