Skip to content
This repository was archived by the owner on Jun 21, 2025. It is now read-only.

Commit b0e8b55

Browse files
committed
feat: repeat
1 parent e919280 commit b0e8b55

File tree

9 files changed

+423
-66
lines changed

9 files changed

+423
-66
lines changed

packages/app/src/App.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ import Widgets from "./components/Widgets.vue";
1111
<TopMenubar />
1212
<div class="flex-grow flex flex-col overflow-hidden">
1313
<Preview :width="1600" :height="900" />
14-
<Tools class="flex-shrink-0" />
15-
<div class="flex-grow flex flex-row overflow-hidden">
16-
<div class="w-1/4 overflow-y-auto">
14+
<Tools class="flex-shrink-0 border-t" />
15+
<div class="h-[300px] flex flex-row overflow-hidden border-t">
16+
<div class="w-1/4 border-r overflow-y-auto">
1717
<Widgets
1818
:widgets="[{ type: 'Arc' }, { type: 'Arc' }, { type: 'Arc' }]"
1919
/>
Lines changed: 58 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,83 @@
11
<script setup lang="ts">
22
import { Motion } from "@vue-motion/lib";
33
/** @ts-expect-error virtual-import */
4-
import Animation from "virtual:user-main";
5-
import { ref, watch } from "vue";
6-
// import TestAnimation from './__test__/TestAnimation.vue'
4+
// import Animation from "virtual:user-main";
5+
import { ref, watch, onMounted, onUnmounted } from "vue";
6+
import TestAnimation from "./__test__/TestAnimation.vue";
77
88
const { width, height } = defineProps<{
99
width: number;
1010
height: number;
1111
}>();
1212
13-
const zoom = ref(((window.innerHeight - 40) * 0.6) / height);
13+
const zoom = ref(1);
14+
const container = ref<HTMLDivElement | null>(null);
1415
1516
function updateZoom() {
16-
zoom.value =
17-
((window.innerWidth - 40) * 0.6) / width <
18-
((window.innerHeight - 40) * 0.6) / height
19-
? ((window.innerHeight - 40) * 0.6) / height
20-
: ((window.innerWidth - 40) * 0.6) / width;
17+
if (!container.value) return;
18+
19+
const rect = container.value.getBoundingClientRect();
20+
const containerWidth = rect.width;
21+
const containerHeight = rect.height;
22+
23+
// Calculate aspect ratios
24+
const videoRatio = width / height;
25+
const containerRatio = containerWidth / containerHeight;
26+
27+
// Determine scaling based on aspect ratio comparison
28+
if (containerRatio > videoRatio) {
29+
// Container is wider than video - use height as constraint
30+
zoom.value = Math.min(containerHeight / height, 1);
31+
} else {
32+
// Container is taller than video - use width as constraint
33+
zoom.value = Math.min(containerWidth / width, 1);
34+
}
2135
}
2236
23-
window.addEventListener("resize", updateZoom);
37+
// Update zoom when container is resized
38+
const resizeObserver = new ResizeObserver(() => {
39+
updateZoom();
40+
});
41+
42+
onMounted(() => {
43+
if (container.value) {
44+
resizeObserver.observe(container.value);
45+
}
46+
});
2447
25-
watch(() => window.innerHeight, updateZoom);
48+
onUnmounted(() => {
49+
resizeObserver.disconnect();
50+
});
2651
2752
let dev: boolean;
2853
if (__D__) dev = true;
2954
else dev = false;
3055
</script>
3156

