react-native之ART绘图详解

来源:互联网 发布:移动数据流量是什么 编辑:程序博客网 时间:2024/06/03 13:28

背景

在移动应用的开发过程中,绘制基本的二维图形或动画是必不可少的。然而,考虑到Android和iOS均有一套各自的API方案,因此采用一种更普遍接受的技术方案,更有利于代码的双平台兼容。

art是一个旨在多浏览器兼容的Node style CommonJS模块。在它的基础上,Facebook又开发了React-art ,封装art,使之可以被react.js所使用,即实现了前端的svg库。然而,考虑到react.js的JSX语法,已经支持将 等等svg标签直接插入到dom中(当然此时使用的就不是react-art库了)此外还有HTML canvas的存在,因此,在前端上,react-art并非不可替代。

然而,在移动端,考虑到跨平台的需求,加之web端的技术积累,react-art成为了现成的绘制图形的解决方案。react-native分别在0.10.0和0.18.0上添加了ios和android平台上对react-art的支持。

示例代码

React.js和React-Native的区别,只在于下文所述的ART获取上,然后该例子就可以同时应用在Web端和移动端上了。react-art自带的官方例子:Vector-Widget

Vector-Widget额外实现了旋转,以及鼠标点击事件的旋转加速响应。Web端可以看到点击加速,但是在移动端无效,原因是React Native并未对Group中onMouseDown和onMouseUp属性作处理。本文着重于静态svg的实现,暂时无视动画部分效果即可。

ART

在react native中ART是个非常重要的库,它让非常酷炫的绘图及动画变成了可能。需要注意的是,在React Native引入ART过程中,Android默认就包含ART库,IOS需要单独添加依赖库。

ios添加依赖库

1、使用xcode中打开React-native中的iOS项目,选中‘Libraries’目录 ——> 右键选择‘Add Files to 项目名称’ ——> ‘node_modules/react-native/Libraries/ART/ART.xcodeproj’ 添加; 
这里写图片描述

2、选中项目根目录 ——> 点击’Build Phases‘ ——> 点击‘Link Binary With Libraries’ ——> 点击左下方‘+’ ——> 选中‘libART.a’添加。

这里写图片描述

基础组件

ART暴露的组件共有7个,本文介绍常用的四个组件:Surface、Group、Shape、Text。

  • Surface - 一个矩形可渲染的区域,是其他元素的容器
  • Group - 可容纳多个形状、文本和其他的分组
  • Shape - 形状定义,可填充
  • Text - 文本形状定义

属性

Surface

  • width : 渲染区域的宽
  • height : 定义渲染区域的高

Shape

  • d : 定义绘制路径
  • stroke : 描边颜色
  • strokeWidth : 描边宽度
  • strokeDash : 定义虚线
  • fill : 填充颜色

Text

  • funt : 字体样式,定义字体、大小、是否加粗 如: bold 35px Heiti SC

Path

  • moveTo(x,y) : 移动到坐标(x,y)
  • lineTo(x,y) : 连线到(x,y)
  • arc() : 绘制弧线
  • close() : 封闭空间

代码示例

绘制直线

这里写图片描述

