Skip to content

Commit

Permalink
fix: disable memoize during interpolation #1618 (#1620)
Browse files Browse the repository at this point in the history
* fix: disable memoize during interpolation #1618

* chore: commit changeset
  • Loading branch information
xiaoiver authored Jan 10, 2024
1 parent f9512fe commit 5f5cf27
Show file tree
Hide file tree
Showing 22 changed files with 284 additions and 34 deletions.
6 changes: 6 additions & 0 deletions .changeset/loud-waves-reflect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@antv/g-web-animations-api': patch
'@antv/g-lite': patch
---

Disable memoize during interpolation to avoid OOM.
28 changes: 28 additions & 0 deletions __tests__/demos/2d/circles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Circle, runtime } from '../../../packages/g';

export async function circles(context) {
runtime.enableCSSParsing = false;

const { canvas } = context;
await canvas.ready;

for (let i = 0; i < 50000; i++) {
const circle = new Circle({
style: {
cx: Math.random() * 500,
cy: Math.random() * 500,
r: 5,
fill: 'red',
stroke: 'blue',
},
});
canvas.appendChild(circle);

circle.addEventListener('mouseenter', () => {
circle.style.fill = 'green';
});
circle.addEventListener('mouseleave', () => {
circle.style.fill = 'red';
});
}
}
1 change: 1 addition & 0 deletions __tests__/demos/lottie/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { spring } from './spring';
14 changes: 14 additions & 0 deletions __tests__/demos/lottie/spring.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { runtime } from '../../../packages/g';
import { loadAnimation } from '../../../packages/g-lottie-player';
import * as d3 from 'd3';

export async function spring(context) {
runtime.enableCSSParsing = true;

const { canvas } = context;
await canvas.ready;

const data = await d3.json('/lottie/spring.json');
const animation = loadAnimation(data, { loop: true, autoplay: true });
const wrapper = animation.render(canvas);
}
2 changes: 2 additions & 0 deletions __tests__/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import * as animation from './demos/animation';
import * as d3 from './demos/d3';
import * as plugin from './demos/plugin';
import * as hammerjs from './demos/hammerjs';
import * as lottie from './demos/lottie';
import * as bugfix from './demos/bugfix';