3257
<template>
33-
<div class="w-full h-[60%] flex overflow-hidden">
34-
<div class="w-full h-full flex items-center justify-center">
35-
<Motion
36-
id="motion"
37-
:width="width"
38-
:height="height"
39-
:scale="dev ? zoom : (null as any)"
40-
:min-width="dev ? width * zoom : (null as any)"
41-
:min-height="dev ? height * zoom : (null as any)"
58+
<div class="flex-grow flex overflow-hidden">
59+
<div
60+
ref="container"
61+
class="w-full h-full flex items-center justify-center p-4"
62+
>
63+
<div
64+
:style="{
65+
width: `${width}px`,
66+
height: `${height}px`,
67+
transform: `scale(${zoom})`,
68+
transformOrigin: 'center',
69+
}"
70+
class="relative"
4271
>
43-
<Animation />
44-
<!-- <TestAnimation /> -->
45-
</Motion>
72+
<Motion
73+
id="motion"
74+
:width="width"
75+
:height="height"
76+
class="max-w-full max-h-full"
77+
>
78+
<TestAnimation />
79+
</Motion>
80+
</div>
4681
</div>
4782
</div>
4883
</template>
Lines changed: 120 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,118 @@
11
<script setup lang="ts">
2+
import { ref, onMounted, onUnmounted } from "vue";
3+
import { usePlayer } from "@vue-motion/core";
4+
5+
const player = usePlayer();
6+
const isDragging = ref(false);
7+
const currentTime = ref(0);
8+
29
const { widget } = defineProps<{
310
widget: {
411
duration: number;
512
};
613
}>();
14+
15+
function handleMouseDown(e: MouseEvent) {
16+
isDragging.value = true;
17+
updateTimeFromMouseEvent(e);
18+
19+
// Prevent text selection while dragging
20+
e.preventDefault();
21+
}
22+
23+
function handleMouseMove(e: MouseEvent) {
24+
if (!isDragging.value) return;
25+
26+
// Find the SVG element and its container
27+
const svg = document.querySelector("svg");
28+
const container = document.querySelector(".overflow-auto");
29+
if (!svg || !container) return;
30+
31+
const containerRect = container.getBoundingClientRect();
32+
const scrollLeft = container.scrollLeft;
33+
34+
// Calculate x position considering scroll and container offset
35+
// Allow negative values for smooth dragging experience
36+
const rawX = e.clientX - containerRect.left - 40 + scrollLeft;
37+
const x = Math.max(0, Math.min(widget.duration * 100, rawX)); // Clamp to timeline width
38+
const totalWidth = widget.duration * 100; // 100px per second
39+
40+
// Auto scroll when dragging near edges or outside
41+
const scrollSpeed = 10;
42+
43+
if (e.clientX <= containerRect.left) {
44+
// Mouse is left of the container - scroll left faster based on distance
45+
const distance = containerRect.left - e.clientX;
46+
container.scrollLeft = Math.max(
47+
0,
48+
scrollLeft - scrollSpeed * (1 + distance / 50),
49+
);
50+
} else if (e.clientX >= containerRect.right) {
51+
// Mouse is right of the container - scroll right faster based on distance
52+
const distance = e.clientX - containerRect.right;
53+
container.scrollLeft = scrollLeft + scrollSpeed * (1 + distance / 50);
54+
}
55+
56+
// Calculate time in seconds (clamp between 0 and duration)
57+
const newTime = Math.max(
58+
0,
59+
Math.min(widget.duration, (x / totalWidth) * widget.duration),
60+
);
61+
currentTime.value = newTime;
62+
player.seek(newTime);
63+
}
64+
65+
function handleMouseUp() {
66+
isDragging.value = false;
67+
}
68+
69+
function updateTimeFromMouseEvent(e: MouseEvent) {
70+
const svg = e.currentTarget as SVGElement;
71+
const container = svg.closest(".overflow-auto");
72+
if (!container) return;
73+
74+
const containerRect = container.getBoundingClientRect();
75+
const scrollLeft = container.scrollLeft;
76+
77+
const rawX = e.clientX - containerRect.left - 40 + scrollLeft;
78+
const x = Math.max(0, Math.min(widget.duration * 100, rawX));
79+
const totalWidth = widget.duration * 100;
80+
81+
const newTime = Math.max(
82+
0,
83+
Math.min(widget.duration, (x / totalWidth) * widget.duration),
84+
);
85+
currentTime.value = newTime;
86+
player.seek(newTime);
87+
}
88+
89+
// Add global mouse event listeners
90+
onMounted(() => {
91+
window.addEventListener("mousemove", handleMouseMove);
92+
window.addEventListener("mouseup", handleMouseUp);
93+
});
94+
95+
onUnmounted(() => {
96+
window.removeEventListener("mousemove", handleMouseMove);
97+
window.removeEventListener("mouseup", handleMouseUp);
98+
});
799
</script>
8100

