Show a wheel step or Fit as a short zoom transition (#309)
The wheel and the Fit button used to jump to the new view. Now the view takes a short way there, 200 ms by default, everywhere the editor zooms. The zoom steps stay the same, only the change is shown. - viewTween: tweenView is the way between two views as a pure function. The scale moves by equal ratios, and the world point that sits at the same place on screen in both views stays there. After a wheel step that is the point under the pointer. - ViewTween drives it on the ticker. A surface hands it reading, writing and drawing its own view and wraps its wheel and Fit code in change(). A step during a transition starts from where that one was heading, so quick steps add up to exactly as many steps. - Anything else that moves the view ends the transition. A pan jumps to the target first, and saving a view saves the target. - Wired into FreeViewport (Scene, Container, animation preview), SheetCanvas (Texture, Curve, Pixel art, Vector art), GraphCanvas, panAndZoom (Tile board, plugin node board) and the timeline. - Menu > General settings... sets the time in ms, 0 jumps at once. It is kept in this browser like the panel sizes, not in the project, and it is not an undo step. - A document that opens still comes up framed at once. The animation preview frames a new subject itself instead of calling fit(). - The pixel art readout names the step the view is heading for. Measured in the scene view at 200 ms on melektaus-atx, where every frame of the transition is a full redraw: - 500 sprites: redraw 6.7 ms, 16 frames, mean 18 ms - 1000 sprites: redraw 13.0 ms, 9 frames, mean 27 ms - 2000 sprites: redraw 32.2 ms, 5 frames, mean 58 ms Tests: - The harness sets 0 ms on every load, so the existing modules read the step and not the way. - e2e 46i samples one wheel step at 1000 ms a frame later: zoom 1.033 between 1 and 2, the point under the pointer kept at (134, -40). It lands on the view a 0 ms jump reaches. - e2e 46 and 49 expect the new menu entry. - Gate (atx): 234 files, 3563 tests, CHECK EXIT 0 (+15) - e2e 46,49 (atx): ALL PASSED, 43 checks (+3, sum 1735) - Three counter-checks fail view-tween.test.ts as expected. e2e 46i fails when the scene view does not carry its centre along. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8452a99c64
commit
cf6043a066
21 files changed
+779
-49
No files matched your search
@@ -4,6 +4,7 @@ import type { Vec2 } from '../../core/model/Properties';
|
||||
import type { CutSize } from '../../core/model/texture';
|
||||
import { type Size, Theme, textStyle, ui, Widget, zoomFactor, crispText } from '../../ui';
|
||||
import { dashedLine } from '../../viewport/symbols';
|
||||
import { ViewTween } from '../viewTween';
|
||||
import { cutLines, imageToLocal, type SheetLine, type SheetView, zoomAbout } from './sheetView';
|
||||
|
||||
/**
|
||||
@@ -24,6 +25,13 @@ export abstract class SheetCanvas<A> extends Widget {
|
||||
/** Fit once the widget has a real size and a picture to fit (a tab starts hidden). */
|
||||
protected needsFit = true;
|
||||
private panning: { pointer: Vec2; panX: number; panY: number } | null = null;
|
||||
protected readonly tween = new ViewTween({
|
||||
read: () => ({ scale: this.view.zoom, x: this.view.panX, y: this.view.panY }),
|
||||
write: (map) => {
|
||||
this.view = { panX: map.x, panY: map.y, zoom: map.scale };
|
||||
},
|
||||
draw: () => this.redraw(),
|
||||
});
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@@ -66,6 +74,7 @@ export abstract class SheetCanvas<A> extends Widget {
|
||||
}
|
||||
|
||||
saveView(): { view: SheetView; needsFit: boolean } {
|
||||
this.tween.land();
|
||||
return { view: { ...this.view }, needsFit: this.needsFit };
|
||||
}
|
||||
|
||||
@@ -78,8 +87,10 @@ export abstract class SheetCanvas<A> extends Widget {
|
||||
}
|
||||
|
||||
fit(): void {
|
||||
this.needsFit = true;
|
||||
this.redraw();
|
||||
this.tween.change(() => {
|
||||
this.needsFit = true;
|
||||
this.redraw();
|
||||
});
|
||||
}
|
||||
|
||||
/** Where a picture pixel sits on screen; the check scripts aim their drags with it. */
|
||||
@@ -96,9 +107,11 @@ export abstract class SheetCanvas<A> extends Widget {
|
||||
|
||||
protected onWheel(e: FederatedWheelEvent): void {
|
||||
const local = this.toLocal(e.global);
|
||||
this.view = zoomAbout(this.view, zoomFactor(e.deltaY), local.x, local.y);
|
||||
this.needsFit = false;
|
||||
this.redraw();
|
||||
this.tween.change(() => {
|
||||
this.view = zoomAbout(this.view, zoomFactor(e.deltaY), local.x, local.y);
|
||||
this.needsFit = false;
|
||||
this.redraw();
|
||||
});
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
@@ -129,6 +142,7 @@ export abstract class SheetCanvas<A> extends Widget {
|
||||
const local = this.toLocal(e.global);
|
||||
const pans = e.button === 0 && e.shiftKey && !this.shiftPresses(target, { x: local.x, y: local.y });
|
||||
if (e.button === 1 || pans) {
|
||||
this.tween.finish();
|
||||
this.panning = { pointer: { x: local.x, y: local.y }, panX: this.view.panX, panY: this.view.panY };
|
||||
e.stopPropagation();
|
||||
return;
|
||||
|
||||
Reference in new issue
Block a user