0.8.1: dirty-state + Cmd/Ctrl+S, fullscreen plays to OUT, UX fixes
- Unsaved-changes indicator: a small accent dot appears on the save
button whenever the scene has changed since the last save/load.
Save/load/new all mark the project clean on next tick.
- Cmd/Ctrl+S saves the project (reuses the same flow as the button;
prompts for a name on first save). Tooltip now mentions the
shortcut.
- Fullscreen preview plays to the OUT marker (or scene.duration when
no OUT is set) instead of looping at the last keyframe. Dropped
the runtime's effectiveEnd helper; RUNTIME_VERSION 13 → 14.
- "Still frame" keyframes no longer disable easing. A held kf's
easing still drives the segment INTO it from the previous kf, so
you can ease into a pause. Only the first kf disables easing
(nothing precedes it). Dropped `timeline.holdDisabledHint`.
- Settings dialog: hint text 11px → 13px (and dropped italic) so
the inline help is readable; section headings 10px → 13px.
- Canvas stage shadow: `shadowCfg` always emits explicit values plus
a `shadowEnabled` flag instead of returning `{}`. Konva's default
shadowOpacity is 1, so bailing with `{}` could leave stale shadow
attrs on the shape. Preview and editor now gate on the same
condition deterministically.
- Sidebar empty-state copy: "No clips" → "No clips yet" to match
"No buttons yet".
Regression tests: runtime rangeOut fallback is scene.duration (not
last kf); sidebar empty-states both contain "yet"; the
holdDisabledHint key is gone; saveProject tooltips mention the
shortcut.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
1e89ea86e0
commit
82ee5c98d0
10 files changed
+133
-48
No files matched your search
+7
-26
@@ -1,6 +1,6 @@
|
||||
import type { Scene } from '@/types/scene'
|
||||
|
||||
const RUNTIME_VERSION = '13'
|
||||
const RUNTIME_VERSION = '14'
|
||||
|
||||
const ENGINE_SOURCE = `(function(){
|
||||
if (window.AnimatrPlayer && window.AnimatrPlayer.__v === '${RUNTIME_VERSION}') return;
|
||||
@@ -128,36 +128,17 @@ const ENGINE_SOURCE = `(function(){
|
||||
})).then(function(){return imgs;});
|
||||
}
|
||||
|
||||
function effectiveEnd(scene){
|
||||
var max=0;
|
||||
for(var i=0;i<scene.layers.length;i++){
|
||||
var kfs=scene.layers[i].keyframes;
|
||||
for(var j=0;j<kfs.length;j++){ if(kfs[j].time>max) max=kfs[j].time; }
|
||||
}
|
||||
var instances=scene.clipInstances||[];
|
||||
var clips=scene.clips||[];
|
||||
var clipById={};
|
||||
for(var ci=0;ci<clips.length;ci++) clipById[clips[ci].id]=clips[ci];
|
||||
for(var ii=0;ii<instances.length;ii++){
|
||||
var inst=instances[ii];
|
||||
var cl=clipById[inst.clipId];
|
||||
if(!cl) continue;
|
||||
var trimS=inst.trimStart||0;
|
||||
var trimE=inst.trimEnd||0;
|
||||
var end=inst.startTime+Math.max(0,cl.duration-trimS-trimE);
|
||||
if(end>max) max=end;
|
||||
}
|
||||
if(max<=0) return scene.duration;
|
||||
return Math.min(max,scene.duration);
|
||||
}
|
||||
function mount(container,scene,opts){
|
||||
if(!container)return;
|
||||
opts=opts||{};
|
||||
var loop=opts.loop!==false;
|
||||
// Honour explicit in/out points; fall back to 0 and the animation's own end.
|
||||
// Honour explicit in/out points. When outTime isn't set, play the
|
||||
// full timeline (scene.duration) — matches the editor's playbackOut
|
||||
// getter. Using the last keyframe as a fallback made still-frame
|
||||
// endings and trailing silence (intentionally empty space before
|
||||
// the OUT mark) unreachable, so the preview looped early.
|
||||
var rangeIn=Math.max(0, Math.min(scene.duration, scene.inTime==null?0:scene.inTime));
|
||||
var autoEnd=effectiveEnd(scene);
|
||||
var rangeOut=Math.max(rangeIn, Math.min(scene.duration, scene.outTime==null?autoEnd:scene.outTime));
|
||||
var rangeOut=Math.max(rangeIn, Math.min(scene.duration, scene.outTime==null?scene.duration:scene.outTime));
|
||||
var sceneEnd=rangeOut;
|
||||
var cs=getComputedStyle(container);
|
||||
if(cs.position==='static')container.style.position='relative';
|
||||
|
||||
Reference in new issue
Block a user