9101
<template>
10-
<div class="w-full h-full overflow-scroll">
11-
<div class="mx-10">
102+
<div class="w-full h-full overflow-auto">
103+
<div class="mx-10 relative">
12104
<svg
105+
@mousedown="handleMouseDown"
13106
:style="{
14-
width: `${widget.duration * 10}px`,
15-
textAlign: 'center',
107+
width: `${widget.duration * 100}px`,
108+
height: '60px',
16109
}"
110+
class="cursor-col-resize"
17111
>
112+
<!-- Timeline marks - 10 marks per second -->
18113
<line :x1="0" :y1="0" :x2="0" :y2="35" stroke="grey" stroke-width="1" />
19114
<line
20-
v-for="i in widget.duration * 10 - 10"
115+
v-for="i in widget.duration * 10"
21116
:key="i"
22117
:x1="i * 10"
23118
:y1="0"
@@ -26,11 +121,30 @@ const { widget } = defineProps<{
26121
stroke="grey"
27122
stroke-width="1"
28123
/>
124+
125+
<!-- Time labels - one per second -->
29126
<text :x="0" :y="50">0</text>
30-
<text v-for="i in widget.duration - 1" :key="i" :x="i * 100" :y="50">
127+
<text v-for="i in widget.duration" :key="i" :x="i * 100 - 10" :y="50">
31128
{{ i }}
32129
</text>
130+
131+
<!-- Current time indicator -->
132+
<line
133+
:x1="currentTime * 100"
134+
:y1="0"
135+
:x2="currentTime * 100"
136+
:y2="50"
137+
stroke="#2196f3"
138+
stroke-width="2"
139+
/>
140+
<circle :cx="currentTime * 100" :cy="0" r="4" fill="#2196f3" />
33141
</svg>
34142
</div>
35143
</div>
36144
</template>
145+
146+
<style scoped>
147+
.cursor-col-resize {
148+
cursor: col-resize;
149+
}
150+
</style>
Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
11
<script setup lang="ts">
2-
import { Arc } from "@vue-motion/lib";
3-
import { useMotion } from "@vue-motion/core";
2+
import { Arc, Circle } from "@vue-motion/lib";
3+
import { useMotion, usePlayer, useWidget } from "@vue-motion/core";
4+
import { onMounted } from "vue";
5+
import { CircleIns } from "@vue-motion/lib";
46
57
const motion = useMotion();
68
motion.width.value = 1600;
79
motion.height.value = 900;
10+
const { play } = usePlayer();
11+
12+
const arc = useWidget<CircleIns>();
13+
14+
onMounted(() => {
15+
arc.move(200, 300, {
16+
duration: 10,
17+
});
18+
play();
19+
});
820
</script>
921

1022
<template>
11-
<Arc :radius="100" />
23+
<Circle :radius="100" :widget="arc" />
1224
</template>

packages/app/src/main.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,20 @@ import "./output.css";
33
import App from "./App.vue";
44
import Preview from "./components/Preview.vue";
55
import "font-awesome/css/font-awesome.css";
6+
import { createPlayer } from "@vue-motion/core";
67
// /**@ts-ignore */
78
// import router from 'virtual:router'
89
// /**@ts-ignore */
910
// import player from 'virtual:player'
1011

1112
if (__D__) {
12-
createApp(App).mount("#app");
13+
createApp(App)
14+
.use(
15+
createPlayer({
16+
studio: true,
17+
}),
18+
)
19+
.mount("#app");
1320
} else {
1421
createApp(Preview).mount("#app");
1522
}

0 commit comments

Comments
 (0)