首页 > web前端 > js教程 > 正文

关于react-native之ART绘图的方法

不言
发布: 2018-06-30 11:57:08
原创
2399人浏览过

本篇文章主要介绍了react-native之art绘图方法详解,内容挺不错的,现在分享给大家,也给大家做个参考。

背景

在移动应用的开发过程中,绘制基本的二维图形或动画是必不可少的。然而,考虑到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() : 封闭空间

代码示例

绘制直线

这里写图片描述

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

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>

    )

  }

}

登录后复制

绘制虚线

了解strokeDash的参数,

[10,5] : 表示绘10像素实线在绘5像素空白,如此循环

[10,5,20,5] : 表示绘10像素实线在绘制5像素空白在绘20像素实线及5像素空白

这里写图片描述

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

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>

    )

  }

}

登录后复制

绘制矩形

首先通过lineTo绘制三条边,在使用close链接第四条边。fill做颜色填充.

这里写图片描述

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

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>

    )

  }

}

登录后复制

绘圆

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

这里写图片描述

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

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>

    )

  }

}

登录后复制

绘制文字

了解funt属性的使用,规则是“粗细 字号 字体”

注意: 字体应该是支持path属性的,应该是实现bug并没有不生效。 Android通过修改源码是可以解决的,IOS没看源码。

这里写图片描述

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

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',

  },

 

});

登录后复制

绘制扇形

这里写图片描述

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

Wedge.js

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, { 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

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

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

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

/**

 * 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);

  }

}

 

var styles = StyleSheet.create({

  container: {

    flex: 1,

  }

});

登录后复制

以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!

相关推荐:

node.js 利用流实现读写同步,边读边写的功能

angular2和nodejs实现图片上传的的的功能

以上就是关于react-native之ART绘图的方法的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号