+
+
+
+
diff --git a/packages/app/src/components/__test__/TestAnimation.vue b/packages/app/src/components/__test__/TestAnimation.vue
index 8db92d8..4bb8ce8 100644
--- a/packages/app/src/components/__test__/TestAnimation.vue
+++ b/packages/app/src/components/__test__/TestAnimation.vue
@@ -1,12 +1,24 @@
-
+
diff --git a/packages/app/src/main.ts b/packages/app/src/main.ts
index 742fcc2..9b552b1 100644
--- a/packages/app/src/main.ts
+++ b/packages/app/src/main.ts
@@ -3,13 +3,20 @@ import "./output.css";
import App from "./App.vue";
import Preview from "./components/Preview.vue";
import "font-awesome/css/font-awesome.css";
+import { createPlayer } from "@vue-motion/core";
// /**@ts-ignore */
// import router from 'virtual:router'
// /**@ts-ignore */
// import player from 'virtual:player'
if (__D__) {
- createApp(App).mount("#app");
+ createApp(App)
+ .use(
+ createPlayer({
+ studio: true,
+ }),
+ )
+ .mount("#app");
} else {
createApp(Preview).mount("#app");
}
diff --git a/packages/app/src/output.css b/packages/app/src/output.css
index 375fe82..f67a177 100644
--- a/packages/app/src/output.css
+++ b/packages/app/src/output.css
@@ -554,6 +554,64 @@ video {
display: none;
}
+.\!container {
+ width: 100% !important;
+}
+
+.container {
+ width: 100%;
+}
+
+@media (min-width: 640px) {
+ .\!container {
+ max-width: 640px !important;
+ }
+
+ .container {
+ max-width: 640px;
+ }
+}
+
+@media (min-width: 768px) {
+ .\!container {
+ max-width: 768px !important;
+ }
+
+ .container {
+ max-width: 768px;
+ }
+}
+
+@media (min-width: 1024px) {
+ .\!container {
+ max-width: 1024px !important;
+ }
+
+ .container {
+ max-width: 1024px;
+ }
+}
+
+@media (min-width: 1280px) {
+ .\!container {
+ max-width: 1280px !important;
+ }
+
+ .container {
+ max-width: 1280px;
+ }
+}
+
+@media (min-width: 1536px) {
+ .\!container {
+ max-width: 1536px !important;
+ }
+
+ .container {
+ max-width: 1536px;
+ }
+}
+
.absolute {
position: absolute;
}
@@ -595,8 +653,28 @@ video {
height: 2rem;
}
-.h-\[60\%\] {
- height: 60%;
+.h-\[100px\] {
+ height: 100px;
+}
+
+.h-\[40\%\] {
+ height: 40%;
+}
+
+.h-\[5\%\] {
+ height: 5%;
+}
+
+.h-\[55\%\] {
+ height: 55%;
+}
+
+.h-\[60vh\] {
+ height: 60vh;
+}
+
+.h-\[80px\] {
+ height: 80px;
}
.h-full {
@@ -607,6 +685,18 @@ video {
height: 100vh;
}
+.h-\[300px\] {
+ height: 300px;
+}
+
+.max-h-full {
+ max-height: 100%;
+}
+
+.min-h-0 {
+ min-height: 0px;
+}
+
.w-1\/4 {
width: 25%;
}
@@ -635,6 +725,14 @@ video {
width: 100vw;
}
+.max-w-full {
+ max-width: 100%;
+}
+
+.flex-1 {
+ flex: 1 1 0%;
+}
+
.flex-shrink-0 {
flex-shrink: 0;
}
@@ -643,10 +741,18 @@ video {
flex-grow: 1;
}
+.transform {
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
+}
+
.cursor-pointer {
cursor: pointer;
}
+.cursor-col-resize {
+ cursor: col-resize;
+}
+
.select-none {
-webkit-user-select: none;
-moz-user-select: none;
@@ -685,10 +791,18 @@ video {
overflow: scroll;
}
+.overflow-x-auto {
+ overflow-x: auto;
+}
+
.overflow-y-auto {
overflow-y: auto;
}
+.overflow-y-hidden {
+ overflow-y: hidden;
+}
+
.overflow-y-visible {
overflow-y: visible;
}
@@ -705,15 +819,37 @@ video {
border-bottom-width: 1px;
}
+.border-r {
+ border-right-width: 1px;
+}
+
+.border-t {
+ border-top-width: 1px;
+}
+
+.border-black {
+ --tw-border-opacity: 1;
+ border-color: rgb(0 0 0 / var(--tw-border-opacity, 1));
+}
+
.bg-white {
--tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
}
+.bg-\[\#1f2428\] {
+ --tw-bg-opacity: 1;
+ background-color: rgb(31 36 40 / var(--tw-bg-opacity, 1));
+}
+
.p-1 {
padding: 0.25rem;
}
+.p-10 {
+ padding: 2.5rem;
+}
+
.p-2 {
padding: 0.5rem;
}
@@ -722,6 +858,10 @@ video {
padding: 0.75rem;
}
+.p-4 {
+ padding: 1rem;
+}
+
.pl-4 {
padding-left: 1rem;
}
@@ -740,6 +880,11 @@ video {
color: rgb(53 73 94 / var(--tw-text-opacity, 1));
}
+.text-white {
+ --tw-text-opacity: 1;
+ color: rgb(255 255 255 / var(--tw-text-opacity, 1));
+}
+
.transition-all {
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
diff --git a/packages/core/src/animation.ts b/packages/core/src/animation.ts
index 019a8f1..1479ef8 100644
--- a/packages/core/src/animation.ts
+++ b/packages/core/src/animation.ts
@@ -21,6 +21,12 @@ export interface AnimationInstance
{
export class AnimationManager {
animations: AnimationInstance[] = [];
+ repeatStatus: [
+ boolean,
+ number | "loop",
+ number,
+ AnimationInstance[],
+ ] = [false, 0, 0, []];
private target: T;
constructor(target: T, elapsed: WatchSource) {
@@ -62,8 +68,12 @@ export class AnimationManager {
context ??= {} as any;
const duration = context.duration ?? 1;
const by = context.by ?? linear;
- this.animations.push({ context, animation, duration, by });
+ if (this.repeatStatus[0]) {
+ // 在重复模式中,将动画保存到重复区间
+ this.repeatStatus[3].push({ context, animation, duration, by });
+ }
+ this.animations.push({ context, animation, duration, by });
return this;
}
@@ -74,14 +84,12 @@ export class AnimationManager {
}
delay(duration: number) {
- this.animate(
+ return this.animate(
() => {
/* empty animation */
},
{ duration },
);
-
- return this;
}
parallel(
@@ -135,13 +143,42 @@ export class AnimationManager {
}
exec(fn: () => void) {
- this.animate(
+ return this.animate(
defineAnimation(() => (progress) => {
if (progress === 0) fn();
}),
{ duration: 0 },
);
}
+
+ repeat(times?: number) {
+ this.repeatStatus = [true, times ?? "loop", 0, []];
+ return this;
+ }
+
+ repeatEnd(fn?: () => void) {
+ // 当前重复次数加1
+ this.repeatStatus[2] += 1;
+
+ if (
+ this.repeatStatus[1] === "loop" ||
+ this.repeatStatus[2] < this.repeatStatus[1]
+ ) {
+ // 如果是无限循环或者还没达到总重复次数,将重复区间的动画再次添加到队列
+ this.animations.push(...this.repeatStatus[3]);
+
+ if (this.repeatStatus[1] === "loop") {
+ // 如果是无限循环,重置当前重复次数
+ this.repeatStatus[2] = 0;
+ }
+ } else {
+ // 重复完成
+ this.repeatStatus[0] = false;
+ this.repeatStatus[3] = [];
+ fn?.();
+ }
+ return this;
+ }
}
export type AnimationSetup = (
diff --git a/packages/lib/src/widgets/widget.ts b/packages/lib/src/widgets/widget.ts
index 2990fee..1999790 100644
--- a/packages/lib/src/widgets/widget.ts
+++ b/packages/lib/src/widgets/widget.ts
@@ -83,6 +83,8 @@ export type WidgetIns = WidgetOptions &
once: (animation: (target: Widget) => void) => void;
delay: (duration: number) => void;
exec: (fn: () => void) => void;
+ repeat: (times?: number) => void;
+ repeatEnd: (fn?: () => void) => void;
};
export interface Animatable {
animate: (
@@ -462,6 +464,24 @@ export function widget(options: WidgetOptions) {
},
options,
);
+ registerAnimation(
+ "repeat",
+ (times: number) => {
+ return (manager: AnimationManager) => {
+ return manager.repeat(times);
+ };
+ },
+ options,
+ );
+ registerAnimation(
+ "repeatEnd",
+ (fn: () => void) => {
+ return (manager: AnimationManager) => {
+ return manager.repeatEnd(fn);
+ };
+ },
+ options,
+ );
const animations = inject<(() => Animation)[]>(
"ADDITION_ANIMATIONS",
);
diff --git a/test/src/App.vue b/test/src/App.vue
index f369ea2..d588a15 100644
--- a/test/src/App.vue
+++ b/test/src/App.vue
@@ -1,6 +1,6 @@
-
+