perf: optimize extraction of fill, stroke, responder, matrix & display

potentially breaking change
if any issues show up, then they'll be considered regressions
should be resolved in ways which cause less traffic from js to native
This commit is contained in:
Mikael Sand
2020-01-18 05:14:54 +02:00
parent 4375ee676e
commit 279c3fcf84
8 changed files with 107 additions and 220 deletions

View File

@@ -156,96 +156,17 @@ exports[`supports CSS in style element 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
<RNSVGGroup
fill={4278190080}
fillOpacity={1}
fillRule={1}
font={Object {}}
matrix={
Array [
1,
0,
0,
1,
0,
0,
]
}
opacity={1}
originX={0}
originY={0}
propList={Array []}
rotation={0}
scaleX={1}
scaleY={1}
skewX={0}
skewY={0}
stroke={null}
strokeDasharray={null}
strokeDashoffset={null}
strokeLinecap={0}
strokeLinejoin={0}
strokeMiterlimit={4}
strokeOpacity={1}
strokeWidth={1}
vectorEffect={0}
x={0}
y={0}
>
<RNSVGDefs />
<RNSVGGroup
fill={4278190080}
fillOpacity={1}
fillRule={1}
font={Object {}}
matrix={
Array [
1,
0,
0,
1,
0,
0,
]
}
opacity={1}
originX={0}
originY={0}
propList={Array []}
rotation={0}
scaleX={1}
scaleY={1}
skewX={0}
skewY={0}
stroke={null}
strokeDasharray={null}
strokeDashoffset={null}
strokeLinecap={0}
strokeLinejoin={0}
strokeMiterlimit={4}
strokeOpacity={1}
strokeWidth={1}
vectorEffect={0}
x={0}
y={0}
>
<RNSVGRect
fill={4294901760}
fillOpacity={1}
fillRule={1}
height={200}
matrix={
Array [
1,
0,
0,
1,
0,
0,
]
}
opacity={1}
originX={0}
originY={0}
propList={
Array [
"fill",
@@ -253,79 +174,21 @@ exports[`supports CSS in style element 1`] = `
"strokeWidth",
]
}
rotation={0}
scaleX={1}
scaleY={1}
skewX={0}
skewY={0}
stroke={4278190335}
strokeDasharray={null}
strokeDashoffset={null}
strokeLinecap={0}
strokeLinejoin={0}
strokeMiterlimit={4}
strokeOpacity={1}
strokeWidth="16"
vectorEffect={0}
width={1000}
x={100}
y={0}
/>
</RNSVGGroup>
<RNSVGGroup
fill={4278190080}
fillOpacity={1}
fillRule={1}
font={Object {}}
matrix={
Array [
1,
0,
0,
1,
0,
0,
]
}
opacity={1}
originX={0}
originY={0}
propList={Array []}
rotation={0}
scaleX={1}
scaleY={1}
skewX={0}
skewY={0}
stroke={null}
strokeDasharray={null}
strokeDashoffset={null}
strokeLinecap={0}
strokeLinejoin={0}
strokeMiterlimit={4}
strokeOpacity={1}
strokeWidth={1}
vectorEffect={0}
x={0}
y={0}
>
<RNSVGRect
fill={4294901760}
fillOpacity={0.3}
fillRule={1}
height={200}
matrix={
Array [
1,
0,
0,
1,
0,
0,
]
}
opacity={1}
originX={0}
originY={0}
propList={
Array [
"fill",
@@ -334,20 +197,8 @@ exports[`supports CSS in style element 1`] = `
"strokeWidth",
]
}
rotation={0}
scaleX={1}
scaleY={1}
skewX={0}
skewY={0}
stroke={4278190335}
strokeDasharray={null}
strokeDashoffset={null}
strokeLinecap={0}
strokeLinejoin={0}
strokeMiterlimit={4}
strokeOpacity={1}
strokeWidth="16"
vectorEffect={0}
width={750}
x={100}
y={350}

View File

@@ -17,6 +17,7 @@ import {
ResponderProps,
TransformProps,
ResponderInstanceProps,
extractedProps,
} from '../lib/extract/types';
import extractResponder from '../lib/extract/extractResponder';
import extractViewBox from '../lib/extract/extractViewBox';
@@ -153,6 +154,8 @@ export default class Svg extends Shape<
: null;
const tint = extractColor(color);
const responder: extractedProps = {};
extractResponder(responder, props, this as ResponderInstanceProps);
return (
<RNSVGSvg
{...props}
@@ -164,8 +167,8 @@ export default class Svg extends Shape<
ref={this.refMethod}
style={[styles.svg, style, opacityStyle, dimensions]}
focusable={Boolean(focusable) && focusable !== 'false'}
{...extractResponder(props, this as ResponderInstanceProps)}
{...extractViewBox({ viewBox, preserveAspectRatio })}
{...responder}
>
<G
{...{

View File

@@ -1,7 +1,7 @@
import extractBrush from './extractBrush';
import extractOpacity from './extractOpacity';
import { colorNames } from './extractColor';
import { FillProps } from './types';
import { extractedProps, FillProps } from './types';
const fillRules: { evenodd: number; nonzero: number } = {
evenodd: 0,
@@ -11,24 +11,22 @@ const fillRules: { evenodd: number; nonzero: number } = {
const defaultFill = colorNames.black;
export default function extractFill(
o: extractedProps,
props: FillProps,
styleProperties: string[],
) {
const { fill, fillRule, fillOpacity } = props;
if (fill != null) {
styleProperties.push('fill');
o.fill =
!fill && typeof fill !== 'number' ? defaultFill : extractBrush(fill);
}
if (fillOpacity != null) {
styleProperties.push('fillOpacity');
o.fillOpacity = extractOpacity(fillOpacity);
}
if (fillRule != null) {
styleProperties.push('fillRule');
o.fillRule = fillRule && fillRules[fillRule] === 0 ? 0 : 1;
}
return {
fill: !fill && typeof fill !== 'number' ? defaultFill : extractBrush(fill),
fillRule: fillRule && fillRules[fillRule] === 0 ? 0 : 1,
fillOpacity: extractOpacity(fillOpacity),
};
}

View File

@@ -1,18 +1,18 @@
import extractFill from './extractFill';
import extractStroke from './extractStroke';
import { transformToMatrix, props2transform } from './extractTransform';
import { props2transform, transformToMatrix } from './extractTransform';
import extractResponder from './extractResponder';
import extractOpacity from './extractOpacity';
import { idPattern } from '../util';
import {
ClipProps,
extractedProps,
FillProps,
NumberProp,
ResponderProps,
StrokeProps,
TransformProps,
} from './types';
import { Component } from 'react';
const clipRules: { evenodd: number; nonzero: number } = {
evenodd: 0,
@@ -74,30 +74,24 @@ export default function extractProps(
const styleProperties: string[] = [];
const transformProps = props2transform(props);
const matrix = transformToMatrix(transformProps, transform);
const extracted: {
name?: string;
mask?: string;
opacity: number;
matrix: number[];
propList: string[];
onLayout?: () => void;
ref?: (instance: Component | null) => void;
markerStart?: string;
markerMid?: string;
markerEnd?: string;
clipPath?: string;
clipRule?: number;
display?: string;
} = {
matrix,
...transformProps,
const extracted: extractedProps = {
propList: styleProperties,
opacity: extractOpacity(opacity),
...extractResponder(props, ref),
...extractFill(props, styleProperties),
...extractStroke(props, styleProperties),
display: display === 'none' ? 'none' : undefined,
};
extractResponder(extracted, props, ref);
extractFill(extracted, props, styleProperties);
extractStroke(extracted, props, styleProperties);
if (matrix !== null) {
extracted.matrix = matrix;
}
if (opacity != null) {
extracted.opacity = extractOpacity(opacity);
}
if (display != null) {
extracted.display = display === 'none' ? 'none' : undefined;
}
if (onLayout) {
extracted.onLayout = onLayout;

View File

@@ -1,10 +1,15 @@
import { PanResponder } from 'react-native';
import { ResponderInstanceProps, ResponderProps } from './types';
import {
extractedProps,
ResponderInstanceProps,
ResponderProps,
} from './types';
const responderKeys = Object.keys(PanResponder.create({}).panHandlers);
const numResponderKeys = responderKeys.length;
export default function extractResponder(
o: extractedProps,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
props: { [x: string]: any } & ResponderProps,
ref: ResponderInstanceProps,
@@ -20,9 +25,6 @@ export default function extractResponder(
delayLongPress,
pointerEvents,
} = props;
const o: {
[touchableProperty: string]: unknown;
} = {};
let responsible = false;
for (let i = 0; i < numResponderKeys; i++) {
@@ -62,6 +64,4 @@ export default function extractResponder(
if (responsible) {
o.responsible = true;
}
return o;
}

View File

@@ -1,7 +1,7 @@
import extractBrush from './extractBrush';
import extractOpacity from './extractOpacity';
import extractLengthList from './extractLengthList';
import { StrokeProps } from './types';
import { extractedProps, StrokeProps } from './types';
const caps = {
butt: 0,
@@ -25,6 +25,7 @@ const vectorEffects = {
};
export default function extractStroke(
o: extractedProps,
props: StrokeProps,
styleProperties: string[],
) {
@@ -42,50 +43,48 @@ export default function extractStroke(
if (stroke != null) {
styleProperties.push('stroke');
o.stroke = extractBrush(stroke);
}
if (strokeWidth != null) {
styleProperties.push('strokeWidth');
o.strokeWidth = strokeWidth;
}
if (strokeOpacity != null) {
styleProperties.push('strokeOpacity');
o.strokeOpacity = extractOpacity(strokeOpacity);
}
if (strokeDasharray != null) {
styleProperties.push('strokeDasharray');
const strokeDash =
!strokeDasharray || strokeDasharray === 'none'
? null
: extractLengthList(strokeDasharray);
o.strokeDasharray =
strokeDash && strokeDash.length % 2 === 1
? strokeDash.concat(strokeDash)
: strokeDash;
}
if (strokeDashoffset != null) {
styleProperties.push('strokeDashoffset');
o.strokeDashoffset =
strokeDasharray && strokeDashoffset ? +strokeDashoffset || 0 : null;
}
if (strokeLinecap != null) {
styleProperties.push('strokeLinecap');
o.strokeLinecap = (strokeLinecap && caps[strokeLinecap]) || 0;
}
if (strokeLinejoin != null) {
styleProperties.push('strokeLinejoin');
o.strokeLinejoin = (strokeLinejoin && joins[strokeLinejoin]) || 0;
}
if (strokeMiterlimit != null) {
styleProperties.push('strokeMiterlimit');
}
const strokeDash =
!strokeDasharray || strokeDasharray === 'none'
? null
: extractLengthList(strokeDasharray);
return {
stroke: extractBrush(stroke),
strokeOpacity: extractOpacity(strokeOpacity),
strokeLinecap: (strokeLinecap && caps[strokeLinecap]) || 0,
strokeLinejoin: (strokeLinejoin && joins[strokeLinejoin]) || 0,
strokeDasharray:
strokeDash && strokeDash.length % 2 === 1
? strokeDash.concat(strokeDash)
: strokeDash,
strokeWidth: strokeWidth != null ? strokeWidth : 1,
strokeDashoffset:
strokeDasharray && strokeDashoffset ? +strokeDashoffset || 0 : null,
strokeMiterlimit:
o.strokeMiterlimit =
(strokeMiterlimit && typeof strokeMiterlimit !== 'number'
? parseFloat(strokeMiterlimit)
: strokeMiterlimit) || 4,
vectorEffect: (vectorEffect && vectorEffects[vectorEffect]) || 0,
};
: strokeMiterlimit) || 4;
}
if (vectorEffect != null) {
o.vectorEffect = (vectorEffect && vectorEffects[vectorEffect]) || 0;
}
}

View File

@@ -67,9 +67,11 @@ function universal2axis(
return [x || defaultValue || 0, y || defaultValue || 0];
}
export function props2transform(props: TransformProps): TransformedProps {
export function props2transform(
props: TransformProps,
): TransformedProps | null {
const {
rotation = 0,
rotation,
translate,
translateX,
translateY,
@@ -85,6 +87,25 @@ export function props2transform(props: TransformProps): TransformedProps {
x,
y,
} = props;
if (
rotation == null &&
translate == null &&
translateX == null &&
translateY == null &&
origin == null &&
originX == null &&
originY == null &&
scale == null &&
scaleX == null &&
scaleY == null &&
skew == null &&
skewX == null &&
skewY == null &&
x == null &&
y == null
) {
return null;
}
if (Array.isArray(x) || Array.isArray(y)) {
console.warn(
@@ -101,7 +122,7 @@ export function props2transform(props: TransformProps): TransformedProps {
const sk = universal2axis(skew, skewX, skewY);
return {
rotation: +rotation || 0,
rotation: rotation == null ? 0 : +rotation || 0,
originX: or[0],
originY: or[1],
scaleX: sc[0],
@@ -114,11 +135,14 @@ export function props2transform(props: TransformProps): TransformedProps {
}
export function transformToMatrix(
props: TransformedProps,
transform: number[] | string | TransformProps | void | undefined,
): [number, number, number, number, number, number] {
props: TransformedProps | null,
transform: number[] | string | TransformProps | void | null | undefined,
): [number, number, number, number, number, number] | null {
if (!props && !transform) {
return null;
}
reset();
appendTransformProps(props);
props && appendTransformProps(props);
if (transform) {
if (Array.isArray(transform)) {
@@ -141,7 +165,8 @@ export function transformToMatrix(
console.error(e);
}
} else {
appendTransformProps(props2transform(transform));
const transformProps = props2transform(transform);
transformProps && appendTransformProps(transformProps);
}
}

View File

@@ -1,3 +1,4 @@
import { Component } from 'react';
import { GestureResponderEvent } from 'react-native';
export type NumberProp = string | number;
@@ -93,3 +94,19 @@ export type ClipProps = {
clipPath?: string;
clipRule?: 'evenodd' | 'nonzero';
};
export type extractedProps = {
name?: string;
mask?: string;
opacity?: number;
matrix?: number[];
propList?: string[];
onLayout?: () => void;
ref?: (instance: Component | null) => void;
markerStart?: string;
markerMid?: string;
markerEnd?: string;
clipPath?: string;
clipRule?: number;
display?: string;
[touchableProperty: string]: unknown;
};