bak
This commit is contained in:
1 parent
c6863f2bfc
commit
94e9846c13
11 files changed
+985
-136
No files matched your search
+94
-34
@@ -1,8 +1,9 @@
|
||||
import type { Scene } from '@/types/scene'
|
||||
|
||||
const RUNTIME_SOURCE = `(function(){
|
||||
var SCENE = __SCENE__;
|
||||
var OPTIONS = __OPTIONS__;
|
||||
const RUNTIME_VERSION = '1'
|
||||
|
||||
const ENGINE_SOURCE = `(function(){
|
||||
if (window.AnimatrPlayer && window.AnimatrPlayer.__v === '${RUNTIME_VERSION}') return;
|
||||
|
||||
function easing(name, t){
|
||||
if(name==='easeIn')return t*t;
|
||||
@@ -156,15 +157,19 @@ const RUNTIME_SOURCE = `(function(){
|
||||
ctx.fillStyle=p.fill||asset.fill;
|
||||
ctx.fillRect(-rOffX,-rOffY,asset.width,asset.height);
|
||||
}else if(asset.kind==='circle'){
|
||||
var cOffX=layer.offsetX!=null?layer.offsetX:0;
|
||||
var cOffY=layer.offsetY!=null?layer.offsetY:0;
|
||||
ctx.fillStyle=p.fill||asset.fill;
|
||||
ctx.beginPath();
|
||||
ctx.arc(0,0,asset.radius,0,Math.PI*2);
|
||||
ctx.arc(-cOffX,-cOffY,asset.radius,0,Math.PI*2);
|
||||
ctx.fill();
|
||||
}else if(asset.kind==='text'){
|
||||
var tOffX=layer.offsetX!=null?layer.offsetX:0;
|
||||
var tOffY=layer.offsetY!=null?layer.offsetY:0;
|
||||
ctx.fillStyle=p.fill||asset.fill;
|
||||
ctx.font=asset.fontSize+'px sans-serif';
|
||||
ctx.textBaseline='top';
|
||||
ctx.fillText(asset.text,0,0);
|
||||
ctx.fillText(asset.text,-tOffX,-tOffY);
|
||||
}else if(asset.kind==='image'){
|
||||
var img=imgs[asset.id];
|
||||
if(img){
|
||||
@@ -183,22 +188,33 @@ const RUNTIME_SOURCE = `(function(){
|
||||
});
|
||||
}
|
||||
|
||||
var script=document.currentScript;
|
||||
var target=null;
|
||||
if(script&&script.dataset&&script.dataset.target){
|
||||
target=document.querySelector(script.dataset.target);
|
||||
}else if(script&&script.parentElement){
|
||||
target=script.parentElement;
|
||||
}
|
||||
var autoOpts={};
|
||||
if(script&&script.dataset){
|
||||
if(script.dataset.loop==='false')autoOpts.loop=false;
|
||||
}
|
||||
if(target)mount(target,SCENE,Object.assign({},OPTIONS,autoOpts));
|
||||
var api={mount:function(c,opts){return mount(c,SCENE,opts||{});}};
|
||||
if(typeof window!=='undefined'){
|
||||
window.AnimatrPlayer=window.AnimatrPlayer||api;
|
||||
var api={ __v: '${RUNTIME_VERSION}', mount: mount };
|
||||
window.AnimatrPlayer = api;
|
||||
|
||||
var pending = window.__animatrPending;
|
||||
window.__animatrPending = null;
|
||||
if (pending) for (var i=0;i<pending.length;i++) { try { pending[i](); } catch(e) { console.error(e); } }
|
||||
})();
|
||||
`
|
||||
|
||||
const SCENE_BOOT_SOURCE = `(function(){
|
||||
var SCENE = __SCENE__;
|
||||
var OPTIONS = __OPTIONS__;
|
||||
var script = document.currentScript;
|
||||
var target = null;
|
||||
if (script && script.dataset && script.dataset.target) target = document.querySelector(script.dataset.target);
|
||||
else if (script && script.parentElement) target = script.parentElement;
|
||||
var autoOpts = {};
|
||||
if (script && script.dataset && script.dataset.loop === 'false') autoOpts.loop = false;
|
||||
function boot(){
|
||||
if (!target || !window.AnimatrPlayer) return;
|
||||
var merged = {};
|
||||
for (var k in OPTIONS) merged[k] = OPTIONS[k];
|
||||
for (var k2 in autoOpts) merged[k2] = autoOpts[k2];
|
||||
window.AnimatrPlayer.mount(target, SCENE, merged);
|
||||
}
|
||||
if (window.AnimatrPlayer) boot();
|
||||
else (window.__animatrPending = window.__animatrPending || []).push(boot);
|
||||
})();
|
||||
`
|
||||
|
||||
@@ -210,24 +226,54 @@ export interface ExportOptions {
|
||||
loop?: boolean
|
||||
}
|
||||
|
||||
export function generateExportScript(scene: Scene, options: ExportOptions = {}): string {
|
||||
const sceneJson = jsonForExport(scene)
|
||||
const optsJson = JSON.stringify({ loop: options.loop !== false })
|
||||
export function generateRuntimeScript(): string {
|
||||
return (
|
||||
'/* Animatr export — generated ' +
|
||||
new Date().toISOString() +
|
||||
' */\n' +
|
||||
RUNTIME_SOURCE.replace('__SCENE__', sceneJson).replace('__OPTIONS__', optsJson)
|
||||
'/* Animatr runtime v' + RUNTIME_VERSION + ' — generated ' + new Date().toISOString() + ' */\n' +
|
||||
ENGINE_SOURCE
|
||||
)
|
||||
}
|
||||
|
||||
export function downloadExport(scene: Scene, filename = 'animation.js') {
|
||||
const source = generateExportScript(scene)
|
||||
triggerDownload(source, filename, 'application/javascript')
|
||||
export function generateSceneScript(scene: Scene, options: ExportOptions = {}): string {
|
||||
const sceneJson = jsonForExport(scene)
|
||||
const optsJson = JSON.stringify({ loop: options.loop !== false })
|
||||
return (
|
||||
'/* Animatr scene — generated ' + new Date().toISOString() + '\n' +
|
||||
' * Requires animatr-runtime.js (v' + RUNTIME_VERSION + ') to be loaded once per page.\n' +
|
||||
' */\n' +
|
||||
SCENE_BOOT_SOURCE.replace('__SCENE__', sceneJson).replace('__OPTIONS__', optsJson)
|
||||
)
|
||||
}
|
||||
|
||||
export function generateBundledScript(scene: Scene, options: ExportOptions = {}): string {
|
||||
return (
|
||||
'/* Animatr bundled export — generated ' + new Date().toISOString() + ' */\n' +
|
||||
ENGINE_SOURCE +
|
||||
'\n' +
|
||||
generateSceneScriptBody(scene, options)
|
||||
)
|
||||
}
|
||||
|
||||
function generateSceneScriptBody(scene: Scene, options: ExportOptions): string {
|
||||
const sceneJson = jsonForExport(scene)
|
||||
const optsJson = JSON.stringify({ loop: options.loop !== false })
|
||||
return SCENE_BOOT_SOURCE.replace('__SCENE__', sceneJson).replace('__OPTIONS__', optsJson)
|
||||
}
|
||||
|
||||
export function downloadRuntime(filename = 'animatr-runtime.js') {
|
||||
triggerDownload(generateRuntimeScript(), filename, 'application/javascript')
|
||||
}
|
||||
|
||||
export function downloadSceneExport(scene: Scene, filename = 'animation.js') {
|
||||
triggerDownload(generateSceneScript(scene), filename, 'application/javascript')
|
||||
}
|
||||
|
||||
export function downloadBundledExport(scene: Scene, filename = 'animation-bundled.js') {
|
||||
triggerDownload(generateBundledScript(scene), filename, 'application/javascript')
|
||||
}
|
||||
|
||||
export function generateExampleHtml(scene: Scene, options: ExportOptions = {}): string {
|
||||
const script = generateExportScript(scene, options)
|
||||
const runtime = ENGINE_SOURCE
|
||||
const sceneBoot = generateSceneScriptBody(scene, options)
|
||||
const aspect = `${scene.canvas.width} / ${scene.canvas.height}`
|
||||
return `<!doctype html>
|
||||
<html lang="de">
|
||||
@@ -289,18 +335,32 @@ export function generateExampleHtml(scene: Scene, options: ExportOptions = {}):
|
||||
</head>
|
||||
<body>
|
||||
<h1>Animatr Export</h1>
|
||||
<p>Diese HTML-Datei enthält Runtime + Szene inline — einfach im Browser öffnen.</p>
|
||||
<p>Runtime einmal laden, dann beliebig viele Szenen auf einer Seite.</p>
|
||||
|
||||
<!-- Runtime: einmal pro Seite -->
|
||||
<script>
|
||||
${runtime}
|
||||
</script>
|
||||
|
||||
<div class="stage">
|
||||
<!-- Szene: pro Animation -->
|
||||
<script>
|
||||
${script}
|
||||
${sceneBoot}
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div class="usage">
|
||||
<h2>Einbinden in eine eigene Seite</h2>
|
||||
<pre><div style="width: 800px; height: 450px;">
|
||||
<pre><!-- einmal pro Seite -->
|
||||
<script src="animatr-runtime.js"></script>
|
||||
|
||||
<!-- je Animation ein Container + Scene-Script -->
|
||||
<div style="width: 800px; height: 450px;">
|
||||
<script src="animation.js"></script>
|
||||
</div>
|
||||
|
||||
<div style="width: 400px; height: 225px;">
|
||||
<script src="animation-2.js"></script>
|
||||
</div></pre>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
+33
-11
@@ -56,20 +56,42 @@ export function sortedKeyframes(layer: Layer): Keyframe[] {
|
||||
return [...layer.keyframes].sort((a, b) => a.time - b.time)
|
||||
}
|
||||
|
||||
export function sampleLayer(layer: Layer, time: number): AnimatableProps {
|
||||
const kfs = sortedKeyframes(layer)
|
||||
if (!kfs.length) return { ...layer.base }
|
||||
if (time <= kfs[0].time) return merge(layer.base, kfs[0].props)
|
||||
const last = kfs[kfs.length - 1]
|
||||
if (time >= last.time) return merge(layer.base, last.props)
|
||||
for (let i = 0; i < kfs.length - 1; i++) {
|
||||
const a = kfs[i]
|
||||
const b = kfs[i + 1]
|
||||
function sampleKeyframeSet(
|
||||
kfs: Keyframe[] | undefined,
|
||||
base: AnimatableProps,
|
||||
time: number,
|
||||
): AnimatableProps {
|
||||
if (!kfs || !kfs.length) return { ...base }
|
||||
const sorted = [...kfs].sort((a, b) => a.time - b.time)
|
||||
if (time <= sorted[0].time) return merge(base, sorted[0].props)
|
||||
const last = sorted[sorted.length - 1]
|
||||
if (time >= last.time) return merge(base, last.props)
|
||||
for (let i = 0; i < sorted.length - 1; i++) {
|
||||
const a = sorted[i]
|
||||
const b = sorted[i + 1]
|
||||
if (time >= a.time && time <= b.time) {
|
||||
const raw = (time - a.time) / (b.time - a.time)
|
||||
const t = easings[b.easing](raw)
|
||||
return lerpProps(merge(layer.base, a.props), merge(layer.base, b.props), t)
|
||||
return lerpProps(merge(base, a.props), merge(base, b.props), t)
|
||||
}
|
||||
}
|
||||
return { ...layer.base }
|
||||
return { ...base }
|
||||
}
|
||||
|
||||
export function sampleLayer(layer: Layer, time: number): AnimatableProps {
|
||||
const main = sampleKeyframeSet(layer.keyframes, layer.base, time)
|
||||
if (!layer.separated) return main
|
||||
|
||||
const posSource = layer.positionKeyframes
|
||||
const rotSource = layer.rotationKeyframes
|
||||
|
||||
const pos = posSource ? sampleKeyframeSet(posSource, layer.base, time) : main
|
||||
const rot = rotSource ? sampleKeyframeSet(rotSource, layer.base, time) : main
|
||||
|
||||
return {
|
||||
...main,
|
||||
x: pos.x,
|
||||
y: pos.y,
|
||||
rotation: rot.rotation,
|
||||
}
|
||||
}
|
||||
+6
-2
@@ -61,15 +61,19 @@ export function drawSceneFrame(
|
||||
ctx.fillStyle = p.fill ?? asset.fill
|
||||
ctx.fillRect(-offX, -offY, asset.width, asset.height)
|
||||
} else if (asset.kind === 'circle') {
|
||||
const offX = layer.offsetX ?? 0
|
||||
const offY = layer.offsetY ?? 0
|
||||
ctx.fillStyle = p.fill ?? asset.fill
|
||||
ctx.beginPath()
|
||||
ctx.arc(0, 0, asset.radius, 0, Math.PI * 2)
|
||||
ctx.arc(-offX, -offY, asset.radius, 0, Math.PI * 2)
|
||||
ctx.fill()
|
||||
} else if (asset.kind === 'text') {
|
||||
const offX = layer.offsetX ?? 0
|
||||
const offY = layer.offsetY ?? 0
|
||||
ctx.fillStyle = p.fill ?? asset.fill
|
||||
ctx.font = `${asset.fontSize}px sans-serif`
|
||||
ctx.textBaseline = 'top'
|
||||
ctx.fillText(asset.text, 0, 0)
|
||||
ctx.fillText(asset.text, -offX, -offY)
|
||||
} else if (asset.kind === 'image') {
|
||||
const img = images[asset.id]
|
||||
if (img) {
|
||||
|
||||
Reference in new issue
Block a user