@routevn/creator-model 1.13.3 → 1.14.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/README.md +16 -0
- package/package.json +1 -1
- package/src/model.js +112 -1
package/README.md
CHANGED
|
@@ -106,6 +106,22 @@ Design rules:
|
|
|
106
106
|
- random ids across RouteVN should use `nanoid` with the RouteVN base58 variant;
|
|
107
107
|
deterministic derived tokens such as partition hashes are a separate case
|
|
108
108
|
|
|
109
|
+
## Animation Camera Tracks
|
|
110
|
+
|
|
111
|
+
Animation resources may explicitly group synchronized position and scale tracks
|
|
112
|
+
with `cameraTracks: ["update"]`, or `["prev", "next"]` for transitions.
|
|
113
|
+
Each listed side must contain `x`, `y`, `scaleX`, and `scaleY` keyframe
|
|
114
|
+
tracks with matching timing/easing and start-value presence, absolute values,
|
|
115
|
+
and positive scales. Track initial values are optional; they must be numeric
|
|
116
|
+
on all four tracks or omitted from all four to use the target's existing pose.
|
|
117
|
+
Translation tracks cannot coexist with Camera on the same side.
|
|
118
|
+
|
|
119
|
+
This authoring marker does not alter the runtime animation format. Existing
|
|
120
|
+
animations without the marker remain separate tracks; no grouping is inferred.
|
|
121
|
+
An update may use `cameraTracks: []` to remove grouping. Updates to animation
|
|
122
|
+
data are validated with the resource's retained grouping unless it is replaced
|
|
123
|
+
in the same command.
|
|
124
|
+
|
|
109
125
|
## Font Weight Metadata
|
|
110
126
|
|
|
111
127
|
Font resources may store extracted weight capabilities in three flat fields:
|
package/package.json
CHANGED
package/src/model.js
CHANGED
|
@@ -408,7 +408,7 @@ const SAVE_LOAD_DATE_FORMATS = new Set([
|
|
|
408
408
|
"DD MMM YYYY",
|
|
409
409
|
"YYYY年MM月DD日",
|
|
410
410
|
]);
|
|
411
|
-
export const SCHEMA_VERSION =
|
|
411
|
+
export const SCHEMA_VERSION = 14;
|
|
412
412
|
const LAYOUT_CONTAINER_ELEMENT_TYPES = [
|
|
413
413
|
"folder",
|
|
414
414
|
"container",
|
|
@@ -2345,6 +2345,85 @@ const validateAnimationDefinition = ({ animation, path, errorFactory }) => {
|
|
|
2345
2345
|
}
|
|
2346
2346
|
};
|
|
2347
2347
|
|
|
2348
|
+
// Camera is an authored grouping of four synchronized runtime tween tracks.
|
|
2349
|
+
// No grouping is inferred for older animations that omit cameraTracks.
|
|
2350
|
+
const validateCameraTracks = ({ value, animation, path, errorFactory }) => {
|
|
2351
|
+
if (value === undefined) return;
|
|
2352
|
+
if (
|
|
2353
|
+
!Array.isArray(value) ||
|
|
2354
|
+
value.some((side) => !["update", "prev", "next"].includes(side)) ||
|
|
2355
|
+
new Set(value).size !== value.length
|
|
2356
|
+
) {
|
|
2357
|
+
return invalidFromErrorFactory(
|
|
2358
|
+
errorFactory,
|
|
2359
|
+
`${path} must be an array of unique update, prev, or next sides`,
|
|
2360
|
+
);
|
|
2361
|
+
}
|
|
2362
|
+
// Partial update payloads are checked against the merged resource below.
|
|
2363
|
+
if (animation === undefined) return;
|
|
2364
|
+
for (const side of value) {
|
|
2365
|
+
const tween = side === "update" ? animation.tween : animation[side]?.tween;
|
|
2366
|
+
const validSide =
|
|
2367
|
+
animation.type === "update" ? side === "update" : side !== "update";
|
|
2368
|
+
const properties = ["x", "y", "scaleX", "scaleY"];
|
|
2369
|
+
const tracks = properties.map((property) => tween?.[property]);
|
|
2370
|
+
if (
|
|
2371
|
+
!validSide ||
|
|
2372
|
+
tracks.some(
|
|
2373
|
+
(track) =>
|
|
2374
|
+
!Array.isArray(track?.keyframes) ||
|
|
2375
|
+
(track.initialValue !== undefined &&
|
|
2376
|
+
!Number.isFinite(track.initialValue)) ||
|
|
2377
|
+
(track.initialValue !== undefined) !==
|
|
2378
|
+
(tracks[0]?.initialValue !== undefined),
|
|
2379
|
+
) ||
|
|
2380
|
+
tween.translateX !== undefined ||
|
|
2381
|
+
tween.translateY !== undefined
|
|
2382
|
+
) {
|
|
2383
|
+
return invalidFromErrorFactory(
|
|
2384
|
+
errorFactory,
|
|
2385
|
+
`${path}.${side} requires x, y, scaleX, and scaleY keyframe tracks with matching initial-value presence and no translation tracks`,
|
|
2386
|
+
);
|
|
2387
|
+
}
|
|
2388
|
+
const reference = tracks[0].keyframes;
|
|
2389
|
+
for (const [index, track] of tracks.entries()) {
|
|
2390
|
+
if (
|
|
2391
|
+
track.keyframes.length !== reference.length ||
|
|
2392
|
+
track.keyframes.some((frame, frameIndex) => {
|
|
2393
|
+
const other = reference[frameIndex];
|
|
2394
|
+
return (
|
|
2395
|
+
frame.relative === true ||
|
|
2396
|
+
frame.duration !== other.duration ||
|
|
2397
|
+
(frame.delay ?? 0) !== (other.delay ?? 0) ||
|
|
2398
|
+
(frame.easing ?? "linear") !== (other.easing ?? "linear") ||
|
|
2399
|
+
(frame.startValue !== undefined) !==
|
|
2400
|
+
(other.startValue !== undefined)
|
|
2401
|
+
);
|
|
2402
|
+
})
|
|
2403
|
+
) {
|
|
2404
|
+
return invalidFromErrorFactory(
|
|
2405
|
+
errorFactory,
|
|
2406
|
+
`${path}.${side} requires synchronized absolute keyframes`,
|
|
2407
|
+
);
|
|
2408
|
+
}
|
|
2409
|
+
if (
|
|
2410
|
+
index >= 2 &&
|
|
2411
|
+
((track.initialValue !== undefined && track.initialValue <= 0) ||
|
|
2412
|
+
track.keyframes.some(
|
|
2413
|
+
(frame) =>
|
|
2414
|
+
frame.value <= 0 ||
|
|
2415
|
+
(frame.startValue !== undefined && frame.startValue <= 0),
|
|
2416
|
+
))
|
|
2417
|
+
) {
|
|
2418
|
+
return invalidFromErrorFactory(
|
|
2419
|
+
errorFactory,
|
|
2420
|
+
`${path}.${side} requires positive camera scales`,
|
|
2421
|
+
);
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
};
|
|
2426
|
+
|
|
2348
2427
|
const validateAnimationItems = ({ items, path, errorFactory }) => {
|
|
2349
2428
|
for (const [itemId, item] of Object.entries(items)) {
|
|
2350
2429
|
const itemPath = `${path}.${itemId}`;
|
|
@@ -2370,6 +2449,7 @@ const validateAnimationItems = ({ items, path, errorFactory }) => {
|
|
|
2370
2449
|
"tagIds",
|
|
2371
2450
|
"thumbnailFileId",
|
|
2372
2451
|
"preview",
|
|
2452
|
+
"cameraTracks",
|
|
2373
2453
|
"animation",
|
|
2374
2454
|
],
|
|
2375
2455
|
path: itemPath,
|
|
@@ -2452,6 +2532,13 @@ const validateAnimationItems = ({ items, path, errorFactory }) => {
|
|
|
2452
2532
|
return result;
|
|
2453
2533
|
}
|
|
2454
2534
|
}
|
|
2535
|
+
const cameraResult = validateCameraTracks({
|
|
2536
|
+
value: item.cameraTracks,
|
|
2537
|
+
animation: item.animation,
|
|
2538
|
+
path: `${itemPath}.cameraTracks`,
|
|
2539
|
+
errorFactory,
|
|
2540
|
+
});
|
|
2541
|
+
if (cameraResult?.valid === false) return cameraResult;
|
|
2455
2542
|
}
|
|
2456
2543
|
}
|
|
2457
2544
|
};
|
|
@@ -12260,6 +12347,7 @@ const validateAnimationCreateData = ({ data, errorFactory }) => {
|
|
|
12260
12347
|
"tagIds",
|
|
12261
12348
|
"thumbnailFileId",
|
|
12262
12349
|
"preview",
|
|
12350
|
+
"cameraTracks",
|
|
12263
12351
|
"animation",
|
|
12264
12352
|
],
|
|
12265
12353
|
path: "payload.data",
|
|
@@ -12327,6 +12415,12 @@ const validateAnimationCreateData = ({ data, errorFactory }) => {
|
|
|
12327
12415
|
return result;
|
|
12328
12416
|
}
|
|
12329
12417
|
}
|
|
12418
|
+
return validateCameraTracks({
|
|
12419
|
+
value: data.cameraTracks,
|
|
12420
|
+
animation: data.animation,
|
|
12421
|
+
path: "payload.data.cameraTracks",
|
|
12422
|
+
errorFactory,
|
|
12423
|
+
});
|
|
12330
12424
|
}
|
|
12331
12425
|
};
|
|
12332
12426
|
|
|
@@ -12340,6 +12434,7 @@ const validateAnimationUpdateData = ({ data, errorFactory }) => {
|
|
|
12340
12434
|
"tagIds",
|
|
12341
12435
|
"thumbnailFileId",
|
|
12342
12436
|
"preview",
|
|
12437
|
+
"cameraTracks",
|
|
12343
12438
|
"animation",
|
|
12344
12439
|
],
|
|
12345
12440
|
path: "payload.data",
|
|
@@ -12415,6 +12510,12 @@ const validateAnimationUpdateData = ({ data, errorFactory }) => {
|
|
|
12415
12510
|
}
|
|
12416
12511
|
}
|
|
12417
12512
|
}
|
|
12513
|
+
return validateCameraTracks({
|
|
12514
|
+
value: data.cameraTracks,
|
|
12515
|
+
animation: data.animation,
|
|
12516
|
+
path: "payload.data.cameraTracks",
|
|
12517
|
+
errorFactory,
|
|
12518
|
+
});
|
|
12418
12519
|
};
|
|
12419
12520
|
|
|
12420
12521
|
const validateAudioEffectCreateData = ({ data, errorFactory }) => {
|
|
@@ -19066,6 +19167,9 @@ const COMMAND_DEFINITIONS = [
|
|
|
19066
19167
|
if (payload.data.preview !== undefined) {
|
|
19067
19168
|
nextAnimation.preview = structuredClone(payload.data.preview);
|
|
19068
19169
|
}
|
|
19170
|
+
if (payload.data.cameraTracks !== undefined) {
|
|
19171
|
+
nextAnimation.cameraTracks = [...payload.data.cameraTracks];
|
|
19172
|
+
}
|
|
19069
19173
|
nextAnimation.animation = structuredClone(payload.data.animation);
|
|
19070
19174
|
}
|
|
19071
19175
|
|
|
@@ -19135,6 +19239,13 @@ const COMMAND_DEFINITIONS = [
|
|
|
19135
19239
|
}
|
|
19136
19240
|
|
|
19137
19241
|
if (currentAnimation.type === "animation") {
|
|
19242
|
+
const cameraResult = validateCameraTracks({
|
|
19243
|
+
value: payload.data.cameraTracks ?? currentAnimation.cameraTracks,
|
|
19244
|
+
animation: payload.data.animation ?? currentAnimation.animation,
|
|
19245
|
+
path: "payload.data.cameraTracks",
|
|
19246
|
+
errorFactory: createPreconditionValidationError,
|
|
19247
|
+
});
|
|
19248
|
+
if (cameraResult?.valid === false) return cameraResult;
|
|
19138
19249
|
if (payload.data.animation !== undefined) {
|
|
19139
19250
|
const result = validateAnimationMaskImageReferences({
|
|
19140
19251
|
state,
|