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
@@ -51,6 +51,10 @@ export function sceneHooks({ state, editor }: HookContext) {
|
||||
sceneHeaderAt: (name: 'fit' | 'visibility') =>
|
||||
centreOf(name === 'fit' ? scenePane.fitButton : scenePane.visibilityButton),
|
||||
viewportInfo: () => viewport.debugInfo(),
|
||||
/** The scene view's centre and zoom, and whether a zoom transition is under way (#309). */
|
||||
sceneView: () => viewport.viewState(),
|
||||
/** The world point under a screen point, as the scene view shows it right now. */
|
||||
sceneWorldAt: (x: number, y: number) => viewport.worldAt(x, y),
|
||||
/** A full redraw of the Scene tab – what a pan, which only moves the camera, is compared with. */
|
||||
viewportRedraw: () => viewport.redraw(),
|
||||
/** Where a dragged asset would land in the Scene tab, and why not when it may not. */
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
} from '../../ui';
|
||||
import { Theme } from '../../ui/Theme';
|
||||
import { currentFontPicker } from '../font/FontPicker';
|
||||
import { generalSettings, keepGeneralSettings } from '../generalSettings';
|
||||
import { currentGroupDialog } from '../panels/GroupNameDialog';
|
||||
import { currentSoundPicker } from '../sound/SoundPicker';
|
||||
import { rectOf, textProbe } from '../textProbe';
|
||||
@@ -318,6 +319,21 @@ export function uiHooks({ app, state, editor }: HookContext) {
|
||||
const dialog = editor.projectSettingsDialog();
|
||||
return dialog ? centreOf(dialog.symbolScaleControl) : null;
|
||||
},
|
||||
/** The "General settings" dialog (#309) as its field stands, null while shut. */
|
||||
generalSettingsDialog: () => editor.generalSettingsDialog()?.values() ?? null,
|
||||
/** Where to click the zoom field inside that dialog. */
|
||||
generalSettingsZoomAt: () => {
|
||||
const dialog = editor.generalSettingsDialog();
|
||||
return dialog ? centreOf(dialog.zoomControl) : null;
|
||||
},
|
||||
/**
|
||||
* The zoom transition in ms as kept in this browser; with `ms` it is set first. The harness sets
|
||||
* 0 on every load, so a check reading the zoom after the wheel finds the step and not the way.
|
||||
*/
|
||||
zoomTransition: (ms?: number) => {
|
||||
if (ms !== undefined) keepGeneralSettings({ zoomTransitionMs: ms });
|
||||
return generalSettings().zoomTransitionMs;
|
||||
},
|
||||
/** The "Open project" dialog (#183): the projects it lists and the row picked. */
|
||||
projectPicker: () => editor.projectPickerDialog()?.info() ?? null,
|
||||
/** The "Snapshots" dialog (#231): its rows, newest first, the row picked and its name field. */
|
||||
|
||||
Reference in new issue
Block a user