const tests = {
Expand All @@ -21,6 +22,7 @@ const tests = {
...createSpecRender(namespace(d3, 'd3')),
...createSpecRender(namespace(plugin, 'plugin')),
...createSpecRender(namespace(hammerjs, 'hammerjs')),
...createSpecRender(namespace(lottie, 'lottie')),
...createSpecRender(namespace(bugfix, 'bugfix')),
};

Expand Down
9 changes: 9 additions & 0 deletions packages/g-lite/src/css/CSSProperty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ export interface CSSProperty<Parsed, Used> {
* '180deg' -> CSS.deg(180)
*/
parser: CSSPropertyParser<Parsed>;

/**
* Don't use memoize, eg. during animation.
*/
parserUnmemoize: CSSPropertyParser<Parsed>;

/**
* Ignore CSS syntax.
*/
parserWithCSSDisabled: CSSPropertyParser<Parsed>;

/**
Expand Down
17 changes: 14 additions & 3 deletions packages/g-lite/src/css/StyleValueRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,7 @@ export class DefaultStyleValueRegistry implements StyleValueRegistry {
skipParse: false,
forceUpdateGeometry: false,
usedAttributes: [],
memoize: true,
},
) {
if (!this.runtime.enableCSSParsing) {
Expand Down Expand Up @@ -932,6 +933,7 @@ export class DefaultStyleValueRegistry implements StyleValueRegistry {
skipParse,
forceUpdateGeometry,
usedAttributes,
memoize,
} = options;

let needUpdateGeometry = forceUpdateGeometry;
Expand All @@ -953,6 +955,7 @@ export class DefaultStyleValueRegistry implements StyleValueRegistry {
name as string,
object.attributes[name],
object,
memoize,
);
});
}
Expand Down Expand Up @@ -997,6 +1000,7 @@ export class DefaultStyleValueRegistry implements StyleValueRegistry {
name as string,
object.computedStyle[name],
object,
memoize,
);
}
});
Expand Down Expand Up @@ -1041,6 +1045,7 @@ export class DefaultStyleValueRegistry implements StyleValueRegistry {
name: string,
value: any,
object: DisplayObject,
memoized: boolean,
): CSSStyleValue {
const metadata = propertyMetadataCache[name];

Expand All @@ -1061,9 +1066,13 @@ export class DefaultStyleValueRegistry implements StyleValueRegistry {
if (keywords && keywords.indexOf(value) > -1) {
// computed = new CSSKeywordValue(value);
computed = getOrCreateKeyword(value);
} else if (handler && handler.parser) {
// try to parse it to CSSStyleValue, eg. '10px' -> CSS.px(10)
computed = handler.parser(value, object);
} else if (handler) {
if (!memoized && handler.parserUnmemoize) {
computed = handler.parserUnmemoize(value, object);
} else if (handler.parser) {
// try to parse it to CSSStyleValue, eg. '10px' -> CSS.px(10)
computed = handler.parser(value, object);
}
}
}
}
Expand All @@ -1078,6 +1087,7 @@ export class DefaultStyleValueRegistry implements StyleValueRegistry {
name: string,
computed: CSSStyleValue,
object: DisplayObject,
memoized: boolean,
) {
const metadata = propertyMetadataCache[name];
const isDocumentElement = object.id === 'g-root';
Expand Down Expand Up @@ -1110,6 +1120,7 @@ export class DefaultStyleValueRegistry implements StyleValueRegistry {
? defaultValue(object.nodeName)
: defaultValue,
object,
memoized,
);
}
} else if (value === 'inherit') {
Expand Down
3 changes: 3 additions & 0 deletions packages/g-lite/src/css/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export interface PropertyParseOptions {
skipParse: boolean;
forceUpdateGeometry: boolean;
usedAttributes: string[];
memoize: boolean;
}

export interface StyleValueRegistry {
Expand All @@ -150,11 +151,13 @@ export interface StyleValueRegistry {
name: string,
value: any,
object: DisplayObject,
memoized: boolean,
) => CSSStyleValue;
computeProperty: (
name: string,
computed: CSSStyleValue,
object: DisplayObject,
memoized: boolean,
) => any;
}

Expand Down
42 changes: 28 additions & 14 deletions packages/g-lite/src/css/parser/dimension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,46 +59,45 @@ export function parseDimension(
* @see https://developer.mozilla.org/zh-CN/docs/Web/CSS/length
* length with only absolute unit, eg. 1px
*/
export const parseLength = memoize((css: string) => {
export const parseLengthUnmemoize = (css: string) => {
return parseDimension(new RegExp('px', 'g'), css);
});
};
export const parseLength = memoize(parseLengthUnmemoize);

/**
* <percentage>
* @see https://developer.mozilla.org/zh-CN/docs/Web/CSS/percentage
*/
export const parserPercentage = memoize((css: string) => {
export const parserPercentageUnmemoize = (css: string) => {
return parseDimension(new RegExp('%', 'g'), css);
});
};
export const parserPercentage = memoize(parserPercentageUnmemoize);

/**
* length with absolute or relative unit,
* eg. 1px, 0.7em, 50%, calc(100% - 200px);
*
* @see https://developer.mozilla.org/zh-CN/docs/Web/CSS/length-percentage
*/
// export const parseLengthOrPercentage = memoize((css: string): CSSUnitValue => {
// if (isNumber(css) || isFinite(Number(css))) {
// return getOrCreateUnitValue(Number(css), 'px');
// }
// return parseDimension(new RegExp('px|%|em|rem', 'g'), css) as CSSUnitValue;
// });

export const parseLengthOrPercentage = (css: string): CSSUnitValue => {
export const parseLengthOrPercentageUnmemoize = (css: string): CSSUnitValue => {
if (isNumber(css) || isFinite(Number(css))) {
// Number(css) is NaN
return getOrCreateUnitValue(Number(css) || 0, 'px');
// return Number(css);
}
return parseDimension(new RegExp('px|%|em|rem', 'g'), css) as CSSUnitValue;
};
export const parseLengthOrPercentage = memoize(
parseLengthOrPercentageUnmemoize,
);

export const parseAngle = memoize((css: string): CSSUnitValue => {
export const parseAngleUnmemoize = (css: string): CSSUnitValue => {
return parseDimension(
new RegExp('deg|rad|grad|turn', 'g'),
css,
) as CSSUnitValue;
});
};
export const parseAngle = memoize(parseAngleUnmemoize);

/**
* merge CSSUnitValue
Expand Down Expand Up @@ -211,6 +210,21 @@ export function parseDimensionArray(
return string.map((segment) => parseLengthOrPercentage(segment.toString()));
}
}
export function parseDimensionArrayUnmemoize(
string: string | (string | number)[],
): CSSUnitValue[] {
if (isString(string)) {
// "1px 2px 3px"
return string
.split(' ')
.map((segment) => parseLengthOrPercentageUnmemoize(segment));
} else {
// [1, '2px', 3]
return string.map((segment) =>
parseLengthOrPercentageUnmemoize(segment.toString()),
);
}
}

// export function mergeDimensionList(
// left: CSSUnitValue[],
Expand Down
14 changes: 12 additions & 2 deletions packages/g-lite/src/css/parser/numeric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function numberToString(x: number) {
* * 0 -> CSSUnitValue(0)
* * '2' -> CSSUnitValue(2)
*/
export const parseNumber = memoize((string: string | number): CSSUnitValue => {
export const parseNumberUnmemoize = (string: string | number): CSSUnitValue => {
if (typeof string === 'number') {
return getOrCreateUnitValue(string);
}
Expand All @@ -25,13 +25,23 @@ export const parseNumber = memoize((string: string | number): CSSUnitValue => {
} else {
return getOrCreateUnitValue(0);
}
});
};
export const parseNumber = memoize(parseNumberUnmemoize);

/**
* separate string to array
* eg.
* * [0.5, 0.5] -> [CSSUnitValue, CSSUnitValue]
*/
export const parseNumberListUnmemoize = (
string: string | number[],
): CSSUnitValue[] => {
if (isString(string)) {
return string.split(' ').map(parseNumberUnmemoize);
} else {
return string.map(parseNumberUnmemoize);
}
};
export const parseNumberList = memoize(
(string: string | number[]): CSSUnitValue[] => {
if (isString(string)) {
Expand Down
45 changes: 44 additions & 1 deletion packages/g-lite/src/css/parser/transform-origin.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { isString } from '@antv/util';
import type { CSSUnitValue } from '../cssom';
import { getOrCreateUnitValue } from '../CSSStyleValuePool';
import { parseLengthOrPercentage } from './dimension';
import {
parseLengthOrPercentage,
parseLengthOrPercentageUnmemoize,
} from './dimension';
import { memoize } from '../../utils/memoize';

/**
Expand Down Expand Up @@ -48,6 +51,46 @@ export const parseTransformOrigin = memoize(
}
},
);
export const parseTransformOriginUnmemoize = (
value: string | number[],
): [CSSUnitValue, CSSUnitValue] => {
if (isString(value)) {
if (value === 'text-anchor') {
return [getOrCreateUnitValue(0, 'px'), getOrCreateUnitValue(0, 'px')];
}

const values = value.split(' ');
if (values.length === 1) {
if (values[0] === 'top' || values[0] === 'bottom') {
// 'top' -> 'center top'
values[1] = values[0];
values[0] = 'center';
} else {
// '50px' -> '50px center'
values[1] = 'center';
}
}

if (values.length !== 2) {
return null;
}

// eg. center bottom
return [
parseLengthOrPercentageUnmemoize(
convertKeyword2Percent(values[0]),
) as CSSUnitValue,
parseLengthOrPercentageUnmemoize(
convertKeyword2Percent(values[1]),
) as CSSUnitValue,
];
} else {
return [
getOrCreateUnitValue(value[0] || 0, 'px'),
getOrCreateUnitValue(value[1] || 0, 'px'),
];
}
};

function convertKeyword2Percent(keyword: string) {
if (keyword === 'center') {
Expand Down
Loading

0 comments on commit 5f5cf27

Please sign in to comment.