narraleaf-react 1.0.0 → 1.1.0
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/dist/game/nlcore/action/actionTypes.d.ts +2 -1
- package/dist/game/nlcore/action/actions/videoAction.d.ts +11 -0
- package/dist/game/nlcore/common/elements.d.ts +1 -0
- package/dist/game/nlcore/elements/video.d.ts +44 -4
- package/dist/game/player/elements/image/Image.d.ts +66 -0
- package/dist/game/player/lib/visibilityFader.d.ts +1 -0
- package/dist/game/player/type.d.ts +7 -3
- package/dist/main.js +53 -52
- package/package.json +124 -124
- package/dist/game/player/lib/useElementVisibility.d.ts +0 -5
|
@@ -16,6 +16,7 @@ import type { Transition } from "../elements/transition/transition";
|
|
|
16
16
|
import type { ImageTransition } from "../elements/transition/transitions/image/imageTransition";
|
|
17
17
|
import type { Layer } from "../elements/layer";
|
|
18
18
|
import type { VfxFadeOptions } from "../elements/vfx";
|
|
19
|
+
import type { VideoFadeOptions } from "../elements/video";
|
|
19
20
|
import type { PuppetCommandOptions } from "../elements/displayable/puppet";
|
|
20
21
|
export declare const DisplayableActionTypes: {
|
|
21
22
|
readonly action: "displayable:action";
|
|
@@ -162,7 +163,7 @@ export declare const VideoActionTypes: {
|
|
|
162
163
|
readonly seek: "video:seek";
|
|
163
164
|
};
|
|
164
165
|
export type VideoActionContentType = {
|
|
165
|
-
[K in typeof VideoActionTypes[keyof typeof VideoActionTypes]]: K extends "video:action" ? any : K extends "video:
|
|
166
|
+
[K in typeof VideoActionTypes[keyof typeof VideoActionTypes]]: K extends "video:action" ? any : K extends "video:show" | "video:hide" ? [VideoFadeOptions?] : K extends "video:preload" | "video:play" | "video:pause" | "video:stop" | "video:resume" ? [] : K extends "video:seek" ? [number] : any;
|
|
166
167
|
};
|
|
167
168
|
export declare const VfxActionTypes: {
|
|
168
169
|
readonly action: "vfx:action";
|
|
@@ -21,6 +21,17 @@ export declare class VideoAction<T extends Values<typeof VideoActionTypes> = Val
|
|
|
21
21
|
};
|
|
22
22
|
executeAction(gameState: GameState, injection: ActionExecutionInjection): Awaitable<CalledActionResult>;
|
|
23
23
|
private changeStateBase;
|
|
24
|
+
/**
|
|
25
|
+
* {@link changeStateBase} for a fade, which the player can walk away from halfway.
|
|
26
|
+
*
|
|
27
|
+
* Aborting the action - stepping back, loading a save, starting a new game - abandons the fade
|
|
28
|
+
* where it stands, and the element goes back to showing what `video.state.display` says: the undo
|
|
29
|
+
* that follows the abort then decides what that is. The handler is told through `signal`, so the
|
|
30
|
+
* part of it that records the change never runs for a change that did not happen.
|
|
31
|
+
*/
|
|
32
|
+
private changeStateFading;
|
|
33
|
+
/** Whether a show or hide was asked to fade, rather than to happen at once. */
|
|
34
|
+
private static fades;
|
|
24
35
|
private changeState;
|
|
25
36
|
private changeStateAsync;
|
|
26
37
|
stringify(_story: Story, _seen: Set<LogicAction.Actions>, _strict: boolean): string;
|
|
@@ -23,6 +23,7 @@ import { Puppet } from "../elements/displayable/puppet";
|
|
|
23
23
|
import { NVLToken } from "../elements/nvl";
|
|
24
24
|
export { Character, Narrator, Condition, Control, Image, Lambda, Menu, NVLToken, Scene, Script, Sentence, Sound, Story, Transform, Word, Text, Pause, TextEvent, Persistent, Service, Layer, Camera, Video, Vfx, Puppet, };
|
|
25
25
|
export type { VfxConfig, VfxBlendMode, VfxFadeOptions } from "../elements/vfx";
|
|
26
|
+
export type { VideoFadeOptions } from "../elements/video";
|
|
26
27
|
export type { IPuppetUserConfig, PuppetConfig, PuppetCommandOptions, } from "../elements/displayable/puppet";
|
|
27
28
|
export type { PuppetBackend, PuppetDescription, PuppetInstance, PuppetMountContext, PuppetSize, PuppetState, PuppetStatus, } from "../game/puppet/puppetBackend";
|
|
28
29
|
export type { LayeredDefinition, LayerGroupDefinition, LayerResolver, LayerSlot, LayerTagsOf, LayerVariants, } from "../elements/displayable/image";
|
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
import { Actionable } from "../action/actionable";
|
|
2
2
|
import { Chained, Proxied } from "../action/chain";
|
|
3
3
|
import { LogicAction } from "../game";
|
|
4
|
+
import type { TransformDefinitions } from "../elements/transform/type";
|
|
4
5
|
export type VideoConfig = {
|
|
5
6
|
src: string;
|
|
6
7
|
muted: boolean;
|
|
7
8
|
};
|
|
9
|
+
/**
|
|
10
|
+
* How {@link Video.show} or {@link Video.hide} fades the clip in or out.
|
|
11
|
+
*
|
|
12
|
+
* Omitted, or with no positive `duration`, the clip appears or disappears at once - which is what
|
|
13
|
+
* `show()` and `hide()` have always done.
|
|
14
|
+
*/
|
|
15
|
+
export type VideoFadeOptions = {
|
|
16
|
+
/** How long the fade takes, in milliseconds. */
|
|
17
|
+
duration?: number;
|
|
18
|
+
/**
|
|
19
|
+
* The curve of the fade: a named easing (`"linear"`, `"easeIn"`, `"easeOut"`, `"easeInOut"`) or a
|
|
20
|
+
* cubic-bezier as four numbers. Linear when omitted. A named easing with no CSS equivalent falls
|
|
21
|
+
* back to `"ease"`, as it does for a `Vfx` fade.
|
|
22
|
+
*/
|
|
23
|
+
easing?: TransformDefinitions.EasingDefinition;
|
|
24
|
+
};
|
|
8
25
|
export type VideoState = {
|
|
9
26
|
display: boolean;
|
|
10
27
|
};
|
|
@@ -33,15 +50,38 @@ export declare class Video extends Actionable<VideoStateRaw> {
|
|
|
33
50
|
*/
|
|
34
51
|
preload(): Proxied<Video, Chained<LogicAction.Actions>>;
|
|
35
52
|
/**
|
|
36
|
-
* Show the video element.
|
|
53
|
+
* Show the video element, putting it on the stage first if it is not there yet.
|
|
54
|
+
*
|
|
55
|
+
* With `options.duration`, the clip fades in over that many milliseconds and the action waits for
|
|
56
|
+
* the fade to finish; it starts once the clip can present a frame, so what fades in is the picture
|
|
57
|
+
* and not an empty rectangle. Without it the clip appears at once.
|
|
37
58
|
* @chainable
|
|
59
|
+
* @example
|
|
60
|
+
* ```ts
|
|
61
|
+
* video.show({duration: 500});
|
|
62
|
+
* ```
|
|
38
63
|
*/
|
|
39
|
-
show(): Proxied<Video, Chained<LogicAction.Actions>>;
|
|
64
|
+
show(options?: VideoFadeOptions): Proxied<Video, Chained<LogicAction.Actions>>;
|
|
40
65
|
/**
|
|
41
|
-
* Hide the video element.
|
|
66
|
+
* Hide the video element and take it off the stage.
|
|
67
|
+
*
|
|
68
|
+
* With `options.duration`, the clip fades out over that many milliseconds - holding whatever frame
|
|
69
|
+
* it is on, which after {@link play} is its last - and leaves the stage once the fade is over; the
|
|
70
|
+
* action waits for that. Without it the clip disappears at once.
|
|
71
|
+
*
|
|
72
|
+
* A clip that is not on the stage has nothing to hide, and the call does nothing.
|
|
42
73
|
* @chainable
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* // A cutscene that clears itself away when it ends
|
|
77
|
+
* scene.action([
|
|
78
|
+
* video.show(),
|
|
79
|
+
* video.play(),
|
|
80
|
+
* video.hide({duration: 600}),
|
|
81
|
+
* ]);
|
|
82
|
+
* ```
|
|
43
83
|
*/
|
|
44
|
-
hide(): Proxied<Video, Chained<LogicAction.Actions>>;
|
|
84
|
+
hide(options?: VideoFadeOptions): Proxied<Video, Chained<LogicAction.Actions>>;
|
|
45
85
|
/**
|
|
46
86
|
* Play the video and wait until it finishes.
|
|
47
87
|
* @chainable
|
|
@@ -1,10 +1,76 @@
|
|
|
1
1
|
import { Image as GameImage } from "../../../nlcore/elements/displayable/image";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { GameState } from "../../gameState";
|
|
4
|
+
import { ElementProp, TransitionTask } from "../../../nlcore/elements/transition/type";
|
|
4
5
|
export type ImageEvents = {
|
|
5
6
|
"event:image.onLoad": [];
|
|
6
7
|
};
|
|
8
|
+
/** A laid-out size, in CSS pixels. */
|
|
9
|
+
export type GroupSize = {
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Where a transition puts a group that does not size the box: on the very pixels it will cover
|
|
15
|
+
* once it is the image at rest. `null` means exactly where a picture at rest sits.
|
|
16
|
+
*
|
|
17
|
+
* The box takes the sizing group's size, and the wrapper around it is placed by a layout anchor (a
|
|
18
|
+
* percentage of the stage, measured from the top - or from the bottom on an inverted axis) plus a
|
|
19
|
+
* `translate` of half its own size. The stage is scaled by a non-integer factor, so these lengths
|
|
20
|
+
* fall between device pixels, and the browser snaps layout positions to the pixel grid while
|
|
21
|
+
* leaving translations where they are. A group of the box's size therefore sits in it exactly as
|
|
22
|
+
* an image at rest does - by layout, on its corner, with no transform of its own - and comes out
|
|
23
|
+
* on the same pixels. That is the common case (two backgrounds of one size, a picture fading into
|
|
24
|
+
* itself, a character's expressions), and adding anything to it, even a translate of nothing, can
|
|
25
|
+
* change how a scaled wrapper's content is sampled.
|
|
26
|
+
*
|
|
27
|
+
* A group of another size has to make up the difference inside the box, and how it does so decides
|
|
28
|
+
* what the browser draws. Centred by layout (`top: 50%`, a flex box, auto margins), or by a
|
|
29
|
+
* transform alone, it lands within a pixel of its resting place but on the other side of a
|
|
30
|
+
* rounding; it is drawn resampled at a slightly different offset, and when the transition ends and
|
|
31
|
+
* the box takes its size, the whole picture steps by a fraction of a pixel and changes sharpness.
|
|
32
|
+
* So the difference is split the way the wrapper will split it at rest. On an axis anchored at the
|
|
33
|
+
* top or left the wrapper's layout position does not depend on its size: the group sits on that
|
|
34
|
+
* edge of the box and a translate moves it by half the size difference - the half the wrapper's own
|
|
35
|
+
* translate stops adding once the box has the group's size. On an inverted axis the wrapper is
|
|
36
|
+
* anchored at its bottom or right edge, so its layout position moves with its size: the group sits
|
|
37
|
+
* on that edge instead, which puts its layout position where the wrapper's will be, and the
|
|
38
|
+
* translate takes the other half back. Every length that gets snapped is then one that will be
|
|
39
|
+
* snapped at rest, and the unsnapped ones add up to the translate the wrapper will carry.
|
|
40
|
+
*
|
|
41
|
+
* Under a wrapper that also scales or rotates, the browser drops the fraction of the wrapper's own
|
|
42
|
+
* layout position before transforming, so on an inverted axis a group of another size can still be
|
|
43
|
+
* sampled a little differently from the way it will be at rest; it is never displaced.
|
|
44
|
+
*
|
|
45
|
+
* Transitions that move a group write the independent `translate` property, which composes with
|
|
46
|
+
* this.
|
|
47
|
+
*/
|
|
48
|
+
export declare function transitionGroupPlacement(box: GroupSize, own: GroupSize, invertX: boolean, invertY: boolean): React.CSSProperties | null;
|
|
7
49
|
export declare function stackStyle(darkness: number): React.CSSProperties;
|
|
50
|
+
/**
|
|
51
|
+
* The props each group of a running transition sits on, index-aligned with the transition's
|
|
52
|
+
* resolvers; a resolver's own frame is merged over them.
|
|
53
|
+
*
|
|
54
|
+
* The group that sizes the box stays exactly as it is at rest, and every other group the transition
|
|
55
|
+
* shows sits where it will rest once it sizes the box itself (see `transitionGroupPlacement`), so
|
|
56
|
+
* neither moves, nor changes how it is sampled, when the transition starts or when it ends. A
|
|
57
|
+
* group whose size is not known yet has not loaded and shows nothing; it is placed as at rest until
|
|
58
|
+
* it has. A keyed resolver drives a picture - a stack wrapper on the layered path - and an unkeyed
|
|
59
|
+
* one drives an element of the transition's own (ThroughColor's colour frame), centred over the box
|
|
60
|
+
* and gone once the transition ends.
|
|
61
|
+
*
|
|
62
|
+
* None of these names a `src`: each side's resolver carries its own. A base that named one would
|
|
63
|
+
* point the outgoing picture somewhere else for the moment between the two writes, which is long
|
|
64
|
+
* enough for the browser to drop the decoded picture and paint a frame without it.
|
|
65
|
+
*/
|
|
66
|
+
export declare function transitionGroupProps(resolve: TransitionTask<HTMLImageElement, any>["resolve"], options: {
|
|
67
|
+
layered: boolean;
|
|
68
|
+
darkness: number;
|
|
69
|
+
invertX: boolean;
|
|
70
|
+
invertY: boolean;
|
|
71
|
+
/** The laid-out size of each group, index-aligned with `resolve`, where it is known. */
|
|
72
|
+
sizes: (GroupSize | undefined)[];
|
|
73
|
+
}): ElementProp<HTMLImageElement>[];
|
|
8
74
|
declare function ImageComponent({ image, state, }: Readonly<{
|
|
9
75
|
image: GameImage;
|
|
10
76
|
state: GameState;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -11,7 +11,7 @@ import { Text } from "../nlcore/elements/displayable/text";
|
|
|
11
11
|
import { Displayable } from "../nlcore/elements/displayable/displayable";
|
|
12
12
|
import { Scene } from "../nlcore/elements/scene";
|
|
13
13
|
import { Sound } from "../nlcore/elements/sound";
|
|
14
|
-
import { Video } from "../nlcore/elements/video";
|
|
14
|
+
import { Video, VideoFadeOptions } from "../nlcore/elements/video";
|
|
15
15
|
import { Vfx, VfxFadeOptions } from "../nlcore/elements/vfx";
|
|
16
16
|
import { Puppet } from "../nlcore/elements/displayable/puppet";
|
|
17
17
|
import { Timeline } from "./Tasks";
|
|
@@ -80,8 +80,12 @@ export type ExposedState = {
|
|
|
80
80
|
setBackgroundMusic: (music: Sound | null, fade: number) => Promise<void>;
|
|
81
81
|
};
|
|
82
82
|
[ExposedStateType.video]: {
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
/** Resolves once the clip is showing: at once, or when a fade in ends, is skipped or is abandoned. */
|
|
84
|
+
show: (options?: VideoFadeOptions) => Promise<void>;
|
|
85
|
+
/** Resolves once the clip is hidden: at once, or when a fade out ends, is skipped or is abandoned. */
|
|
86
|
+
hide: (options?: VideoFadeOptions) => Promise<void>;
|
|
87
|
+
/** Abandon a fade in flight and show the clip as `video.state.display` says. */
|
|
88
|
+
cancelFade: () => void;
|
|
85
89
|
play: () => Promise<void>;
|
|
86
90
|
pause: () => void;
|
|
87
91
|
resume: () => Promise<void>;
|