drawnui-react 0.1.0-preview.10 → 0.1.0-preview.11
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/README.md +11 -0
- package/SKIPPED.md +3 -1
- package/dist/controls/DrawnGame.d.ts +37 -0
- package/dist/controls/DrawnGame.js +60 -0
- package/dist/core/Animators.d.ts +9 -0
- package/dist/core/Animators.js +26 -1
- package/dist/core/Canvas.js +6 -1
- package/dist/core/SkiaControl.d.ts +8 -1
- package/dist/core/SkiaControl.js +17 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@ through a custom `react-reconciler` renderer.
|
|
|
6
6
|
|
|
7
7
|
Check out latest playbook: [helloreact.drawnui.net](https://helloreact.drawnui.net/) 👈
|
|
8
8
|
|
|
9
|
+
Playground to use and test live: [drawfiddle.com](https://drawfiddle.com/) 👈
|
|
10
|
+
|
|
9
11
|
*Work in progress*: the goal is same API surface and semantics as DrawnUi (.NET) — same control names, same PascalCase
|
|
10
12
|
property names, same measure/arrange/paint contract — so knowledge and docs transfer 1:1.
|
|
11
13
|
|
|
@@ -65,6 +67,15 @@ accessibility all live in the engine and would work identically under Vue, Svelt
|
|
|
65
67
|
That is also why the demo pages describe DrawnUi features, not React ones: the same pages are meant to be reused as
|
|
66
68
|
the showcase for other frameworks on this engine.
|
|
67
69
|
|
|
70
|
+
## Games
|
|
71
|
+
|
|
72
|
+
`DrawnGame` (from DrawnUi.Gaming) is ported: a layout with a game loop. Subclass it, build your sprites in the
|
|
73
|
+
constructor, call `StartLoop()`, and override `GameLoop(deltaSeconds)` and `OnKeyDown` / `OnKeyUp` (DOM `event.code`
|
|
74
|
+
names). Move sprites with `Left` / `Top` on cached controls, as in C#. The demo's `#/pong` page is the .NET Pong
|
|
75
|
+
sample (`src/Shared/Samples/Pong.Shared`) ported line for line: the game classes are plain TypeScript in
|
|
76
|
+
`samples/demo/pages/pong`, mounted from `PongPage.tsx` with `AddSubView`, and a small `RescalingLayout` fits the
|
|
77
|
+
360×640 field to any screen by changing the children's rendering scale.
|
|
78
|
+
|
|
68
79
|
## Context menu (right click)
|
|
69
80
|
|
|
70
81
|
A right click on the canvas normally opens the browser's own menu ("Save image as…"). Every mouse button goes
|
package/SKIPPED.md
CHANGED
|
@@ -31,7 +31,9 @@ DrawnUi name and semantics; nothing here is a redesign, only an omission.
|
|
|
31
31
|
| `FillGradient`, `SkiaShape.StrokeGradient` | ported | `SkiaGradient` as a plain object: `Type` Linear (Start/End ratios or CSS `Angle`), Circular / Conical (radius = half the smaller side), Oval (radial scaled to the box), Sweep (around the center, `Value1`/`Value2` angles), `ColorPositions`, `TileMode`, `Light` (< 1 darker, > 1 lighter — HSL lightness, C# uses `MakeDarker/MakeLighter`, exact curve unverified), `Opacity`, `BlendMode`; C# `SetupGradient` paint setup (white base); shaders cached per gradient object and rect (C# `Version` cache: a new React literal replaces the old shaders). `SkiaLabel.FillGradient` paints the glyphs (`GradientByLines` default true = per line bounds, false = the text block) and the background only when `BackgroundColor` is set, like the C# label override. `SkiaLabel` text stroke (`StrokeColor`, `StrokeWidth` points, `StrokeGradient`) and drop shadow (`DropShadowColor`, `DropShadowSize`, `DropShadowOffsetX/Y`) in the C# order shadow → stroke → fill, measurement inflated by stroke × 2 and shadow size + offset like C#. Not ported: `Background` MAUI brushes, `FillBlendMode`. |
|
|
32
32
|
| `SkiaShell` | partial (React-level) | `Routes` (route -> JSX page factory), `GoToAsync(route, animated)` / `GoBackAsync(animated)` / `PopToRootAsync` with the SkiaViewSwitcher slide (`PagesAnimationSpeed` 200 ms, pushed pages opaque over the page below, lower pages hidden when idle), `NavigationStack`, `CanGoBack`, `Route`, nav bar with Back + title, `useShell()`; `OpenPopupAsync(content, { animated, closeWhenBackgroundTapped, showOverlay, backgroundColor })` (C# PopupWrapper: dimmed backdrop, scale 0.5→1 + fade over `PopupsAnimationSpeed`, tap outside the content closes), `ClosePopupAsync`, `CloseAllPopups`; `PushModalAsync(content, { useGestures, animated, freezeBackground })` (C# ModalWrapper: full-screen `SkiaDrawer` FromBottom HeaderSize 0, drag-to-close with `useGestures`), `PopModalAsync`; `ShowToast(text | content, ms)` (C# layout: bottom banner, slide + fade 300/250 ms, `ShellDefaults.Toast*`), `CloseAllToasts`; `PopupsCount`/`ModalsCount`/`ToastsCount`; `ShellDefaults` = the C# statics (`PopupBackgroundColor`, `PopupsBackgroundBlur`, `PopupsAnimationSpeed`, `PopupsCancelAnimationsAfterMs`, `ZIndex*`, `Toast*`, tab colors); popups/modals sit on a `SkiaBackdrop` (blur + tint); `Tabs` (C# SkiaViewSwitcher tabs: bottom tab bar, per-tab page stacks, `SelectedTab`, `SelectTabAsync`, `PopTabToRootAsync`); `GoBackAsync` follows C# `GoBackDefault` (top popup, then modal, then page); React extension `UseBrowserHistory` (default true): pages in the URL hash `#/a/b`, one history entry per page / popup / modal, browser Back unwinds in the same order, deep links restore the page stack. Not ported: engine-level shell / `NavigationLayout` view switcher, frozen-screenshot backgrounds, `pixelsScaleInFrom`, `Navigating`/`Navigated`/`RouteChanged` events, `RegisterRoute` by type with arguments, `IHandleGoBack`, insets, `PushAsync(page instance)`; `AnimateTabs` + `TabsAnimationSpeed` (150) ported with the C# SelectLeftTab/SelectRightTab slide (0.75 width, fade, back-ease 0.55) — only the tab roots animate, pages pushed inside the leaving tab drop instantly. Browser history: a `popstate` at the same depth with another hash (a plain `<a href="#/route">` link, a typed hash) rebuilds the stack like a forward navigation. |
|
|
33
33
|
| Styles / `ConfigureStyles` | ported | `Super.UseDrawnUi().ConfigureStyles(styles => styles.AddStyle({ TargetType, ApplyToDerivedTypes, Setters }))`, the C# `Style` shape with the `Setter` list written as a plain object (`Setters: { FontFamily: "FontText" }`) because controls hold plain properties instead of BindableProperty. Per-class resolution cached, base-type styles applied furthest-first so the nearest wins, exact type last (C# `ResolveStylesForType`). Applied by the reconciler between `new` and the JSX props, so props override; a control built in code-behind is styled on its first `Measure` and keeps values that differ from its class defaults (the React stand-in for C# `ExplicitPropertiesSet`, which needs BindableProperty). Not ported: `BasedOn`, `Class`, `Triggers`, `Behaviors`, the per-control `Style` property, `ConditionalStyle`. |
|
|
34
|
-
| Animators core: `AnimatorBase` / `SkiaValueAnimator` / `RenderingAnimator` (`IOverlayEffect`), `Canvas.RegisterAnimator`/`AnimatingControls`, `PostAnimators` + `ExecutePostAnimators`, `Easing` (Linear/Cubic*) | ported | Frame-driven: a running animator keeps frames coming, idle canvas draws nothing. |
|
|
34
|
+
| Animators core: `AnimatorBase` / `SkiaValueAnimator` / `RenderingAnimator` (`IOverlayEffect`), `Canvas.RegisterAnimator`/`AnimatingControls`, `PostAnimators` + `ExecutePostAnimators`, `Easing` (Linear/Cubic*) | ported | Frame-driven: a running animator keeps frames coming, idle canvas draws nothing. An animator started before its control is attached waits in `PendingUnattachedAnimators` and starts on the control's first arrange (C# starts it in `SuperViewChanged`). Frame time = the frame's vsync timestamp, see PARITY "Animators run on vsync time". |
|
|
35
|
+
| `ActionOnTickAnimator` | ported | Runs an action with the frame time on every frame until stopped. |
|
|
36
|
+
| `DrawnGame` (DrawnUi.Gaming) | ported | `SkiaLayout` subclass: `StartLoop(delayMs)` / `StopLoop`, override `GameLoop(deltaSeconds)`, `OnKeyDown` / `OnKeyUp` (DOM `event.code` names, subscribed through `KeyboardManager` for the game's lifetime), `Pause` / `Resume` / `IsPaused` / `OnPaused` / `OnResumed`, `LastFrameTimeNanos`. Not ported: `FrameTimeInterpolator` (C# disables it on BROWSER too), `IGame`, the `IsPaused` property-changed notification. Demo: `#/pong`, the .NET Pong.Shared sample. |
|
|
35
37
|
| Touch feedback: `AnimationTapped="Ripple"` + `TouchEffectColor` + `AnimationTappedSpeed` on any control, `SkiaButton.ApplyEffect="Ripple"` (on Down), `PlayRippleAnimation`, `RippleAnimator`, `ClipEffects`/`CreateClip` | ported | Same numbers as C#: 500ms CubicIn, radius 300pt, opacity 0.20 fading over 1.15x progress. |
|
|
36
38
|
| `Shimmer` touch animation, `ShimmerAnimator`, `ClippedEffectsWith`, `TransformView`, `DelayCallbackMs`, `removePrevious`, `Pause`/`Resume`, `UseInterpolator`, spring/deceleration timing, `AnimateExtensions` (`FadeToAsync`, `TranslateToAsync`, ...) | skipped | |
|
|
37
39
|
| `VisualEffects` + `SkiaEffect` / `IPostRendererEffect` / `ISkiaGestureProcessor` | partial | `VisualEffects` array on every control (assign a new array; effects attach / detach, dispose with the control, `DisableEffects`, `GetEffectMargin` aggregated into the effects margin); post renderers run after the direct paint (C# `DrawDirectInternal`) or instead of the Image cache blit (C# `DrawRenderObject`, the effect samples `CachedImage`); effects with `ProcessGestures` see the parent's gestures first (C# `EffectsGestureProcessors`). Not ported: `IRenderEffect` chain (`ChainEffectResult`, `Draw(ctx, drawControl)`), `IColorEffect` / `IImageEffect` on VisualEffects (`BlurEffect`, `DropShadowEffect`, `OuterGlowEffect`, `ColorPresetEffect`, `AdjustRGBEffect`, `ChainAdjust*`, `Chain*`), `IStateEffect.UpdateState`, `Tag` lookup. |
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type InputKey } from "../core/KeyboardManager";
|
|
2
|
+
import { SkiaLayout } from "./SkiaLayout";
|
|
3
|
+
/**
|
|
4
|
+
* Mirrors DrawnUi.Gaming DrawnGame (SharedGame/DrawnGame.cs + Game.Input.cs): a layout that runs a game loop.
|
|
5
|
+
* `StartLoop()` / `StopLoop()`, override `GameLoop(deltaSeconds)` for the game and `OnKeyDown` / `OnKeyUp` for keys
|
|
6
|
+
* (DOM `event.code` names, e.g. "ArrowLeft", "Space"). The loop is an `ActionOnTickAnimator`, ticked once per drawn
|
|
7
|
+
* frame; started before the game is attached it waits for the first layout, like C#.
|
|
8
|
+
* Not ported: `FrameTimeInterpolator` (C# disables it on BROWSER too, the raw frame delta is used).
|
|
9
|
+
*/
|
|
10
|
+
export declare class DrawnGame extends SkiaLayout {
|
|
11
|
+
private appLoop?;
|
|
12
|
+
protected LastFrameTimeNanos: number;
|
|
13
|
+
private isPaused;
|
|
14
|
+
private readonly onKeyDown;
|
|
15
|
+
private readonly onKeyChar;
|
|
16
|
+
private readonly onKeyUp;
|
|
17
|
+
constructor();
|
|
18
|
+
protected OnDisposing(): void;
|
|
19
|
+
protected OnResumed(): void;
|
|
20
|
+
protected OnPaused(): void;
|
|
21
|
+
/** Override this for your game. `deltaSeconds` is the time elapsed between the previous frame and this one. */
|
|
22
|
+
GameLoop(_deltaSeconds: number): void;
|
|
23
|
+
/** Stops the game loop. */
|
|
24
|
+
StopLoop(): void;
|
|
25
|
+
/** Starts the game loop. */
|
|
26
|
+
StartLoop(delayMs?: number): void;
|
|
27
|
+
/** Internal, override `GameLoop` for your game. Frame time is in nanoseconds. */
|
|
28
|
+
protected GameTick(frameTimeNanos: number): void;
|
|
29
|
+
get IsPaused(): boolean;
|
|
30
|
+
set IsPaused(value: boolean);
|
|
31
|
+
Pause(): void;
|
|
32
|
+
Resume(): void;
|
|
33
|
+
/** Override this to process game keys. */
|
|
34
|
+
OnKeyDown(_key: InputKey): void;
|
|
35
|
+
/** Override this to process game keys. */
|
|
36
|
+
OnKeyUp(_key: InputKey): void;
|
|
37
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ActionOnTickAnimator } from "../core/Animators";
|
|
2
|
+
import { KeyboardManager } from "../core/KeyboardManager";
|
|
3
|
+
import { SkiaLayout } from "./SkiaLayout";
|
|
4
|
+
/**
|
|
5
|
+
* Mirrors DrawnUi.Gaming DrawnGame (SharedGame/DrawnGame.cs + Game.Input.cs): a layout that runs a game loop.
|
|
6
|
+
* `StartLoop()` / `StopLoop()`, override `GameLoop(deltaSeconds)` for the game and `OnKeyDown` / `OnKeyUp` for keys
|
|
7
|
+
* (DOM `event.code` names, e.g. "ArrowLeft", "Space"). The loop is an `ActionOnTickAnimator`, ticked once per drawn
|
|
8
|
+
* frame; started before the game is attached it waits for the first layout, like C#.
|
|
9
|
+
* Not ported: `FrameTimeInterpolator` (C# disables it on BROWSER too, the raw frame delta is used).
|
|
10
|
+
*/
|
|
11
|
+
export class DrawnGame extends SkiaLayout {
|
|
12
|
+
appLoop;
|
|
13
|
+
LastFrameTimeNanos = 0;
|
|
14
|
+
isPaused = false;
|
|
15
|
+
onKeyDown = (key) => this.OnKeyDown(key);
|
|
16
|
+
onKeyChar = () => { };
|
|
17
|
+
onKeyUp = (key) => this.OnKeyUp(key);
|
|
18
|
+
constructor() {
|
|
19
|
+
super();
|
|
20
|
+
KeyboardManager.Subscribe(this.onKeyDown, this.onKeyChar, this.onKeyUp);
|
|
21
|
+
}
|
|
22
|
+
OnDisposing() {
|
|
23
|
+
KeyboardManager.Unsubscribe(this.onKeyDown, this.onKeyChar, this.onKeyUp);
|
|
24
|
+
this.appLoop?.Dispose();
|
|
25
|
+
this.appLoop = undefined;
|
|
26
|
+
super.OnDisposing();
|
|
27
|
+
}
|
|
28
|
+
OnResumed() { }
|
|
29
|
+
OnPaused() { }
|
|
30
|
+
/** Override this for your game. `deltaSeconds` is the time elapsed between the previous frame and this one. */
|
|
31
|
+
GameLoop(_deltaSeconds) { }
|
|
32
|
+
/** Stops the game loop. */
|
|
33
|
+
StopLoop() { this.appLoop?.Stop(); }
|
|
34
|
+
/** Starts the game loop. */
|
|
35
|
+
StartLoop(delayMs = 0) {
|
|
36
|
+
this.appLoop ??= new ActionOnTickAnimator(this, (t) => this.GameTick(t));
|
|
37
|
+
this.appLoop.Start(delayMs);
|
|
38
|
+
}
|
|
39
|
+
/** Internal, override `GameLoop` for your game. Frame time is in nanoseconds. */
|
|
40
|
+
GameTick(frameTimeNanos) {
|
|
41
|
+
const deltaSeconds = (frameTimeNanos - this.LastFrameTimeNanos) / 1_000_000_000;
|
|
42
|
+
this.LastFrameTimeNanos = frameTimeNanos;
|
|
43
|
+
this.GameLoop(deltaSeconds);
|
|
44
|
+
}
|
|
45
|
+
get IsPaused() { return this.isPaused; }
|
|
46
|
+
set IsPaused(value) { this.isPaused = value; }
|
|
47
|
+
Pause() {
|
|
48
|
+
this.IsPaused = true;
|
|
49
|
+
this.OnPaused();
|
|
50
|
+
}
|
|
51
|
+
Resume() {
|
|
52
|
+
this.LastFrameTimeNanos = Math.round(performance.now() * 1_000_000);
|
|
53
|
+
this.IsPaused = false;
|
|
54
|
+
this.OnResumed();
|
|
55
|
+
}
|
|
56
|
+
/** Override this to process game keys. */
|
|
57
|
+
OnKeyDown(_key) { }
|
|
58
|
+
/** Override this to process game keys. */
|
|
59
|
+
OnKeyUp(_key) { }
|
|
60
|
+
}
|
package/dist/core/Animators.d.ts
CHANGED
|
@@ -29,6 +29,15 @@ export declare class AnimatorBase {
|
|
|
29
29
|
Dispose(): void;
|
|
30
30
|
private SetRunning;
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* DrawnUi ActionOnTickAnimator: runs `action(frameTimeNanos)` on every frame until stopped, a game loop for
|
|
34
|
+
* DrawnGame or any per-frame work.
|
|
35
|
+
*/
|
|
36
|
+
export declare class ActionOnTickAnimator extends AnimatorBase {
|
|
37
|
+
private readonly action;
|
|
38
|
+
constructor(parent: SkiaControl, action: (frameTimeNanos: number) => void);
|
|
39
|
+
TickFrame(frameTimeNanos: number): boolean;
|
|
40
|
+
}
|
|
32
41
|
/** DrawnUi SkiaValueAnimator: drives mValue from mMinValue to mMaxValue over Speed ms with Easing. */
|
|
33
42
|
export declare class SkiaValueAnimator extends AnimatorBase {
|
|
34
43
|
mValue: number;
|
package/dist/core/Animators.js
CHANGED
|
@@ -29,7 +29,14 @@ export class AnimatorBase {
|
|
|
29
29
|
if (!parent.PostAnimators.includes(effect))
|
|
30
30
|
parent.PostAnimators.push(effect);
|
|
31
31
|
}
|
|
32
|
-
const
|
|
32
|
+
const superview = parent.Superview;
|
|
33
|
+
const ok = superview?.RegisterAnimator(this) ?? false;
|
|
34
|
+
// C# SkiaControl.RegisterAnimator: not attached yet, queue it; the parent starts it on its first arrange
|
|
35
|
+
if (!superview && !parent.IsDisposed) {
|
|
36
|
+
const pending = parent.PendingUnattachedAnimators ??= [];
|
|
37
|
+
if (!pending.includes(this))
|
|
38
|
+
pending.push(this);
|
|
39
|
+
}
|
|
33
40
|
parent.Repaint();
|
|
34
41
|
return ok;
|
|
35
42
|
}
|
|
@@ -42,6 +49,9 @@ export class AnimatorBase {
|
|
|
42
49
|
if (i >= 0)
|
|
43
50
|
parent.PostAnimators.splice(i, 1);
|
|
44
51
|
}
|
|
52
|
+
const pending = parent.PendingUnattachedAnimators?.indexOf(this) ?? -1;
|
|
53
|
+
if (pending >= 0)
|
|
54
|
+
parent.PendingUnattachedAnimators.splice(pending, 1);
|
|
45
55
|
parent.Superview?.UnregisterAnimator(this.Uid);
|
|
46
56
|
}
|
|
47
57
|
Start(delayMs = 0) {
|
|
@@ -96,6 +106,21 @@ export class AnimatorBase {
|
|
|
96
106
|
this.OnStop?.();
|
|
97
107
|
}
|
|
98
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* DrawnUi ActionOnTickAnimator: runs `action(frameTimeNanos)` on every frame until stopped, a game loop for
|
|
111
|
+
* DrawnGame or any per-frame work.
|
|
112
|
+
*/
|
|
113
|
+
export class ActionOnTickAnimator extends AnimatorBase {
|
|
114
|
+
action;
|
|
115
|
+
constructor(parent, action) {
|
|
116
|
+
super(parent);
|
|
117
|
+
this.action = action;
|
|
118
|
+
}
|
|
119
|
+
TickFrame(frameTimeNanos) {
|
|
120
|
+
this.action(frameTimeNanos);
|
|
121
|
+
return super.TickFrame(frameTimeNanos);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
99
124
|
/** DrawnUi SkiaValueAnimator: drives mValue from mMinValue to mMaxValue over Speed ms with Easing. */
|
|
100
125
|
export class SkiaValueAnimator extends AnimatorBase {
|
|
101
126
|
mValue = 0;
|
package/dist/core/Canvas.js
CHANGED
|
@@ -135,7 +135,12 @@ export class Canvas {
|
|
|
135
135
|
Draw(canvas) {
|
|
136
136
|
const started = performance.now();
|
|
137
137
|
this.ProcessPendingGestures();
|
|
138
|
-
|
|
138
|
+
// Animators run on the frame's vsync time (inside a rAF callback document.timeline.currentTime is the frame
|
|
139
|
+
// timestamp), not on when this callback happened to start: callback starts drift by several ms around vsync
|
|
140
|
+
// (measured 12..23 ms between frames shown 16.7 ms apart), and a game loop moving by speed * delta then
|
|
141
|
+
// jumps unevenly on screen.
|
|
142
|
+
const frameTime = Number(document.timeline?.currentTime ?? started);
|
|
143
|
+
const executed = this.ExecuteAnimators(Math.round(frameTime * 1_000_000));
|
|
139
144
|
const canRender = this.CanRender;
|
|
140
145
|
if (canRender && !this.WasRendered)
|
|
141
146
|
this.WillFirstTimeDraw?.(this, { Canvas: canvas, Surface: this.surface });
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Canvas as SkCanvas, Image, Path, SkPicture, Surface } from "canvaskit-wasm";
|
|
2
2
|
import { type Color, type LayoutOptions, SKRect, ScaledSize, type SkiaCacheType, type SkiaGradient, type SkiaTouchAnimation, Thickness } from "./Types";
|
|
3
|
-
import { type IOverlayEffect } from "./Animators";
|
|
3
|
+
import { type AnimatorBase, type IOverlayEffect } from "./Animators";
|
|
4
4
|
import { Easing } from "./Easing";
|
|
5
5
|
import type { Canvas } from "./Canvas";
|
|
6
6
|
import { ContextMenuEventArgs, ControlTappedEventArgs, GestureEventProcessingInfo, type LockTouch, SKPoint, SkiaGesturesInfo, SkiaGesturesParameters } from "./Gestures";
|
|
@@ -164,6 +164,11 @@ export declare class SkiaControl {
|
|
|
164
164
|
AnimationTappedSpeed: number;
|
|
165
165
|
/** Overlay effects drawn above this control's content every frame (ripple etc). */
|
|
166
166
|
readonly PostAnimators: IOverlayEffect[];
|
|
167
|
+
/**
|
|
168
|
+
* C# PendingUnattachedAnimators: animators started while no Canvas was above this control yet (e.g. a game loop
|
|
169
|
+
* started in a constructor). They start on the control's first arrange, where C# starts them in SuperViewChanged.
|
|
170
|
+
*/
|
|
171
|
+
PendingUnattachedAnimators?: AnimatorBase[];
|
|
167
172
|
private visualEffects;
|
|
168
173
|
/** Post-renderer effects among VisualEffects (C# EffectPostRenderers). */
|
|
169
174
|
EffectPostRenderers: (SkiaEffect & IPostRendererEffect)[];
|
|
@@ -220,6 +225,8 @@ export declare class SkiaControl {
|
|
|
220
225
|
protected MeasureAbsolute(_widthConstraint: number, _heightConstraint: number, scale: number): ScaledSize;
|
|
221
226
|
/** Places MeasuredSize inside destination per Margin + Horizontal/VerticalOptions -> DrawingRect. */
|
|
222
227
|
Arrange(destination: SKRect, _widthRequest: number, _heightRequest: number, scale: number): void;
|
|
228
|
+
/** C# SuperViewChanged: starts the animators queued before this control was attached. */
|
|
229
|
+
private StartPendingAnimators;
|
|
223
230
|
private static nextAccessibilityId;
|
|
224
231
|
/** Stable identity for the DOM overlay. */
|
|
225
232
|
readonly AccessibilityId: number;
|
package/dist/core/SkiaControl.js
CHANGED
|
@@ -199,6 +199,11 @@ export class SkiaControl {
|
|
|
199
199
|
AnimationTappedSpeed = 0;
|
|
200
200
|
/** Overlay effects drawn above this control's content every frame (ripple etc). */
|
|
201
201
|
PostAnimators = [];
|
|
202
|
+
/**
|
|
203
|
+
* C# PendingUnattachedAnimators: animators started while no Canvas was above this control yet (e.g. a game loop
|
|
204
|
+
* started in a constructor). They start on the control's first arrange, where C# starts them in SuperViewChanged.
|
|
205
|
+
*/
|
|
206
|
+
PendingUnattachedAnimators;
|
|
202
207
|
// ---- visual effects (C# VisualEffects: attached SkiaEffect objects) ----
|
|
203
208
|
visualEffects = [];
|
|
204
209
|
/** Post-renderer effects among VisualEffects (C# EffectPostRenderers). */
|
|
@@ -363,10 +368,21 @@ export class SkiaControl {
|
|
|
363
368
|
const y = availT + SkiaControl.Align(this.VerticalOptions, fullH, h);
|
|
364
369
|
this.DrawingRect = SKRect.Create(x, y, w, h);
|
|
365
370
|
this.OnLayoutChanged();
|
|
371
|
+
if (this.PendingUnattachedAnimators)
|
|
372
|
+
this.StartPendingAnimators();
|
|
366
373
|
// DrawnUi OnLayoutReady: an accessible control registers itself on its first layout
|
|
367
374
|
if (!this.registeredWithAccessibility && this.IsAccessibilityElement)
|
|
368
375
|
this.NotifyAccessibility();
|
|
369
376
|
}
|
|
377
|
+
/** C# SuperViewChanged: starts the animators queued before this control was attached. */
|
|
378
|
+
StartPendingAnimators() {
|
|
379
|
+
if (!this.Superview)
|
|
380
|
+
return;
|
|
381
|
+
const pending = this.PendingUnattachedAnimators;
|
|
382
|
+
this.PendingUnattachedAnimators = undefined;
|
|
383
|
+
for (const animator of pending)
|
|
384
|
+
animator.Start();
|
|
385
|
+
}
|
|
370
386
|
// ---- accessibility (DrawnUi ISkiaAccessibilityNode) ----
|
|
371
387
|
static nextAccessibilityId = 1;
|
|
372
388
|
/** Stable identity for the DOM overlay. */
|
|
@@ -1038,6 +1054,7 @@ export class SkiaControl {
|
|
|
1038
1054
|
this.ownAnimations.clear();
|
|
1039
1055
|
for (const e of [...this.PostAnimators])
|
|
1040
1056
|
e.Stop?.();
|
|
1057
|
+
this.PendingUnattachedAnimators = undefined;
|
|
1041
1058
|
this.PostAnimators.length = 0;
|
|
1042
1059
|
this.DestroyRenderingObject();
|
|
1043
1060
|
this.compositeSurface?.delete();
|
package/dist/index.d.ts
CHANGED
|
@@ -40,6 +40,7 @@ export * from "./controls/SkiaBackdrop";
|
|
|
40
40
|
export * from "./controls/SkiaEditor";
|
|
41
41
|
export * from "./controls/SkiaSprite";
|
|
42
42
|
export * from "./controls/SkiaSpriteSet";
|
|
43
|
+
export * from "./controls/DrawnGame";
|
|
43
44
|
export * from "./core/KeyboardManager";
|
|
44
45
|
export * from "./controls/AnimatedFramesRenderer";
|
|
45
46
|
export * from "./controls/SkiaLottie";
|
package/dist/index.js
CHANGED
|
@@ -40,6 +40,7 @@ export * from "./controls/SkiaBackdrop";
|
|
|
40
40
|
export * from "./controls/SkiaEditor";
|
|
41
41
|
export * from "./controls/SkiaSprite";
|
|
42
42
|
export * from "./controls/SkiaSpriteSet";
|
|
43
|
+
export * from "./controls/DrawnGame";
|
|
43
44
|
export * from "./core/KeyboardManager";
|
|
44
45
|
export * from "./controls/AnimatedFramesRenderer";
|
|
45
46
|
export * from "./controls/SkiaLottie";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "drawnui-react",
|
|
3
|
-
"version": "0.1.0-preview.
|
|
3
|
+
"version": "0.1.0-preview.11",
|
|
4
4
|
"description": "DrawnUi for React: the DrawnUi (.NET) drawn-UI engine in TypeScript on CanvasKit (Skia WASM). Same controls, same PascalCase API as DrawnUi.Net.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Nick Kovalsky (AppoMobi)",
|