chore: cleanup
This commit is contained in:
parent
2ee0a44735
commit
b8d08df244
|
|
@ -1,200 +0,0 @@
|
||||||
import type {
|
|
||||||
AnimationBindingKind,
|
|
||||||
AnimationInterpolation,
|
|
||||||
AnimationPropertyPath,
|
|
||||||
AnimationValue,
|
|
||||||
NumericSpec,
|
|
||||||
} from "@/animation/types";
|
|
||||||
import {
|
|
||||||
coerceParamValue,
|
|
||||||
getParamDefaultInterpolation,
|
|
||||||
getParamNumericRange,
|
|
||||||
getParamValueKind,
|
|
||||||
type ParamDefinition,
|
|
||||||
} from "@/params";
|
|
||||||
import {
|
|
||||||
getBuiltInElementParams,
|
|
||||||
getElementParam,
|
|
||||||
readElementParamValue,
|
|
||||||
writeElementParamValue,
|
|
||||||
} from "@/params/registry";
|
|
||||||
import type { ElementType, TimelineElement } from "@/timeline";
|
|
||||||
|
|
||||||
export interface AnimationPropertyDefinition {
|
|
||||||
kind: AnimationBindingKind;
|
|
||||||
defaultInterpolation: AnimationInterpolation;
|
|
||||||
numericRanges?: Partial<Record<string, NumericSpec>>;
|
|
||||||
supportsElement: ({ element }: { element: TimelineElement }) => boolean;
|
|
||||||
getValue: ({ element }: { element: TimelineElement }) => AnimationValue | null;
|
|
||||||
coerceValue: ({ value }: { value: AnimationValue }) => AnimationValue | null;
|
|
||||||
applyValue: ({
|
|
||||||
element,
|
|
||||||
value,
|
|
||||||
}: {
|
|
||||||
element: TimelineElement;
|
|
||||||
value: AnimationValue;
|
|
||||||
}) => TimelineElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFallbackParam({
|
|
||||||
propertyPath,
|
|
||||||
}: {
|
|
||||||
propertyPath: AnimationPropertyPath;
|
|
||||||
}): ParamDefinition | null {
|
|
||||||
const elementTypes: ElementType[] = [
|
|
||||||
"video",
|
|
||||||
"image",
|
|
||||||
"text",
|
|
||||||
"sticker",
|
|
||||||
"graphic",
|
|
||||||
"audio",
|
|
||||||
];
|
|
||||||
for (const type of elementTypes) {
|
|
||||||
const param =
|
|
||||||
getBuiltInElementParams({ type }).find(
|
|
||||||
(candidate) => candidate.key === propertyPath,
|
|
||||||
) ?? null;
|
|
||||||
if (param) {
|
|
||||||
return param;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildDefinition({
|
|
||||||
propertyPath,
|
|
||||||
element,
|
|
||||||
}: {
|
|
||||||
propertyPath: AnimationPropertyPath;
|
|
||||||
element?: TimelineElement;
|
|
||||||
}): AnimationPropertyDefinition | null {
|
|
||||||
const param = element
|
|
||||||
? getElementParam({ element, key: propertyPath })
|
|
||||||
: getFallbackParam({ propertyPath });
|
|
||||||
if (!param || param.keyframable === false) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const range = getParamNumericRange({ param });
|
|
||||||
|
|
||||||
return {
|
|
||||||
kind: getParamValueKind({ param }),
|
|
||||||
defaultInterpolation: getParamDefaultInterpolation({ param }),
|
|
||||||
numericRanges: range ? { value: range } : undefined,
|
|
||||||
supportsElement: ({ element: candidate }) =>
|
|
||||||
getElementParam({ element: candidate, key: propertyPath }) !== null,
|
|
||||||
getValue: ({ element: candidate }) =>
|
|
||||||
readElementParamValue({ element: candidate, param }),
|
|
||||||
coerceValue: ({ value }) => coerceParamValue({ param, value }),
|
|
||||||
applyValue: ({ element: candidate, value }) => {
|
|
||||||
const targetParam = getElementParam({
|
|
||||||
element: candidate,
|
|
||||||
key: propertyPath,
|
|
||||||
});
|
|
||||||
if (!targetParam) {
|
|
||||||
return candidate;
|
|
||||||
}
|
|
||||||
const coercedValue = coerceParamValue({
|
|
||||||
param: targetParam,
|
|
||||||
value,
|
|
||||||
});
|
|
||||||
if (coercedValue === null) {
|
|
||||||
return candidate;
|
|
||||||
}
|
|
||||||
return writeElementParamValue({
|
|
||||||
element: candidate,
|
|
||||||
param: targetParam,
|
|
||||||
value: coercedValue,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isAnimationPropertyPath(
|
|
||||||
propertyPath: string,
|
|
||||||
): propertyPath is AnimationPropertyPath {
|
|
||||||
return !propertyPath.startsWith("params.") && !propertyPath.startsWith("effects.");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getAnimationPropertyDefinition({
|
|
||||||
propertyPath,
|
|
||||||
element,
|
|
||||||
}: {
|
|
||||||
propertyPath: AnimationPropertyPath;
|
|
||||||
element?: TimelineElement;
|
|
||||||
}): AnimationPropertyDefinition {
|
|
||||||
const definition = buildDefinition({ propertyPath, element });
|
|
||||||
if (!definition) {
|
|
||||||
throw new Error(`Unknown animation property for element: ${propertyPath}`);
|
|
||||||
}
|
|
||||||
return definition;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function supportsAnimationProperty({
|
|
||||||
element,
|
|
||||||
propertyPath,
|
|
||||||
}: {
|
|
||||||
element: TimelineElement;
|
|
||||||
propertyPath: AnimationPropertyPath;
|
|
||||||
}): boolean {
|
|
||||||
return getElementParam({ element, key: propertyPath }) !== null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getElementBaseValueForProperty({
|
|
||||||
element,
|
|
||||||
propertyPath,
|
|
||||||
}: {
|
|
||||||
element: TimelineElement;
|
|
||||||
propertyPath: AnimationPropertyPath;
|
|
||||||
}): AnimationValue | null {
|
|
||||||
const param = getElementParam({ element, key: propertyPath });
|
|
||||||
if (!param) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return readElementParamValue({ element, param });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function withElementBaseValueForProperty({
|
|
||||||
element,
|
|
||||||
propertyPath,
|
|
||||||
value,
|
|
||||||
}: {
|
|
||||||
element: TimelineElement;
|
|
||||||
propertyPath: AnimationPropertyPath;
|
|
||||||
value: AnimationValue;
|
|
||||||
}): TimelineElement {
|
|
||||||
const param = getElementParam({ element, key: propertyPath });
|
|
||||||
if (!param) {
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
const coercedValue = coerceParamValue({ param, value });
|
|
||||||
if (coercedValue === null) {
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
return writeElementParamValue({ element, param, value: coercedValue });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getDefaultInterpolationForProperty({
|
|
||||||
propertyPath,
|
|
||||||
element,
|
|
||||||
}: {
|
|
||||||
propertyPath: AnimationPropertyPath;
|
|
||||||
element?: TimelineElement;
|
|
||||||
}): AnimationInterpolation {
|
|
||||||
return getAnimationPropertyDefinition({ propertyPath, element })
|
|
||||||
.defaultInterpolation;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function coerceAnimationValueForProperty({
|
|
||||||
propertyPath,
|
|
||||||
value,
|
|
||||||
element,
|
|
||||||
}: {
|
|
||||||
propertyPath: AnimationPropertyPath;
|
|
||||||
value: AnimationValue;
|
|
||||||
element?: TimelineElement;
|
|
||||||
}): AnimationValue | null {
|
|
||||||
return getAnimationPropertyDefinition({ propertyPath, element }).coerceValue({
|
|
||||||
value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { transformProjectV28ToV29 } from "../transformers/v28-to-v29";
|
||||||
import { asRecord, asRecordArray } from "./helpers";
|
import { asRecord, asRecordArray } from "./helpers";
|
||||||
|
|
||||||
describe("V28 to V29 Migration", () => {
|
describe("V28 to V29 Migration", () => {
|
||||||
test("moves built-in element fields into params", () => {
|
test("copies built-in element fields into params without deleting source fields", () => {
|
||||||
const result = transformProjectV28ToV29({
|
const result = transformProjectV28ToV29({
|
||||||
project: {
|
project: {
|
||||||
id: "project-v28-builtins",
|
id: "project-v28-builtins",
|
||||||
|
|
@ -83,10 +83,15 @@ describe("V28 to V29 Migration", () => {
|
||||||
const tracks = asRecord(scene.tracks);
|
const tracks = asRecord(scene.tracks);
|
||||||
const main = asRecord(tracks.main);
|
const main = asRecord(tracks.main);
|
||||||
const video = asRecordArray(main.elements)[0];
|
const video = asRecordArray(main.elements)[0];
|
||||||
expect(video.transform).toBeUndefined();
|
expect(video.transform).toEqual({
|
||||||
expect(video.opacity).toBeUndefined();
|
position: { x: 12, y: -4 },
|
||||||
expect(video.volume).toBeUndefined();
|
scaleX: 1.25,
|
||||||
expect(video.muted).toBeUndefined();
|
scaleY: 0.75,
|
||||||
|
rotate: 9,
|
||||||
|
});
|
||||||
|
expect(video.opacity).toBe(0.5);
|
||||||
|
expect(video.volume).toBe(-6);
|
||||||
|
expect(video.muted).toBe(true);
|
||||||
expect(video.params).toEqual({
|
expect(video.params).toEqual({
|
||||||
"transform.positionX": 12,
|
"transform.positionX": 12,
|
||||||
"transform.positionY": -4,
|
"transform.positionY": -4,
|
||||||
|
|
@ -101,8 +106,13 @@ describe("V28 to V29 Migration", () => {
|
||||||
|
|
||||||
const overlay = asRecordArray(tracks.overlay)[0];
|
const overlay = asRecordArray(tracks.overlay)[0];
|
||||||
const text = asRecordArray(asRecord(overlay).elements)[0];
|
const text = asRecordArray(asRecord(overlay).elements)[0];
|
||||||
expect(text.content).toBeUndefined();
|
expect(text.content).toBe("Hello");
|
||||||
expect(text.background).toBeUndefined();
|
expect(text.background).toEqual({
|
||||||
|
enabled: true,
|
||||||
|
color: "#111111",
|
||||||
|
paddingX: 10,
|
||||||
|
paddingY: 12,
|
||||||
|
});
|
||||||
expect(text.params).toMatchObject({
|
expect(text.params).toMatchObject({
|
||||||
content: "Hello",
|
content: "Hello",
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
|
|
|
||||||
|
|
@ -114,23 +114,6 @@ function migrateElement({ element }: { element: unknown }): unknown {
|
||||||
}
|
}
|
||||||
|
|
||||||
nextElement.params = params;
|
nextElement.params = params;
|
||||||
delete nextElement.transform;
|
|
||||||
delete nextElement.opacity;
|
|
||||||
delete nextElement.blendMode;
|
|
||||||
delete nextElement.volume;
|
|
||||||
delete nextElement.muted;
|
|
||||||
delete nextElement.content;
|
|
||||||
delete nextElement.fontSize;
|
|
||||||
delete nextElement.fontFamily;
|
|
||||||
delete nextElement.color;
|
|
||||||
delete nextElement.background;
|
|
||||||
delete nextElement.textAlign;
|
|
||||||
delete nextElement.fontWeight;
|
|
||||||
delete nextElement.fontStyle;
|
|
||||||
delete nextElement.textDecoration;
|
|
||||||
delete nextElement.letterSpacing;
|
|
||||||
delete nextElement.lineHeight;
|
|
||||||
|
|
||||||
return nextElement;
|
return nextElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@ import {
|
||||||
} from "@/text/layout";
|
} from "@/text/layout";
|
||||||
import { DEFAULTS } from "@/timeline/defaults";
|
import { DEFAULTS } from "@/timeline/defaults";
|
||||||
import { mediaTimeFromSeconds } from "@/wasm";
|
import { mediaTimeFromSeconds } from "@/wasm";
|
||||||
import type { CreateTextElement, TextBackground } from "@/timeline";
|
import type { CreateTextElement } from "@/timeline";
|
||||||
|
import type { TextBackground } from "@/text/background";
|
||||||
import type {
|
import type {
|
||||||
TextAlign,
|
TextAlign,
|
||||||
TextDecoration,
|
TextDecoration,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import type { TextBackground } from "@/timeline";
|
import type { TextBackground } from "@/text/background";
|
||||||
import type {
|
import type {
|
||||||
TextAlign,
|
TextAlign,
|
||||||
TextDecoration,
|
TextDecoration,
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,12 @@
|
||||||
export const CORNER_RADIUS_MIN = 0;
|
export const CORNER_RADIUS_MIN = 0;
|
||||||
export const CORNER_RADIUS_MAX = 100;
|
export const CORNER_RADIUS_MAX = 100;
|
||||||
|
|
||||||
|
export interface TextBackground {
|
||||||
|
enabled: boolean;
|
||||||
|
color: string;
|
||||||
|
cornerRadius?: number;
|
||||||
|
paddingX?: number;
|
||||||
|
paddingY?: number;
|
||||||
|
offsetX?: number;
|
||||||
|
offsetY?: number;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import type { TextBackground } from "@/timeline";
|
import type { TextBackground } from "@/text/background";
|
||||||
import { DEFAULTS } from "@/timeline/defaults";
|
import { DEFAULTS } from "@/timeline/defaults";
|
||||||
import type { TextAlign } from "@/text/primitives";
|
import type { TextAlign } from "@/text/primitives";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { CORNER_RADIUS_MIN } from "@/text/background";
|
import { CORNER_RADIUS_MIN } from "@/text/background";
|
||||||
import { DEFAULTS } from "@/timeline/defaults";
|
import { DEFAULTS } from "@/timeline/defaults";
|
||||||
import type { TextBackground, TextElement } from "@/timeline";
|
import type { TextElement } from "@/timeline";
|
||||||
|
import type { TextBackground } from "@/text/background";
|
||||||
import { resolveNumberAtTime } from "@/animation/values";
|
import { resolveNumberAtTime } from "@/animation/values";
|
||||||
import {
|
import {
|
||||||
getTextVisualRect,
|
getTextVisualRect,
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,6 @@ import {
|
||||||
} from "@/params";
|
} from "@/params";
|
||||||
import {
|
import {
|
||||||
getElementParam,
|
getElementParam,
|
||||||
readElementParamValue,
|
|
||||||
writeElementParamValue,
|
|
||||||
type ElementParamDefinition,
|
|
||||||
} from "@/params/registry";
|
} from "@/params/registry";
|
||||||
import type { TimelineElement } from "@/timeline";
|
import type { TimelineElement } from "@/timeline";
|
||||||
import { isVisualElement } from "@/timeline/element-utils";
|
import { isVisualElement } from "@/timeline/element-utils";
|
||||||
|
|
@ -96,35 +93,14 @@ function buildElementParamDescriptor({
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildTimelineElementParamDescriptor({ element, param });
|
return buildParamDescriptor({
|
||||||
}
|
param,
|
||||||
|
baseParams: element.params,
|
||||||
function buildTimelineElementParamDescriptor({
|
setParams: (params) => ({
|
||||||
element,
|
...element,
|
||||||
param,
|
params,
|
||||||
}: {
|
}),
|
||||||
element: TimelineElement;
|
});
|
||||||
param: ElementParamDefinition;
|
|
||||||
}): AnimationPathDescriptor | null {
|
|
||||||
if (param.keyframable === false) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
kind: getParamValueKind({ param }),
|
|
||||||
defaultInterpolation: getParamDefaultInterpolation({ param }),
|
|
||||||
numericRanges: paramNumericRanges({ param }),
|
|
||||||
coerceValue: ({ value }) => coerceParamValue({ param, value }),
|
|
||||||
getBaseValue: () => readElementParamValue({ element, param }),
|
|
||||||
setBaseValue: ({ value }) => {
|
|
||||||
const coercedValue = coerceParamValue({ param, value });
|
|
||||||
if (coercedValue === null) {
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
return writeElementParamValue({ element, param, value: coercedValue });
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildGraphicParamDescriptor({
|
function buildGraphicParamDescriptor({
|
||||||
|
|
|
||||||
|
|
@ -117,8 +117,6 @@ interface BaseTimelineElement {
|
||||||
export interface VideoElement extends BaseTimelineElement {
|
export interface VideoElement extends BaseTimelineElement {
|
||||||
type: "video";
|
type: "video";
|
||||||
mediaId: string;
|
mediaId: string;
|
||||||
volume?: number;
|
|
||||||
muted?: boolean;
|
|
||||||
isSourceAudioEnabled?: boolean;
|
isSourceAudioEnabled?: boolean;
|
||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
retime?: RetimeConfig;
|
retime?: RetimeConfig;
|
||||||
|
|
@ -134,16 +132,6 @@ export interface ImageElement extends BaseTimelineElement {
|
||||||
masks?: Mask[];
|
masks?: Mask[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TextBackground {
|
|
||||||
enabled: boolean;
|
|
||||||
color: string;
|
|
||||||
cornerRadius?: number;
|
|
||||||
paddingX?: number;
|
|
||||||
paddingY?: number;
|
|
||||||
offsetX?: number;
|
|
||||||
offsetY?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TextElement extends BaseTimelineElement {
|
export interface TextElement extends BaseTimelineElement {
|
||||||
type: "text";
|
type: "text";
|
||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue