animot-presenter 0.5.19 → 0.5.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AnimotPresenter.svelte +177 -13
- package/dist/cdn/animot-presenter.css +1 -1
- package/dist/cdn/animot-presenter.esm.js +4737 -4656
- package/dist/cdn/animot-presenter.min.js +9 -9
- package/dist/engine/container-layout.d.ts +12 -0
- package/dist/engine/container-layout.js +108 -0
- package/dist/renderers/Container.svelte +30 -0
- package/dist/renderers/Container.svelte.d.ts +7 -0
- package/dist/types.d.ts +60 -2
- package/package.json +1 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-layout pass for ContainerElement. Walks a slide's elements list,
|
|
3
|
+
* finds the container by id, and rewrites the position (and optionally
|
|
4
|
+
* size, when align=stretch) of every child id listed in childIds.
|
|
5
|
+
*
|
|
6
|
+
* This is the same algorithm the editor uses; running it in the presenter
|
|
7
|
+
* keeps embedded JSONs visually identical to what the user authored, and
|
|
8
|
+
* gives us a single source of truth for layout math.
|
|
9
|
+
*/
|
|
10
|
+
import type { CanvasElement } from '../types';
|
|
11
|
+
export declare function relayoutContainer(elements: CanvasElement[], containerId: string): CanvasElement[];
|
|
12
|
+
export declare function relayoutAllContainers(elements: CanvasElement[]): CanvasElement[];
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-layout pass for ContainerElement. Walks a slide's elements list,
|
|
3
|
+
* finds the container by id, and rewrites the position (and optionally
|
|
4
|
+
* size, when align=stretch) of every child id listed in childIds.
|
|
5
|
+
*
|
|
6
|
+
* This is the same algorithm the editor uses; running it in the presenter
|
|
7
|
+
* keeps embedded JSONs visually identical to what the user authored, and
|
|
8
|
+
* gives us a single source of truth for layout math.
|
|
9
|
+
*/
|
|
10
|
+
function resolvePadding(p) {
|
|
11
|
+
if (typeof p === 'number')
|
|
12
|
+
return { top: p, right: p, bottom: p, left: p };
|
|
13
|
+
return p;
|
|
14
|
+
}
|
|
15
|
+
export function relayoutContainer(elements, containerId) {
|
|
16
|
+
const container = elements.find((el) => el.id === containerId);
|
|
17
|
+
if (!container || container.type !== 'container')
|
|
18
|
+
return elements;
|
|
19
|
+
const c = container;
|
|
20
|
+
const pad = resolvePadding(c.padding);
|
|
21
|
+
const innerW = Math.max(0, c.size.width - pad.left - pad.right);
|
|
22
|
+
const innerH = Math.max(0, c.size.height - pad.top - pad.bottom);
|
|
23
|
+
const isRow = c.direction === 'row';
|
|
24
|
+
const innerMain = isRow ? innerW : innerH;
|
|
25
|
+
const innerCross = isRow ? innerH : innerW;
|
|
26
|
+
const mainOriginAbs = (isRow ? c.position.x + pad.left : c.position.y + pad.top);
|
|
27
|
+
const crossOriginAbs = (isRow ? c.position.y + pad.top : c.position.x + pad.left);
|
|
28
|
+
const childMap = new Map(elements.map((el) => [el.id, el]));
|
|
29
|
+
const children = c.childIds
|
|
30
|
+
.map((id) => childMap.get(id))
|
|
31
|
+
.filter((el) => el !== undefined);
|
|
32
|
+
if (children.length === 0)
|
|
33
|
+
return elements;
|
|
34
|
+
const mainSizes = children.map((ch) => (isRow ? ch.size.width : ch.size.height));
|
|
35
|
+
const totalChildMain = mainSizes.reduce((a, b) => a + b, 0);
|
|
36
|
+
const totalGap = c.gap * Math.max(0, children.length - 1);
|
|
37
|
+
const usedMain = totalChildMain + totalGap;
|
|
38
|
+
const freeMain = Math.max(0, innerMain - usedMain);
|
|
39
|
+
let mainCursor = 0;
|
|
40
|
+
let extraGap = 0;
|
|
41
|
+
switch (c.justify) {
|
|
42
|
+
case 'start':
|
|
43
|
+
mainCursor = 0;
|
|
44
|
+
break;
|
|
45
|
+
case 'center':
|
|
46
|
+
mainCursor = freeMain / 2;
|
|
47
|
+
break;
|
|
48
|
+
case 'end':
|
|
49
|
+
mainCursor = freeMain;
|
|
50
|
+
break;
|
|
51
|
+
case 'space-between':
|
|
52
|
+
mainCursor = 0;
|
|
53
|
+
extraGap = children.length > 1 ? freeMain / (children.length - 1) : 0;
|
|
54
|
+
break;
|
|
55
|
+
case 'space-around':
|
|
56
|
+
extraGap = children.length > 0 ? freeMain / children.length : 0;
|
|
57
|
+
mainCursor = extraGap / 2;
|
|
58
|
+
break;
|
|
59
|
+
case 'space-evenly':
|
|
60
|
+
extraGap = freeMain / (children.length + 1);
|
|
61
|
+
mainCursor = extraGap;
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
const updates = new Map();
|
|
65
|
+
for (let i = 0; i < children.length; i++) {
|
|
66
|
+
const ch = children[i];
|
|
67
|
+
const mainSize = isRow ? ch.size.width : ch.size.height;
|
|
68
|
+
const intrinsicCross = isRow ? ch.size.height : ch.size.width;
|
|
69
|
+
let crossOffset = 0;
|
|
70
|
+
let crossSize = intrinsicCross;
|
|
71
|
+
switch (c.align) {
|
|
72
|
+
case 'start':
|
|
73
|
+
crossOffset = 0;
|
|
74
|
+
break;
|
|
75
|
+
case 'center':
|
|
76
|
+
crossOffset = (innerCross - intrinsicCross) / 2;
|
|
77
|
+
break;
|
|
78
|
+
case 'end':
|
|
79
|
+
crossOffset = innerCross - intrinsicCross;
|
|
80
|
+
break;
|
|
81
|
+
case 'stretch':
|
|
82
|
+
crossOffset = 0;
|
|
83
|
+
crossSize = innerCross;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
const x = isRow ? mainOriginAbs + mainCursor : crossOriginAbs + crossOffset;
|
|
87
|
+
const y = isRow ? crossOriginAbs + crossOffset : mainOriginAbs + mainCursor;
|
|
88
|
+
const w = isRow ? mainSize : crossSize;
|
|
89
|
+
const h = isRow ? crossSize : mainSize;
|
|
90
|
+
updates.set(ch.id, { position: { x, y }, size: { width: w, height: h } });
|
|
91
|
+
mainCursor += mainSize + c.gap + extraGap;
|
|
92
|
+
}
|
|
93
|
+
return elements.map((el) => {
|
|
94
|
+
const up = updates.get(el.id);
|
|
95
|
+
if (!up)
|
|
96
|
+
return el;
|
|
97
|
+
return { ...el, position: up.position, size: up.size ?? el.size };
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
export function relayoutAllContainers(elements) {
|
|
101
|
+
let result = elements;
|
|
102
|
+
for (const el of elements) {
|
|
103
|
+
if (el.type === 'container') {
|
|
104
|
+
result = relayoutContainer(result, el.id);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ContainerElement } from '../types';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
element: ContainerElement;
|
|
6
|
+
}
|
|
7
|
+
let { element }: Props = $props();
|
|
8
|
+
|
|
9
|
+
const fill = $derived(element.fillColor || 'transparent');
|
|
10
|
+
const borderCss = $derived(
|
|
11
|
+
element.borderWidth && element.borderColor
|
|
12
|
+
? `${element.borderWidth}px solid ${element.borderColor}`
|
|
13
|
+
: 'none'
|
|
14
|
+
);
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<div class="animot-container-element"
|
|
18
|
+
style:width="100%"
|
|
19
|
+
style:height="100%"
|
|
20
|
+
style:background={fill}
|
|
21
|
+
style:border={borderCss}
|
|
22
|
+
style:border-radius="{element.borderRadius ?? 0}px"
|
|
23
|
+
></div>
|
|
24
|
+
|
|
25
|
+
<style>
|
|
26
|
+
.animot-container-element {
|
|
27
|
+
box-sizing: border-box;
|
|
28
|
+
pointer-events: none;
|
|
29
|
+
}
|
|
30
|
+
</style>
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,34 @@
|
|
|
1
|
-
export type ElementType = 'code' | 'text' | 'arrow' | 'image' | 'shape' | 'counter' | 'chart' | 'icon' | 'svg' | 'motionPath' | 'video' | 'progress';
|
|
1
|
+
export type ElementType = 'code' | 'text' | 'arrow' | 'image' | 'shape' | 'counter' | 'chart' | 'icon' | 'svg' | 'motionPath' | 'video' | 'progress' | 'container';
|
|
2
|
+
/**
|
|
3
|
+
* Per-element keyframe — animates state at a specific time WITHIN a slide's
|
|
4
|
+
* display window. Layered on top of slide-to-slide morphing: morphing handles
|
|
5
|
+
* between-slide transitions, keyframes handle within-slide animation.
|
|
6
|
+
*/
|
|
7
|
+
export interface Keyframe {
|
|
8
|
+
id: string;
|
|
9
|
+
time: number;
|
|
10
|
+
easing?: 'linear' | 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'spring';
|
|
11
|
+
position?: Position;
|
|
12
|
+
size?: Size;
|
|
13
|
+
rotation?: number;
|
|
14
|
+
opacity?: number;
|
|
15
|
+
skewX?: number;
|
|
16
|
+
skewY?: number;
|
|
17
|
+
tiltX?: number;
|
|
18
|
+
tiltY?: number;
|
|
19
|
+
borderRadius?: number;
|
|
20
|
+
fontSize?: number;
|
|
21
|
+
fillColor?: string;
|
|
22
|
+
strokeColor?: string;
|
|
23
|
+
strokeWidth?: number;
|
|
24
|
+
backgroundColor?: string;
|
|
25
|
+
color?: string;
|
|
26
|
+
blur?: number;
|
|
27
|
+
brightness?: number;
|
|
28
|
+
contrast?: number;
|
|
29
|
+
saturate?: number;
|
|
30
|
+
grayscale?: number;
|
|
31
|
+
}
|
|
2
32
|
export type ShapeType = 'rectangle' | 'circle' | 'triangle' | 'ellipse' | 'star' | 'hexagon';
|
|
3
33
|
export interface Position {
|
|
4
34
|
x: number;
|
|
@@ -57,6 +87,11 @@ export interface BaseElement {
|
|
|
57
87
|
name?: string;
|
|
58
88
|
locked?: boolean;
|
|
59
89
|
groupId?: string;
|
|
90
|
+
/** Auto-layout container parent. When set, position/size is computed
|
|
91
|
+
* by the container's flex layout pass. */
|
|
92
|
+
containerId?: string;
|
|
93
|
+
/** Per-element keyframes that play DURING this slide. Sorted by time. */
|
|
94
|
+
keyframes?: Keyframe[];
|
|
60
95
|
position: Position;
|
|
61
96
|
size: Size;
|
|
62
97
|
rotation: number;
|
|
@@ -414,7 +449,30 @@ export interface ProgressElement extends BaseElement {
|
|
|
414
449
|
};
|
|
415
450
|
animationDuration?: number;
|
|
416
451
|
}
|
|
417
|
-
|
|
452
|
+
/**
|
|
453
|
+
* Auto-layout container. Holds a list of child element ids and arranges them
|
|
454
|
+
* via flex-style rules. Children remain independently selectable; layout
|
|
455
|
+
* pass writes computed position+size onto each child.
|
|
456
|
+
*/
|
|
457
|
+
export interface ContainerElement extends BaseElement {
|
|
458
|
+
type: 'container';
|
|
459
|
+
childIds: string[];
|
|
460
|
+
direction: 'row' | 'column';
|
|
461
|
+
gap: number;
|
|
462
|
+
padding: number | {
|
|
463
|
+
top: number;
|
|
464
|
+
right: number;
|
|
465
|
+
bottom: number;
|
|
466
|
+
left: number;
|
|
467
|
+
};
|
|
468
|
+
align: 'start' | 'center' | 'end' | 'stretch';
|
|
469
|
+
justify: 'start' | 'center' | 'end' | 'space-between' | 'space-around' | 'space-evenly';
|
|
470
|
+
fillColor?: string;
|
|
471
|
+
borderColor?: string;
|
|
472
|
+
borderWidth?: number;
|
|
473
|
+
borderRadius?: number;
|
|
474
|
+
}
|
|
475
|
+
export type CanvasElement = CodeElement | TextElement | ArrowElement | ImageElement | VideoElement | ShapeElement | CounterElement | ChartElement | IconElement | SvgElement | MotionPathElement | ProgressElement | ContainerElement;
|
|
418
476
|
export type ParticleShape = 'circle' | 'square' | 'star' | 'triangle';
|
|
419
477
|
export interface ParticlesConfig {
|
|
420
478
|
enabled: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "animot-presenter",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.21",
|
|
4
4
|
"description": "Embed animated presentations anywhere. Works with vanilla JS, React, Vue, Angular, Svelte, and any frontend framework. Morphing animations, code highlighting, charts, particles, and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"svelte": "./dist/index.js",
|