aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pages/editor.tsx57
-rw-r--r--styles/keyframes.css5
2 files changed, 46 insertions, 16 deletions
diff --git a/pages/editor.tsx b/pages/editor.tsx
index 5e0f2b0..fee3177 100644
--- a/pages/editor.tsx
+++ b/pages/editor.tsx
@@ -252,6 +252,13 @@ function getFrameAtOffset(offset: number) {
return frame;
}
+function getOffsetAtFrame(frame: number) {
+ var timeline = document.querySelector('.timeline .timelineInner');
+ var currentOffset = timeline.scrollLeft;
+ var offset = zoomToPx(global.timeline.zoom.value) * frame - currentOffset;
+ return offset;
+}
+
function calculateSelectionOffsets(left: slideTypes, right: slideTypes) {
var offsets = {
default: { left: -6, right: 6 },
@@ -619,6 +626,29 @@ function GhostLoop(props: {
</div>;
}
+function getGhostParams(x: number, y: number) {
+ var frame = getFrameAtOffset(x);
+
+ var springValues = {
+ x,
+ y,
+ frame,
+ frameEnd: 0,
+ offsetWeight: 1,
+ };
+
+ var scrubberX = getOffsetAtFrame(global.timeline.frame.value);
+ if (Math.abs(scrubberX + 0.5 * zoomToPx(global.timeline.zoom.value) - x) < 10) {
+ springValues.x = scrubberX;
+ springValues.frame = global.timeline.frame.value;
+ springValues.offsetWeight = 0;
+ }
+
+ springValues.frameEnd = springValues.frame + 5;
+
+ return springValues;
+}
+
function TimelineEditor() {
var timelineZoom = useHookstate(global).timeline.zoom;
var workingTimeline = useHookstate(global).timeline.workingTimeline;
@@ -769,22 +799,18 @@ function TimelineEditor() {
y: 0,
frame: 0,
frameEnd: 0,
+ offsetWeight: 1,
config: { mass: 0.5, tension: 500, friction: 20 },
}));
useEffect(() => {
timelineRef.current.addEventListener('mousemove', (e: MouseEvent) => {
+ if ((e.buttons & (1 << 0)) > 0) return;
+
var rect = timelineRef.current.getBoundingClientRect();
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
- var frame = getFrameAtOffset(x);
- if ((e.buttons & (1 << 0)) == 0) {
- ghostApi.start({
- x,
- y,
- frame,
- frameEnd: frame + 5,
- });
- }
+
+ ghostApi.start(getGhostParams(x, y));
});
}, []);
// place new slide
@@ -814,16 +840,16 @@ function TimelineEditor() {
global.update.refreshLiveTimeline.value();
}
} else {
- ghostApi.start({ frame: getFrameAtOffset(x), x, y });
+ var ghostParams = getGhostParams(x, y);
+ ghostApi.start(ghostParams);
if (last) {
var offset = -4; // keyframe offset
- var frame = getFrameAtOffset(x + offset) - 0.5;
+ var frame = ghostParams.offsetWeight
+ ? getFrameAtOffset(ghostParams.x + offset) - 0.5
+ : ghostParams.frame;
var slide = new toolToSlide[tool.value](Math.round(frame));
global.timeline.workingTimeline[global.timeline.workingTimeline.value.length].set(slide);
- keyframeInAnimations[slide.id] = {
- x: frame,
- y,
- };
+ keyframeInAnimations[slide.id] = { x: frame, y };
global.update.refreshLiveTimeline.value();
}
}
@@ -876,6 +902,7 @@ function TimelineEditor() {
'--x': ghost.x,
'--frame': ghost.frame,
'--frame-end': ghost.frameEnd,
+ '--offset-weight': ghost.offsetWeight,
} as unknown as CSSProperties}
children={tool.value == 'loop'
? <GhostLoop begin={0} end={0} />
diff --git a/styles/keyframes.css b/styles/keyframes.css
index 935642f..3672421 100644
--- a/styles/keyframes.css
+++ b/styles/keyframes.css
@@ -57,7 +57,10 @@
.keyframe.ghost .outline { opacity: .7; }
#ghost {
- transform: translate(-16px, -16px);
+ --negative-offset-weight: calc(1 - var(--offset-weight));
+ --offset-x: calc(0.5 * var(--zoom) * var(--negative-offset-weight) * 1px - 12px * var(--negative-offset-weight) + -16px * var(--offset-weight));
+ transform: translateX(var(--offset-x)) translateY(-16px);
+
top: calc(var(--y) * 1px);
left: calc(var(--zoom) * var(--frame) * 1px);