UNPKG

7.58 kBTypeScriptView Raw
1import { Component } from 'react';
2import SvgTouchableMixin from '../lib/SvgTouchableMixin';
3import {
4 NativeModules,
5 findNodeHandle,
6 NativeMethodsMixinStatic,
7} from 'react-native';
8import { TransformProps } from '../lib/extract/types';
9
10const RNSVGRenderableManager = NativeModules.RNSVGRenderableManager;
11
12export interface SVGBoundingBoxOptions {
13 fill?: boolean;
14 stroke?: boolean;
15 markers?: boolean;
16 clipped?: boolean;
17}
18
19export interface DOMPointInit {
20 x?: number;
21 y?: number;
22 z?: number;
23 w?: number;
24}
25
26export interface Point {
27 x: number;
28 y: number;
29}
30
31export interface SVGPoint extends Point {
32 constructor(point?: Point): SVGPoint;
33 matrixTransform(matrix: Matrix): SVGPoint;
34}
35
36export interface Rect {
37 x: number;
38 y: number;
39 width: number;
40 height: number;
41}
42export interface SVGRect extends Rect {}
43
44export interface Matrix {
45 a: number;
46 b: number;
47 c: number;
48 d: number;
49 e: number;
50 f: number;
51}
52
53export interface SVGMatrix extends Matrix {
54 constructor(matrix?: Matrix): SVGMatrix;
55 multiply(secondMatrix: Matrix): SVGMatrix;
56 inverse(): SVGMatrix;
57 translate(x: number, y: number): SVGMatrix;
58 scale(scaleFactor: number): SVGMatrix;
59 scaleNonUniform(scaleFactorX: number, scaleFactorY: number): SVGMatrix;
60 rotate(angle: number): SVGMatrix;
61 rotateFromVector(x: number, y: number): SVGMatrix;
62 flipX(): SVGMatrix;
63 flipY(): SVGMatrix;
64 skewX(angle: number): SVGMatrix;
65 skewY(angle: number): SVGMatrix;
66}
67
68export function multiply_matrices(l: Matrix, r: Matrix): Matrix {
69 const { a: al, b: bl, c: cl, d: dl, e: el, f: fl } = l;
70 const { a: ar, b: br, c: cr, d: dr, e: er, f: fr } = r;
71
72 const a = al * ar + cl * br;
73 const c = al * cr + cl * dr;
74 const e = al * er + cl * fr + el;
75 const b = bl * ar + dl * br;
76 const d = bl * cr + dl * dr;
77 const f = bl * er + dl * fr + fl;
78
79 return { a, c, e, b, d, f };
80}
81
82export function invert({ a, b, c, d, e, f }: Matrix): Matrix {
83 const n = a * d - b * c;
84 return {
85 a: d / n,
86 b: -b / n,
87 c: -c / n,
88 d: a / n,
89 e: (c * f - d * e) / n,
90 f: -(a * f - b * e) / n,
91 };
92}
93
94const deg2rad = Math.PI / 180;
95
96export class SVGMatrix implements SVGMatrix {
97 constructor(matrix?: Matrix) {
98 if (matrix) {
99 const { a, b, c, d, e, f } = matrix;
100 this.a = a;
101 this.b = b;
102 this.c = c;
103 this.d = d;
104 this.e = e;
105 this.f = f;
106 } else {
107 this.a = 1;
108 this.b = 0;
109 this.c = 0;
110 this.d = 1;
111 this.e = 0;
112 this.f = 0;
113 }
114 }
115 multiply(secondMatrix: Matrix): SVGMatrix {
116 return new SVGMatrix(multiply_matrices(this, secondMatrix));
117 }
118 inverse(): SVGMatrix {
119 return new SVGMatrix(invert(this));
120 }
121 translate(x: number, y: number): SVGMatrix {
122 return new SVGMatrix(
123 multiply_matrices(this, { a: 1, b: 0, c: 0, d: 1, e: x, f: y }),
124 );
125 }
126 scale(scaleFactor: number): SVGMatrix {
127 return new SVGMatrix(
128 multiply_matrices(this, {
129 a: scaleFactor,
130 b: 0,
131 c: 0,
132 d: scaleFactor,
133 e: 0,
134 f: 0,
135 }),
136 );
137 }
138 scaleNonUniform(scaleFactorX: number, scaleFactorY: number): SVGMatrix {
139 return new SVGMatrix(
140 multiply_matrices(this, {
141 a: scaleFactorX,
142 b: 0,
143 c: 0,
144 d: scaleFactorY,
145 e: 0,
146 f: 0,
147 }),
148 );
149 }
150 rotate(angle: number): SVGMatrix {
151 const cos = Math.cos(deg2rad * angle);
152 const sin = Math.sin(deg2rad * angle);
153 return new SVGMatrix(
154 multiply_matrices(this, { a: cos, b: sin, c: -sin, d: cos, e: 0, f: 0 }),
155 );
156 }
157 rotateFromVector(x: number, y: number): SVGMatrix {
158 const angle = Math.atan2(y, x);
159 const cos = Math.cos(deg2rad * angle);
160 const sin = Math.sin(deg2rad * angle);
161 return new SVGMatrix(
162 multiply_matrices(this, { a: cos, b: sin, c: -sin, d: cos, e: 0, f: 0 }),
163 );
164 }
165 flipX(): SVGMatrix {
166 return new SVGMatrix(
167 multiply_matrices(this, { a: -1, b: 0, c: 0, d: 1, e: 0, f: 0 }),
168 );
169 }
170 flipY(): SVGMatrix {
171 return new SVGMatrix(
172 multiply_matrices(this, { a: 1, b: 0, c: 0, d: -1, e: 0, f: 0 }),
173 );
174 }
175 skewX(angle: number): SVGMatrix {
176 return new SVGMatrix(
177 multiply_matrices(this, {
178 a: 1,
179 b: 0,
180 c: Math.tan(deg2rad * angle),
181 d: 1,
182 e: 0,
183 f: 0,
184 }),
185 );
186 }
187 skewY(angle: number): SVGMatrix {
188 return new SVGMatrix(
189 multiply_matrices(this, {
190 a: 1,
191 b: Math.tan(deg2rad * angle),
192 c: 0,
193 d: 1,
194 e: 0,
195 f: 0,
196 }),
197 );
198 }
199}
200
201export function matrixTransform(matrix: Matrix, point: Point): Point {
202 const { a, b, c, d, e, f } = matrix;
203 const { x, y } = point;
204 return {
205 x: a * x + c * y + e,
206 y: b * x + d * y + f,
207 };
208}
209
210export class SVGPoint implements SVGPoint {
211 constructor(point?: Point) {
212 if (point) {
213 const { x, y } = point;
214 this.x = x;
215 this.y = y;
216 } else {
217 this.x = 0;
218 this.y = 0;
219 }
220 }
221 matrixTransform(matrix: Matrix): SVGPoint {
222 return new SVGPoint(matrixTransform(matrix, this));
223 }
224}
225
226export const ownerSVGElement = {
227 createSVGPoint(): SVGPoint {
228 return new SVGPoint();
229 },
230 createSVGMatrix(): SVGMatrix {
231 return new SVGMatrix();
232 },
233};
234
235export default class Shape<P> extends Component<P> {
236 [x: string]: unknown;
237 root: (Shape<P> & NativeMethodsMixinStatic) | null = null;
238 constructor(props: P, context: {}) {
239 super(props, context);
240 SvgTouchableMixin(this);
241 }
242 refMethod: (
243 instance: (Shape<P> & NativeMethodsMixinStatic) | null,
244 ) => void = (instance: (Shape<P> & NativeMethodsMixinStatic) | null) => {
245 this.root = instance;
246 };
247 setNativeProps = (
248 props: Object & {
249 matrix?: [number, number, number, number, number, number];
250 } & TransformProps,
251 ) => {
252 this.root && this.root.setNativeProps(props);
253 };
254 /*
255 * The following native methods are experimental and likely broken in some
256 * ways. If you have a use case for these, please open an issue with a
257 * representative example / reproduction.
258 * */
259 getBBox = (options?: SVGBoundingBoxOptions): SVGRect => {
260 const { fill = true, stroke = true, markers = true, clipped = true } =
261 options || {};
262 const handle = findNodeHandle(this.root as Component);
263 return RNSVGRenderableManager.getBBox(handle, {
264 fill,
265 stroke,
266 markers,
267 clipped,
268 });
269 };
270 getCTM = (): SVGMatrix => {
271 const handle = findNodeHandle(this.root as Component);
272 return new SVGMatrix(RNSVGRenderableManager.getCTM(handle));
273 };
274 getScreenCTM = (): SVGMatrix => {
275 const handle = findNodeHandle(this.root as Component);
276 return new SVGMatrix(RNSVGRenderableManager.getScreenCTM(handle));
277 };
278 isPointInFill = (options: DOMPointInit): boolean => {
279 const handle = findNodeHandle(this.root as Component);
280 return RNSVGRenderableManager.isPointInFill(handle, options);
281 };
282 isPointInStroke = (options: DOMPointInit): boolean => {
283 const handle = findNodeHandle(this.root as Component);
284 return RNSVGRenderableManager.isPointInStroke(handle, options);
285 };
286 getTotalLength = (): number => {
287 const handle = findNodeHandle(this.root as Component);
288 return RNSVGRenderableManager.getTotalLength(handle);
289 };
290 getPointAtLength = (length: number): SVGPoint => {
291 const handle = findNodeHandle(this.root as Component);
292 return new SVGPoint(
293 RNSVGRenderableManager.getPointAtLength(handle, { length }),
294 );
295 };
296}
297Shape.prototype.ownerSVGElement = ownerSVGElement;