reze-engine 0.3.11 → 0.3.13
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/bezier-interpolate.d.ts +15 -0
- package/dist/bezier-interpolate.d.ts.map +1 -0
- package/dist/bezier-interpolate.js +40 -0
- package/dist/engine.d.ts +3 -8
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +23 -34
- package/dist/ik.d.ts +32 -0
- package/dist/ik.d.ts.map +1 -0
- package/dist/ik.js +337 -0
- package/dist/pool-scene.d.ts +52 -0
- package/dist/pool-scene.d.ts.map +1 -0
- package/dist/pool-scene.js +1122 -0
- package/dist/pool.d.ts +38 -0
- package/dist/pool.d.ts.map +1 -0
- package/dist/pool.js +422 -0
- package/dist/rzm-converter.d.ts +12 -0
- package/dist/rzm-converter.d.ts.map +1 -0
- package/dist/rzm-converter.js +40 -0
- package/dist/rzm-loader.d.ts +24 -0
- package/dist/rzm-loader.d.ts.map +1 -0
- package/dist/rzm-loader.js +488 -0
- package/dist/rzm-writer.d.ts +27 -0
- package/dist/rzm-writer.d.ts.map +1 -0
- package/dist/rzm-writer.js +701 -0
- package/package.json +1 -1
- package/src/engine.ts +32 -32
- package/src/player.ts +0 -290
package/package.json
CHANGED
package/src/engine.ts
CHANGED
|
@@ -9,6 +9,16 @@ export type EngineOptions = {
|
|
|
9
9
|
rimLightIntensity?: number
|
|
10
10
|
cameraDistance?: number
|
|
11
11
|
cameraTarget?: Vec3
|
|
12
|
+
cameraFov?: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const DEFAULT_ENGINE_OPTIONS: Required<EngineOptions> = {
|
|
16
|
+
ambientColor: new Vec3(1.0, 1.0, 1.0),
|
|
17
|
+
bloomIntensity: 0.12,
|
|
18
|
+
rimLightIntensity: 0.45,
|
|
19
|
+
cameraDistance: 26.6,
|
|
20
|
+
cameraTarget: new Vec3(0, 12.5, 0),
|
|
21
|
+
cameraFov: Math.PI / 4,
|
|
12
22
|
}
|
|
13
23
|
|
|
14
24
|
export interface EngineStats {
|
|
@@ -42,8 +52,9 @@ export class Engine {
|
|
|
42
52
|
private camera!: Camera
|
|
43
53
|
private cameraUniformBuffer!: GPUBuffer
|
|
44
54
|
private cameraMatrixData = new Float32Array(36)
|
|
45
|
-
private cameraDistance
|
|
46
|
-
private cameraTarget
|
|
55
|
+
private cameraDistance!: number
|
|
56
|
+
private cameraTarget!: Vec3
|
|
57
|
+
private cameraFov!: number
|
|
47
58
|
private lightUniformBuffer!: GPUBuffer
|
|
48
59
|
private lightData = new Float32Array(4)
|
|
49
60
|
private vertexBuffer!: GPUBuffer
|
|
@@ -72,18 +83,8 @@ export class Engine {
|
|
|
72
83
|
private readonly STENCIL_EYE_VALUE = 1
|
|
73
84
|
private readonly BLOOM_DOWNSCALE_FACTOR = 2
|
|
74
85
|
|
|
75
|
-
// Default values
|
|
76
|
-
private static readonly DEFAULT_BLOOM_THRESHOLD = 0.01
|
|
77
|
-
private static readonly DEFAULT_BLOOM_INTENSITY = 0.12
|
|
78
|
-
private static readonly DEFAULT_RIM_LIGHT_INTENSITY = 0.45
|
|
79
|
-
private static readonly DEFAULT_CAMERA_DISTANCE = 26.6
|
|
80
|
-
private static readonly DEFAULT_CAMERA_TARGET = new Vec3(0, 12.5, 0)
|
|
81
|
-
private static readonly TRANSPARENCY_EPSILON = 0.001
|
|
82
|
-
private static readonly STATS_FPS_UPDATE_INTERVAL_MS = 1000
|
|
83
|
-
private static readonly STATS_FRAME_TIME_ROUNDING = 100
|
|
84
|
-
|
|
85
86
|
// Ambient light settings
|
|
86
|
-
private ambientColor
|
|
87
|
+
private ambientColor!: Vec3
|
|
87
88
|
// Bloom post-processing textures
|
|
88
89
|
private sceneRenderTexture!: GPUTexture
|
|
89
90
|
private sceneRenderTextureView!: GPUTextureView // Cached view (recreated on resize)
|
|
@@ -104,10 +105,10 @@ export class Engine {
|
|
|
104
105
|
private bloomBlurVBindGroup?: GPUBindGroup
|
|
105
106
|
private bloomComposeBindGroup?: GPUBindGroup
|
|
106
107
|
// Bloom settings
|
|
107
|
-
private bloomThreshold
|
|
108
|
-
private bloomIntensity
|
|
108
|
+
private bloomThreshold!: number
|
|
109
|
+
private bloomIntensity!: number
|
|
109
110
|
// Rim light settings
|
|
110
|
-
private rimLightIntensity
|
|
111
|
+
private rimLightIntensity!: number
|
|
111
112
|
|
|
112
113
|
private currentModel: Model | null = null
|
|
113
114
|
private modelDir: string = ""
|
|
@@ -132,11 +133,12 @@ export class Engine {
|
|
|
132
133
|
constructor(canvas: HTMLCanvasElement, options?: EngineOptions) {
|
|
133
134
|
this.canvas = canvas
|
|
134
135
|
if (options) {
|
|
135
|
-
this.ambientColor = options.ambientColor ??
|
|
136
|
-
this.bloomIntensity = options.bloomIntensity ??
|
|
137
|
-
this.rimLightIntensity = options.rimLightIntensity ??
|
|
138
|
-
this.cameraDistance = options.cameraDistance ??
|
|
139
|
-
this.cameraTarget = options.cameraTarget ??
|
|
136
|
+
this.ambientColor = options.ambientColor ?? DEFAULT_ENGINE_OPTIONS.ambientColor!
|
|
137
|
+
this.bloomIntensity = options.bloomIntensity ?? DEFAULT_ENGINE_OPTIONS.bloomIntensity
|
|
138
|
+
this.rimLightIntensity = options.rimLightIntensity ?? DEFAULT_ENGINE_OPTIONS.rimLightIntensity
|
|
139
|
+
this.cameraDistance = options.cameraDistance ?? DEFAULT_ENGINE_OPTIONS.cameraDistance
|
|
140
|
+
this.cameraTarget = options.cameraTarget ?? DEFAULT_ENGINE_OPTIONS.cameraTarget
|
|
141
|
+
this.cameraFov = options.cameraFov ?? DEFAULT_ENGINE_OPTIONS.cameraFov
|
|
140
142
|
}
|
|
141
143
|
}
|
|
142
144
|
|
|
@@ -366,12 +368,12 @@ export class Engine {
|
|
|
366
368
|
|
|
367
369
|
let lightAccum = light.ambientColor;
|
|
368
370
|
|
|
369
|
-
// Rim light calculation
|
|
371
|
+
// Rim light calculation - proper Fresnel for edge-only highlights
|
|
370
372
|
let viewDir = normalize(camera.viewPos - input.worldPos);
|
|
371
|
-
|
|
372
|
-
rimFactor =
|
|
373
|
+
let fresnel = 1.0 - abs(dot(n, viewDir));
|
|
374
|
+
let rimFactor = pow(fresnel, 4.0); // Higher power for sharper edge-only effect
|
|
373
375
|
let rimLight = material.rimColor * material.rimIntensity * rimFactor;
|
|
374
|
-
|
|
376
|
+
|
|
375
377
|
let color = albedo * lightAccum + rimLight;
|
|
376
378
|
|
|
377
379
|
return vec4f(color, finalAlpha);
|
|
@@ -1052,7 +1054,7 @@ export class Engine {
|
|
|
1052
1054
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
1053
1055
|
})
|
|
1054
1056
|
|
|
1055
|
-
this.camera = new Camera(Math.PI, Math.PI / 2.5, this.cameraDistance, this.cameraTarget)
|
|
1057
|
+
this.camera = new Camera(Math.PI, Math.PI / 2.5, this.cameraDistance, this.cameraTarget, this.cameraFov)
|
|
1056
1058
|
|
|
1057
1059
|
this.camera.aspect = this.canvas.width / this.canvas.height
|
|
1058
1060
|
this.camera.attachControl(this.canvas)
|
|
@@ -1285,7 +1287,7 @@ export class Engine {
|
|
|
1285
1287
|
if (!diffuseTexture) throw new Error(`Material "${mat.name}" has no diffuse texture`)
|
|
1286
1288
|
|
|
1287
1289
|
const materialAlpha = mat.diffuse[3]
|
|
1288
|
-
const isTransparent = materialAlpha < 1.0 -
|
|
1290
|
+
const isTransparent = materialAlpha < 1.0 - 0.001
|
|
1289
1291
|
|
|
1290
1292
|
const materialUniformBuffer = this.createMaterialUniformBuffer(mat.name, materialAlpha, 0.0)
|
|
1291
1293
|
|
|
@@ -1739,17 +1741,15 @@ export class Engine {
|
|
|
1739
1741
|
this.frameTimeSum -= avg
|
|
1740
1742
|
this.frameTimeCount = maxSamples
|
|
1741
1743
|
}
|
|
1742
|
-
this.stats.frameTime =
|
|
1743
|
-
Math.round((this.frameTimeSum / this.frameTimeCount) * Engine.STATS_FRAME_TIME_ROUNDING) /
|
|
1744
|
-
Engine.STATS_FRAME_TIME_ROUNDING
|
|
1744
|
+
this.stats.frameTime = Math.round((this.frameTimeSum / this.frameTimeCount) * 100) / 100
|
|
1745
1745
|
|
|
1746
1746
|
// FPS tracking
|
|
1747
1747
|
const now = performance.now()
|
|
1748
1748
|
this.framesSinceLastUpdate++
|
|
1749
1749
|
const elapsed = now - this.lastFpsUpdate
|
|
1750
1750
|
|
|
1751
|
-
if (elapsed >=
|
|
1752
|
-
this.stats.fps = Math.round((this.framesSinceLastUpdate / elapsed) *
|
|
1751
|
+
if (elapsed >= 1000) {
|
|
1752
|
+
this.stats.fps = Math.round((this.framesSinceLastUpdate / elapsed) * 1000)
|
|
1753
1753
|
this.framesSinceLastUpdate = 0
|
|
1754
1754
|
this.lastFpsUpdate = now
|
|
1755
1755
|
}
|
package/src/player.ts
DELETED
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
import { Quat, Vec3, bezierInterpolate } from "./math"
|
|
2
|
-
import { BoneFrame, MorphFrame, VMDKeyFrame, VMDLoader } from "./vmd-loader"
|
|
3
|
-
|
|
4
|
-
export interface AnimationPose {
|
|
5
|
-
boneRotations: Map<string, Quat>
|
|
6
|
-
boneTranslations: Map<string, Vec3>
|
|
7
|
-
morphWeights: Map<string, number>
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface AnimationProgress {
|
|
11
|
-
current: number
|
|
12
|
-
duration: number
|
|
13
|
-
percentage: number
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export class Player {
|
|
17
|
-
// Animation data
|
|
18
|
-
private frames: VMDKeyFrame[] = []
|
|
19
|
-
private boneTracks: Map<string, Array<{ boneFrame: BoneFrame; time: number }>> = new Map()
|
|
20
|
-
private morphTracks: Map<string, Array<{ morphFrame: MorphFrame; time: number }>> = new Map()
|
|
21
|
-
private _duration: number = 0
|
|
22
|
-
|
|
23
|
-
// Playback state
|
|
24
|
-
private isPlaying: boolean = false
|
|
25
|
-
private isPaused: boolean = false
|
|
26
|
-
private _currentTime: number = 0
|
|
27
|
-
|
|
28
|
-
// Timing
|
|
29
|
-
private startTime: number = 0 // Real-time when playback started
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Load VMD animation file
|
|
33
|
-
*/
|
|
34
|
-
async loadVmd(vmdUrl: string): Promise<void> {
|
|
35
|
-
// Load animation
|
|
36
|
-
this.frames = await VMDLoader.load(vmdUrl)
|
|
37
|
-
this.processFrames()
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Process frames into tracks
|
|
42
|
-
*/
|
|
43
|
-
private processFrames(): void {
|
|
44
|
-
// Helper to group frames by name and sort by time
|
|
45
|
-
const groupFrames = <T>(
|
|
46
|
-
items: Array<{ item: T; name: string; time: number }>
|
|
47
|
-
): Map<string, Array<{ item: T; time: number }>> => {
|
|
48
|
-
const tracks = new Map<string, Array<{ item: T; time: number }>>()
|
|
49
|
-
for (const { item, name, time } of items) {
|
|
50
|
-
if (!tracks.has(name)) tracks.set(name, [])
|
|
51
|
-
tracks.get(name)!.push({ item, time })
|
|
52
|
-
}
|
|
53
|
-
for (const keyFrames of tracks.values()) {
|
|
54
|
-
keyFrames.sort((a, b) => a.time - b.time)
|
|
55
|
-
}
|
|
56
|
-
return tracks
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Collect all bone and morph frames
|
|
60
|
-
const boneItems: Array<{ item: BoneFrame; name: string; time: number }> = []
|
|
61
|
-
const morphItems: Array<{ item: MorphFrame; name: string; time: number }> = []
|
|
62
|
-
|
|
63
|
-
for (const keyFrame of this.frames) {
|
|
64
|
-
for (const boneFrame of keyFrame.boneFrames) {
|
|
65
|
-
boneItems.push({ item: boneFrame, name: boneFrame.boneName, time: keyFrame.time })
|
|
66
|
-
}
|
|
67
|
-
for (const morphFrame of keyFrame.morphFrames) {
|
|
68
|
-
morphItems.push({ item: morphFrame, name: morphFrame.morphName, time: keyFrame.time })
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Transform to expected format
|
|
73
|
-
this.boneTracks = new Map()
|
|
74
|
-
for (const [name, frames] of groupFrames(boneItems).entries()) {
|
|
75
|
-
this.boneTracks.set(
|
|
76
|
-
name,
|
|
77
|
-
frames.map((f) => ({ boneFrame: f.item, time: f.time }))
|
|
78
|
-
)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
this.morphTracks = new Map()
|
|
82
|
-
for (const [name, frames] of groupFrames(morphItems).entries()) {
|
|
83
|
-
this.morphTracks.set(
|
|
84
|
-
name,
|
|
85
|
-
frames.map((f) => ({ morphFrame: f.item, time: f.time }))
|
|
86
|
-
)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Calculate duration from all tracks
|
|
90
|
-
const allTracks = [...this.boneTracks.values(), ...this.morphTracks.values()]
|
|
91
|
-
this._duration = allTracks.reduce((max, keyFrames) => {
|
|
92
|
-
const lastTime = keyFrames[keyFrames.length - 1]?.time ?? 0
|
|
93
|
-
return Math.max(max, lastTime)
|
|
94
|
-
}, 0)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Start or resume playback
|
|
99
|
-
* Note: For iOS, this should be called synchronously from a user interaction event
|
|
100
|
-
*/
|
|
101
|
-
play(): void {
|
|
102
|
-
if (this.frames.length === 0) return
|
|
103
|
-
|
|
104
|
-
this.isPaused = false
|
|
105
|
-
this.startTime = performance.now() - this._currentTime * 1000
|
|
106
|
-
|
|
107
|
-
this.isPlaying = true
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Pause playback
|
|
112
|
-
*/
|
|
113
|
-
pause(): void {
|
|
114
|
-
if (!this.isPlaying || this.isPaused) return
|
|
115
|
-
this.isPaused = true
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Stop playback and reset to beginning
|
|
120
|
-
*/
|
|
121
|
-
stop(): void {
|
|
122
|
-
this.isPlaying = false
|
|
123
|
-
this.isPaused = false
|
|
124
|
-
this._currentTime = 0
|
|
125
|
-
this.startTime = 0
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Seek to specific time
|
|
130
|
-
*/
|
|
131
|
-
seek(time: number): void {
|
|
132
|
-
const clampedTime = Math.max(0, Math.min(time, this._duration))
|
|
133
|
-
this._currentTime = clampedTime
|
|
134
|
-
|
|
135
|
-
if (this.isPlaying && !this.isPaused) {
|
|
136
|
-
this.startTime = performance.now() - clampedTime * 1000
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Update playback and return current pose
|
|
142
|
-
* Returns null if not playing, but returns current pose if paused
|
|
143
|
-
*/
|
|
144
|
-
update(currentRealTime: number): AnimationPose | null {
|
|
145
|
-
if (!this.isPlaying || this.frames.length === 0) {
|
|
146
|
-
return null
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// If paused, return current pose at paused time (no time update)
|
|
150
|
-
if (this.isPaused) {
|
|
151
|
-
return this.getPoseAtTime(this._currentTime)
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Calculate current animation time
|
|
155
|
-
const elapsedSeconds = (currentRealTime - this.startTime) / 1000
|
|
156
|
-
this._currentTime = elapsedSeconds
|
|
157
|
-
|
|
158
|
-
// Check if animation ended
|
|
159
|
-
if (this._currentTime >= this._duration) {
|
|
160
|
-
this._currentTime = this._duration
|
|
161
|
-
this.pause() // Auto-pause at end
|
|
162
|
-
return this.getPoseAtTime(this._currentTime)
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return this.getPoseAtTime(this._currentTime)
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Get pose at specific time (pure function)
|
|
170
|
-
*/
|
|
171
|
-
getPoseAtTime(time: number): AnimationPose {
|
|
172
|
-
const pose: AnimationPose = {
|
|
173
|
-
boneRotations: new Map(),
|
|
174
|
-
boneTranslations: new Map(),
|
|
175
|
-
morphWeights: new Map(),
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Generic binary search for upper bound
|
|
179
|
-
const upperBound = <T extends { time: number }>(time: number, keyFrames: T[]): number => {
|
|
180
|
-
let left = 0,
|
|
181
|
-
right = keyFrames.length
|
|
182
|
-
while (left < right) {
|
|
183
|
-
const mid = Math.floor((left + right) / 2)
|
|
184
|
-
if (keyFrames[mid].time <= time) left = mid + 1
|
|
185
|
-
else right = mid
|
|
186
|
-
}
|
|
187
|
-
return left
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Process bone tracks
|
|
191
|
-
for (const [boneName, keyFrames] of this.boneTracks.entries()) {
|
|
192
|
-
if (keyFrames.length === 0) continue
|
|
193
|
-
|
|
194
|
-
const clampedTime = Math.max(keyFrames[0].time, Math.min(keyFrames[keyFrames.length - 1].time, time))
|
|
195
|
-
const idx = upperBound(clampedTime, keyFrames) - 1
|
|
196
|
-
if (idx < 0) continue
|
|
197
|
-
|
|
198
|
-
const frameA = keyFrames[idx].boneFrame
|
|
199
|
-
const frameB = keyFrames[idx + 1]?.boneFrame
|
|
200
|
-
|
|
201
|
-
if (!frameB) {
|
|
202
|
-
pose.boneRotations.set(boneName, frameA.rotation)
|
|
203
|
-
pose.boneTranslations.set(boneName, frameA.translation)
|
|
204
|
-
} else {
|
|
205
|
-
const timeA = keyFrames[idx].time
|
|
206
|
-
const timeB = keyFrames[idx + 1].time
|
|
207
|
-
const gradient = (clampedTime - timeA) / (timeB - timeA)
|
|
208
|
-
const interp = frameB.interpolation
|
|
209
|
-
|
|
210
|
-
pose.boneRotations.set(
|
|
211
|
-
boneName,
|
|
212
|
-
Quat.slerp(
|
|
213
|
-
frameA.rotation,
|
|
214
|
-
frameB.rotation,
|
|
215
|
-
bezierInterpolate(interp[0] / 127, interp[1] / 127, interp[2] / 127, interp[3] / 127, gradient)
|
|
216
|
-
)
|
|
217
|
-
)
|
|
218
|
-
|
|
219
|
-
const lerp = (a: number, b: number, w: number) => a + (b - a) * w
|
|
220
|
-
const getWeight = (offset: number) =>
|
|
221
|
-
bezierInterpolate(
|
|
222
|
-
interp[offset] / 127,
|
|
223
|
-
interp[offset + 8] / 127,
|
|
224
|
-
interp[offset + 4] / 127,
|
|
225
|
-
interp[offset + 12] / 127,
|
|
226
|
-
gradient
|
|
227
|
-
)
|
|
228
|
-
|
|
229
|
-
pose.boneTranslations.set(
|
|
230
|
-
boneName,
|
|
231
|
-
new Vec3(
|
|
232
|
-
lerp(frameA.translation.x, frameB.translation.x, getWeight(0)),
|
|
233
|
-
lerp(frameA.translation.y, frameB.translation.y, getWeight(16)),
|
|
234
|
-
lerp(frameA.translation.z, frameB.translation.z, getWeight(32))
|
|
235
|
-
)
|
|
236
|
-
)
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Process morph tracks
|
|
241
|
-
for (const [morphName, keyFrames] of this.morphTracks.entries()) {
|
|
242
|
-
if (keyFrames.length === 0) continue
|
|
243
|
-
|
|
244
|
-
const clampedTime = Math.max(keyFrames[0].time, Math.min(keyFrames[keyFrames.length - 1].time, time))
|
|
245
|
-
const idx = upperBound(clampedTime, keyFrames) - 1
|
|
246
|
-
if (idx < 0) continue
|
|
247
|
-
|
|
248
|
-
const frameA = keyFrames[idx].morphFrame
|
|
249
|
-
const frameB = keyFrames[idx + 1]?.morphFrame
|
|
250
|
-
|
|
251
|
-
if (!frameB) {
|
|
252
|
-
pose.morphWeights.set(morphName, frameA.weight)
|
|
253
|
-
} else {
|
|
254
|
-
const timeA = keyFrames[idx].time
|
|
255
|
-
const timeB = keyFrames[idx + 1].time
|
|
256
|
-
const gradient = (clampedTime - timeA) / (timeB - timeA)
|
|
257
|
-
pose.morphWeights.set(morphName, frameA.weight + (frameB.weight - frameA.weight) * gradient)
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
return pose
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Get current playback progress
|
|
266
|
-
*/
|
|
267
|
-
getProgress(): AnimationProgress {
|
|
268
|
-
return {
|
|
269
|
-
current: this._currentTime,
|
|
270
|
-
duration: this._duration,
|
|
271
|
-
percentage: this._duration > 0 ? (this._currentTime / this._duration) * 100 : 0,
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
get currentTime(): number {
|
|
276
|
-
return this._currentTime
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
get duration(): number {
|
|
280
|
-
return this._duration
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
get isPlayingState(): boolean {
|
|
284
|
-
return this.isPlaying && !this.isPaused
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
get isPausedState(): boolean {
|
|
288
|
-
return this.isPaused
|
|
289
|
-
}
|
|
290
|
-
}
|