aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pages/editor.tsx106
-rw-r--r--readme.md5
-rw-r--r--styles/editor.css20
3 files changed, 87 insertions, 44 deletions
diff --git a/pages/editor.tsx b/pages/editor.tsx
index 01cd989..36a4c66 100644
--- a/pages/editor.tsx
+++ b/pages/editor.tsx
@@ -25,44 +25,86 @@ import { mdiCursorDefault } from '@mdi/js';
function TimelineEditor(props: {
player: TimedVideoPlayer;
}) {
- var frames = [...new Array(props.player.timeline?.framecount || 0)].map((el, i) =>
- <div className='frame'>
- <div className='line posabs abscenterh b0' />
- <span className='timecode nosel numbers posabs abscenterh'>
- {props.player?.frameToTimestampString(i + 1)}
- </span>
+ var keyframes = props.player?.timeline?.slides.map(slide =>
+ <div className='frame posabs' style={{ '--frame': slide.frame.toString() } as CSSProperties}>
<div className='keyframeWrapper posabs abscenterh'>
- {(() => {
- var slide = props.player?.timeline?.slides.find(slide => slide.frame == i + 1);
- if (slide) {
- if (slide.type == 'loop') {
- return <Loop length={slide.frame - (slide as loopSlide).beginFrame} />;
- } else {
- return <SlideKeyframe type={slide.type} />;
- }
- }
- })()}
+ {slide.type == 'loop'
+ ? <Loop length={slide.frame - (slide as loopSlide).beginFrame} />
+ : <SlideKeyframe type={slide.type} />}
</div>
</div>
);
+ useEffect(() => {
+ var canvas = document.getElementById('timeScaleCanvas') as HTMLCanvasElement;
+ var ctx = canvas.getContext('2d');
+
+ var mouseX = 0;
+ var mouseY = 0;
+
+ window.addEventListener('mousemove', e => {
+ var rect = canvas.getBoundingClientRect();
+ mouseX = e.clientX - rect.x;
+ mouseY = e.clientY - rect.y;
+ });
+
+ var css = (varname: string) => getComputedStyle(document.body).getPropertyValue(varname).trim();
+ var textColor = css('--c100');
+ var lineColor = css('--c300');
+
+ function draw() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ var offset = document.querySelector('.timeline .timelineInner').scrollLeft;
+
+ var d = true;
+ var frameWidth = Number(
+ getComputedStyle(document.querySelector('.timeline')).getPropertyValue('--zoom').trim(),
+ );
+ var a = 1;
+ var ns = [300, 150, 120, 90, 60, 30, 30, 30, 15, 15, 15, 15, 15];
+ var everyN = ns[Math.floor(frameWidth)];
+ for (var x = -offset; x < canvas.width + offset; x += frameWidth) {
+ ctx.fillStyle = d ? textColor : lineColor;
+ if (a % everyN == 0) ctx.fillStyle = '#ff00ff';
+ ctx.fillRect(x, 0, frameWidth, canvas.height);
+ d = !d;
+ a++;
+ }
+
+ requestAnimationFrame(draw);
+ }
+ draw();
+
+ function onresize() {
+ var size = document.querySelector('.timeline .timelineInner .keyframes');
+ canvas.width = size.clientWidth;
+ canvas.height = size.clientHeight;
+ }
+ onresize();
+ window.addEventListener('resize', onresize);
+ }, []);
+
return <>
- <div className='scrubber posabs v0'>
- <svg
- width='20'
- height='28'
- viewBox='0 0 20 28'
- xmlns='http://www.w3.org/2000/svg'
- className='head posabs t0 abscenterh'
- >
- <path
- d='M0 4C0 1.79086 1.79086 0 4 0H16C18.2091 0 20 1.79086 20 4V17.3431C20 18.404 19.5786 19.4214 18.8284 20.1716L11 28H9L1.17157 20.1716C0.421426 19.4214 0 18.404 0 17.3431V4Z'
- />
- </svg>
- <div className='needle posabs a0' />
- <div className='frameOverlay posabs v0' />
+ <canvas className='timeScale posabs a0' id='timeScaleCanvas' />
+ <div className='timelineInner posabs a0'>
+ <div className='scrubber posabs v0'>
+ <svg
+ width='20'
+ height='28'
+ viewBox='0 0 20 28'
+ xmlns='http://www.w3.org/2000/svg'
+ className='head posabs t0 abscenterh'
+ >
+ <path
+ d='M0 4C0 1.79086 1.79086 0 4 0H16C18.2091 0 20 1.79086 20 4V17.3431C20 18.404 19.5786 19.4214 18.8284 20.1716L11 28H9L1.17157 20.1716C0.421426 19.4214 0 18.404 0 17.3431V4Z'
+ />
+ </svg>
+ <div className='needle posabs a0' />
+ <div className='frameOverlay posabs v0' />
+ </div>
+ <div className='keyframes'>{keyframes}</div>
</div>
- <div className='frames'>{frames}</div>
</>;
}
@@ -71,7 +113,7 @@ export default function Index() {
var rerender = () => setDummy(!dummy);
var [player, setPlayer] = useState(new TimedVideoPlayer());
- var [timelineZoom, setTimelineZoom] = useState(4);
+ var [timelineZoom, setTimelineZoom] = useState(0.2);
useEffect(() => {
var videoEL = document.getElementById('player') as HTMLVideoElement;
diff --git a/readme.md b/readme.md
index ab6f2f5..2f1ba92 100644
--- a/readme.md
+++ b/readme.md
@@ -28,6 +28,11 @@ when I'm working on them/are finished:
timeline.json file. Maybe also a feature where you could import recorded
presentation timings for adding a voice-over and re-exporting the video as a
standalone video.
+- **Manim add-on library**
+ Would allow adding of something like `pressure.slide()` or
+ `pressure.slide('speedChange', 0.7)` to your Manim scene source and export it
+ using a simple python cli script, or alongside the regular Manim renderer if
+ that's possible.
## designs
diff --git a/styles/editor.css b/styles/editor.css
index 9e315ca..4b1e006 100644
--- a/styles/editor.css
+++ b/styles/editor.css
@@ -156,36 +156,32 @@
margin: 4px;
}
-.appGrid .timeline {
- --zoom: 4;
-}
-
-.appGrid .timeline .frames {
+.appGrid .timeline .keyframes {
height: 100%;
}
-.appGrid .timeline .frames .frame {
+.appGrid .timeline .keyframes .frame {
background-color: transparent;
height: 100%;
width: calc(var(--zoom) * 1px);
display: inline-block;
border-radius: 2px 2px 0 0;
- position: relative;
overflow: visible;
+ left: calc(var(--zoom) * (var(--frame) - 1) * 1px);
}
-.appGrid .timeline .frames .frame:nth-child(30n) .line {
+.appGrid .timeline .keyframes .frame:nth-child(30n) .line {
background-color: var(--c300);
top: 28px;
width: 2px;
border-radius: 2px 2px 0 0;
}
-.appGrid .timeline .frames .frame .timecode {
+.appGrid .timeline .keyframes .frame .timecode {
display: none;
}
-.appGrid .timeline .frames .frame .keyframeWrapper {
+.appGrid .timeline .keyframes .frame .keyframeWrapper {
line-height: 0;
top: 44px;
z-index: 999;
@@ -194,7 +190,7 @@
height: 24px;
}
-.appGrid .timeline .frames .frame:nth-child(30n) .timecode {
+.appGrid .timeline .keyframes .frame:nth-child(30n) .timecode {
color: var(--c700);
font-weight: 500;
line-height: 1;
@@ -202,7 +198,7 @@
top: 12px;
}
-.appGrid .timeline {
+.appGrid .timeline .timelineInner {
overflow-y: hidden;
overflow-x: scroll;
white-space: nowrap;