import React from 'react'import {    View,    ART} from 'react-native'export default class Line extends React.Component{    render(){        const path = ART.Path();        path.moveTo(1,1); //将起始点移动到(1,1) 默认(0,0)        path.lineTo(300,1); //连线到目标点(300,1)        return(            <View style={this.props.style}>                <ART.Surface width={300} height={2}>                    <ART.Shape d={path} stroke="#000000" strokeWidth={1} />                </ART.Surface>            </View>        )    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

绘制虚线

了解strokeDash的参数, 
[10,5] : 表示绘10像素实线在绘5像素空白,如此循环 
[10,5,20,5] : 表示绘10像素实线在绘制5像素空白在绘20像素实线及5像素空白

这里写图片描述

import React from 'react'import {    View,    ART} from 'react-native'const {Surface, Shape, Path} = ART;export default class DashLine extends React.Component{    render(){        const path = Path()            .moveTo(1,1)            .lineTo(300,1);        return(            <View style={this.props.style}>                <Surface width={300} height={2}>                    <Shape d={path} stroke="#000000" strokeWidth={2} strokeDash={[10,5]}/>                </Surface>            </View>        )    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

绘制矩形

首先通过lineTo绘制三条边,在使用close链接第四条边。fill做颜色填充. 
这里写图片描述

import React from 'react'import {    View,    ART} from 'react-native'const {Surface, Shape, Path} = ART;export default class Rect extends React.Component{    render(){        const path = new Path()            .moveTo(1,1)            .lineTo(1,99)            .lineTo(99,99)            .lineTo(99,1)            .close();        return(            <View style={this.props.style}>                <Surface width={100} height={100}>                    <Shape d={path} stroke="#000000" fill="#892265" strokeWidth={1} />                </Surface>            </View>        )    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

绘圆

了解arc(x,y,radius)的使用, 终点坐标距离起点坐标的相对距离。

这里写图片描述

import React from 'react'import {    View,    ART} from 'react-native'const {Surface, Shape, Path} = ART;export default class Circle extends React.Component{    render(){        const path = new Path()            .moveTo(50,1)            .arc(0,99,25)            .arc(0,-99,25)            .close();        return(            <View style={this.props.style}>                <Surface width={100} height={100}>                    <Shape d={path} stroke="#000000" strokeWidth={1}/>                </Surface>            </View>        )    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

绘制文字

了解funt属性的使用,规则是“粗细 字号 字体” 
注意: 字体应该是支持path属性的,应该是实现bug并没有不生效。 Android通过修改源码是可以解决的,IOS没看源码。

这里写图片描述

import React, {Component} from 'react';import {    AppRegistry,    StyleSheet,    ART,    View} from 'react-native';const {Surface, Text, Path} = ART;export default class ArtTextView extends Component {    render() {        return (            <View style={styles.container}>                <Surface width={100} height={100}>                    <Text strokeWidth={1} stroke="#000" font="bold 35px Heiti SC" path={new Path().moveTo(40,40).lineTo(99,10)} >React</Text>                </Surface>            </View>        );    }}const styles = StyleSheet.create({    container: {        flex: 1,        justifyContent: 'center',        alignItems: 'center',        backgroundColor: '#F5FCFF',    },});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

绘制扇形

这里写图片描述

在这里需要使用arc做路径绘制。 
Wedge.js

import React, { Component, PropTypes } from 'react';import { ART } from 'react-native';const { Shape, Path } = ART;/** * Wedge is a React component for drawing circles, wedges and arcs. Like other * ReactART components, it must be used in a <Surface>. */export default class Wedge extends Component<void, any, any> {    static propTypes = {        outerRadius: PropTypes.number.isRequired,        startAngle: PropTypes.number.isRequired,        endAngle: PropTypes.number.isRequired,        originX: PropTypes.number.isRequired,        originY: PropTypes.number.isRequired,        innerRadius: PropTypes.number,    };    constructor(props : any) {        super(props);        (this:any).circleRadians = Math.PI * 2;        (this:any).radiansPerDegree = Math.PI / 180;        (this:any)._degreesToRadians = this._degreesToRadians.bind(this);    }    /**     * _degreesToRadians(degrees)     *     * Helper function to convert degrees to radians     *     * @param {number} degrees     * @return {number}     */    _degreesToRadians(degrees : number) : number {        if (degrees !== 0 && degrees % 360 === 0) { // 360, 720, etc.            return (this:any).circleRadians;        }        return degrees * (this:any).radiansPerDegree % (this:any).circleRadians;    }    /**     * _createCirclePath(or, ir)     *     * Creates the ReactART Path for a complete circle.     *     * @param {number} or The outer radius of the circle     * @param {number} ir The inner radius, greater than zero for a ring     * @return {object}     */    _createCirclePath(or : number, ir : number) : Path {        const path = new Path();        path.move(0, or)            .arc(or * 2, 0, or)            .arc(-or * 2, 0, or);        if (ir) {            path.move(or - ir, 0)                .counterArc(ir * 2, 0, ir)                .counterArc(-ir * 2, 0, ir);        }        path.close();        return path;    }    /**     * _createArcPath(sa, ea, ca, or, ir)     *     * Creates the ReactART Path for an arc or wedge.     *     * @param {number} startAngle The starting degrees relative to 12 o'clock     * @param {number} endAngle The ending degrees relative to 12 o'clock     * @param {number} or The outer radius in pixels     * @param {number} ir The inner radius in pixels, greater than zero for an arc     * @return {object}     */    _createArcPath(originX : number, originY : number, startAngle : number, endAngle : number, or : number, ir : number) : Path {        const path = new Path();        // angles in radians        const sa = this._degreesToRadians(startAngle);        const ea = this._degreesToRadians(endAngle);        // central arc angle in radians        const ca = sa > ea ? (this:any).circleRadians - sa + ea : ea - sa;        // cached sine and cosine values        const ss = Math.sin(sa);        const es = Math.sin(ea);        const sc = Math.cos(sa);        const ec = Math.cos(ea);        // cached differences        const ds = es - ss;        const dc = ec - sc;        const dr = ir - or;        // if the angle is over pi radians (180 degrees)        // we will need to let the drawing method know.        const large = ca > Math.PI;        // TODO (sema) Please improve theses comments to make the math        // more understandable.        //        // Formula for a point on a circle at a specific angle with a center        // at (0, 0):        // x = radius * Math.sin(radians)        // y = radius * Math.cos(radians)        //        // For our starting point, we offset the formula using the outer        // radius because our origin is at (top, left).        // In typical web layout fashion, we are drawing in quadrant IV        // (a.k.a. Southeast) where x is positive and y is negative.        //        // The arguments for path.arc and path.counterArc used below are:        // (endX, endY, radiusX, radiusY, largeAngle)        path.move(or + or * ss, or - or * sc) // move to starting point            .arc(or * ds, or * -dc, or, or, large) // outer arc            .line(dr * es, dr * -ec);   // width of arc or wedge        if (ir) {            path.counterArc(ir * -ds, ir * dc, ir, ir, large); // inner arc        }        return path;    }    render() : any {        // angles are provided in degrees        const startAngle = this.props.startAngle;        const endAngle = this.props.endAngle;        // if (startAngle - endAngle === 0) {        //  return null;        // }        // radii are provided in pixels        const innerRadius = this.props.innerRadius || 0;        const outerRadius = this.props.outerRadius;        const { originX, originY } = this.props;        // sorted radii        const ir = Math.min(innerRadius, outerRadius);        const or = Math.max(innerRadius, outerRadius);        let path;        if (endAngle >= startAngle + 360) {            path = this._createCirclePath(or, ir);        } else {            path = this._createArcPath(originX, originY, startAngle, endAngle, or, ir);        }        return <Shape {...this.props} d={path} />;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160

示例代码:

import React from 'react'import {    View,    ART} from  'react-native'const {Surface} = ART;import Wedge from './Wedge'export default class Fan extends  React.Component{    render(){        return(            <View style={this.props.style}>                <Surface width={100} height={100}>                    <Wedge                     outerRadius={50}                     startAngle={0}                     endAngle={60}                     originX={50}                     originY={50}                     fill="blue"/>                </Surface>            </View>        )    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

综合示例

这里写图片描述

相关代码:

/** * Sample React Native App * https://github.com/facebook/react-native * @flow */import React, {    Component}from 'react';import {    ART as Art,    StyleSheet,    View,    Dimensions,    TouchableWithoutFeedback,    Animated} from 'react-native';var HEART_SVG = "M130.4-0.8c25.4 0 46 20.6 46 46.1 0 13.1-5.5 24.9-14.2 33.3L88 153.6 12.5 77.3c-7.9-8.3-12.8-19.6-12.8-31.9 0-25.5 20.6-46.1 46-46.2 19.1 0 35.5 11.7 42.4 28.4C94.9 11 111.3-0.8 130.4-0.8"var HEART_COLOR = 'rgb(226,38,77,1)';var GRAY_HEART_COLOR = "rgb(204,204,204,1)";var FILL_COLORS = [    'rgba(221,70,136,1)',    'rgba(212,106,191,1)',    'rgba(204,142,245,1)',    'rgba(204,142,245,1)',    'rgba(204,142,245,1)',    'rgba(0,0,0,0)'];var PARTICLE_COLORS = [    'rgb(158, 202, 250)',    'rgb(161, 235, 206)',    'rgb(208, 148, 246)',    'rgb(244, 141, 166)',    'rgb(234, 171, 104)',    'rgb(170, 163, 186)']getXYParticle = (total, i, radius) => {    var angle = ( (2 * Math.PI) / total ) * i;    var x = Math.round((radius * 2) * Math.cos(angle - (Math.PI / 2)));    var y = Math.round((radius * 2) * Math.sin(angle - (Math.PI / 2)));    return {        x: x,        y: y,    }}getRandomInt = (min, max) => {    return Math.floor(Math.random() * (max - min)) + min;}shuffleArray = (array) => {    for (var i = array.length - 1; i > 0; i--) {        var j = Math.floor(Math.random() * (i + 1));        var temp = array[i];        array[i] = array[j];        array[j] = temp;    }    return array;}var {    Surface,    Group,    Shape,    Path} = Art;//使用Animated.createAnimatedComponent对其他组件创建对话//创建一个灰色的新型图片var AnimatedShape = Animated.createAnimatedComponent(Shape);var {    width: deviceWidth,    height: deviceHeight} = Dimensions.get('window');export default  class ArtAnimView extends Component {    constructor(props) {        super(props);        this.state = {            animation: new Animated.Value(0)        };    }    explode = () => {        Animated.timing(this.state.animation, {            duration: 1500,            toValue: 28        }).start(() => {            this.state.animation.setValue(0);            this.forceUpdate();        });    }    getSmallExplosions = (radius, offset) => {        return [0, 1, 2, 3, 4, 5, 6].map((v, i, t) => {            var scaleOut = this.state.animation.interpolate({                inputRange: [0, 5.99, 6, 13.99, 14, 21],                outputRange: [0, 0, 1, 1, 1, 0],                extrapolate: 'clamp'            });            var moveUp = this.state.animation.interpolate({                inputRange: [0, 5.99, 14],                outputRange: [0, 0, -15],                extrapolate: 'clamp'            });            var moveDown = this.state.animation.interpolate({                inputRange: [0, 5.99, 14],                outputRange: [0, 0, 15],                extrapolate: 'clamp'            });            var color_top_particle = this.state.animation.interpolate({                inputRange: [6, 8, 10, 12, 17, 21],                outputRange: shuffleArray(PARTICLE_COLORS)            })            var color_bottom_particle = this.state.animation.interpolate({                inputRange: [6, 8, 10, 12, 17, 21],                outputRange: shuffleArray(PARTICLE_COLORS)            })            var position = getXYParticle(7, i, radius)            return (                <Group                    x={position.x + offset.x }                    y={position.y + offset.y}                    rotation={getRandomInt(0, 40) * i}                >                    <AnimatedCircle                        x={moveUp}                        y={moveUp}                        radius={15}                        scale={scaleOut}                        fill={color_top_particle}                    />                    <AnimatedCircle                        x={moveDown}                        y={moveDown}                        radius={8}                        scale={scaleOut}                        fill={color_bottom_particle}                    />                </Group>            )        }, this)    }    render() {        var heart_scale = this.state.animation.interpolate({            inputRange: [0, .01, 6, 10, 12, 18, 28],            outputRange: [1, 0, .1, 1, 1.2, 1, 1],            extrapolate: 'clamp'        });        var heart_fill = this.state.animation.interpolate({            inputRange: [0, 2],            outputRange: [GRAY_HEART_COLOR, HEART_COLOR],            extrapolate: 'clamp'        })        var heart_x = heart_scale.interpolate({            inputRange: [0, 1],            outputRange: [90, 0],        })        var heart_y = heart_scale.interpolate({            inputRange: [0, 1],            outputRange: [75, 0],        })        var circle_scale = this.state.animation.interpolate({            inputRange: [0, 1, 4],            outputRange: [0, .3, 1],            extrapolate: 'clamp'        });        var circle_stroke_width = this.state.animation.interpolate({            inputRange: [0, 5.99, 6, 7, 10],            outputRange: [0, 0, 15, 8, 0],            extrapolate: 'clamp'        });        var circle_fill_colors = this.state.animation.interpolate({            inputRange: [1, 2, 3, 4, 4.99, 5],            outputRange: FILL_COLORS,            extrapolate: 'clamp'        })        var circle_opacity = this.state.animation.interpolate({            inputRange: [1, 9.99, 10],            outputRange: [1, 1, 0],            extrapolate: 'clamp'        })        return (            <View style={styles.container}>                <TouchableWithoutFeedback onPress={this.explode} style={styles.container}>                    <View style={{transform: [{scale: .8}]}}>                        <Surface width={deviceWidth} height={deviceHeight}>                            <Group x={75} y={200}>                                <AnimatedShape                                    d={HEART_SVG}                                    x={heart_x}                                    y={heart_y}                                    scale={heart_scale}                                    fill={heart_fill}                                />                                <AnimatedCircle                                    x={89}                                    y={75}                                    radius={150}                                    scale={circle_scale}                                    strokeWidth={circle_stroke_width}                                    stroke={FILL_COLORS[2]}                                    fill={circle_fill_colors}                                    opacity={circle_opacity}                                />                                {this.getSmallExplosions(75, {x: 89, y: 75})}                            </Group>                        </Surface>                    </View>                </TouchableWithoutFeedback>            </View>        );    }};class AnimatedCircle extends Component {    render() {        var radius = this.props.radius;        var path = Path().moveTo(0, -radius)            .arc(0, radius * 2, radius)            .arc(0, radius * -2, radius)            .close();        return React.createElement(AnimatedShape);    }}http://blog.csdn.net/xiangzhihong8/article/details/76572405var styles = StyleSheet.create({    container: {        flex: 1,    }});