您好,登錄后才能下訂單哦!
小編給大家分享一下react-native之ART繪圖的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
背景
在移動(dòng)應(yīng)用的開(kāi)發(fā)過(guò)程中,繪制基本的二維圖形或動(dòng)畫(huà)是必不可少的。然而,考慮到Android和iOS均有一套各自的API方案,因此采用一種更普遍接受的技術(shù)方案,更有利于代碼的雙平臺(tái)兼容。
art是一個(gè)旨在多瀏覽器兼容的Node style CommonJS模塊。在它的基礎(chǔ)上,F(xiàn)acebook又開(kāi)發(fā)了React-art ,封裝art,使之可以被react.js所使用,即實(shí)現(xiàn)了前端的svg庫(kù)。然而,考慮到react.js的JSX語(yǔ)法,已經(jīng)支持將 等等svg標(biāo)簽直接插入到dom中(當(dāng)然此時(shí)使用的就不是react-art庫(kù)了)此外還有HTML canvas的存在,因此,在前端上,react-art并非不可替代。
然而,在移動(dòng)端,考慮到跨平臺(tái)的需求,加之web端的技術(shù)積累,react-art成為了現(xiàn)成的繪制圖形的解決方案。react-native分別在0.10.0和0.18.0上添加了ios和android平臺(tái)上對(duì)react-art的支持。
示例代碼
React.js和React-Native的區(qū)別,只在于下文所述的ART獲取上,然后該例子就可以同時(shí)應(yīng)用在Web端和移動(dòng)端上了。react-art自帶的官方例子:Vector-Widget
Vector-Widget額外實(shí)現(xiàn)了旋轉(zhuǎn),以及鼠標(biāo)點(diǎn)擊事件的旋轉(zhuǎn)加速響應(yīng)。Web端可以看到點(diǎn)擊加速,但是在移動(dòng)端無(wú)效,原因是React Native并未對(duì)Group中onMouseDown和onMouseUp屬性作處理。本文著重于靜態(tài)svg的實(shí)現(xiàn),暫時(shí)無(wú)視動(dòng)畫(huà)部分效果即可。
ART
在react native中ART是個(gè)非常重要的庫(kù),它讓非??犰诺睦L圖及動(dòng)畫(huà)變成了可能。需要注意的是,在React Native引入ART過(guò)程中,Android默認(rèn)就包含ART庫(kù),IOS需要單獨(dú)添加依賴庫(kù)。
ios添加依賴庫(kù)
1、使用xcode中打開(kāi)React-native中的iOS項(xiàng)目,選中‘Libraries'目錄 ——> 右鍵選擇‘Add Files to 項(xiàng)目名稱' ——> ‘node_modules/react-native/Libraries/ART/ART.xcodeproj' 添加;
2、選中項(xiàng)目根目錄 ——> 點(diǎn)擊'Build Phases‘ ——> 點(diǎn)擊‘Link Binary With Libraries' ——> 點(diǎn)擊左下方‘+' ——> 選中‘libART.a'添加。
基礎(chǔ)組件
ART暴露的組件共有7個(gè),本文介紹常用的四個(gè)組件:Surface、Group、Shape、Text。
Surface - 一個(gè)矩形可渲染的區(qū)域,是其他元素的容器
Group - 可容納多個(gè)形狀、文本和其他的分組
Shape - 形狀定義,可填充
Text - 文本形狀定義
屬性
Surface
width : 渲染區(qū)域的寬
height : 定義渲染區(qū)域的高
Shape
d : 定義繪制路徑
stroke : 描邊顏色
strokeWidth : 描邊寬度
strokeDash : 定義虛線
fill : 填充顏色
Text
funt : 字體樣式,定義字體、大小、是否加粗 如: bold 35px Heiti SC
Path
moveTo(x,y) : 移動(dòng)到坐標(biāo)(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); //將起始點(diǎn)移動(dòng)到(1,1) 默認(rèn)(0,0) path.lineTo(300,1); //連線到目標(biāo)點(diǎn)(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的參數(shù),
[10,5] : 表示繪10像素實(shí)線在繪5像素空白,如此循環(huán)
[10,5,20,5] : 表示繪10像素實(shí)線在繪制5像素空白在繪20像素實(shí)線及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> ) } }
繪制矩形
首先通過(guò)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> ) } }
繪圓
了解arc(x,y,radius)的使用, 終點(diǎn)坐標(biāo)距離起點(diǎn)坐標(biāo)的相對(duì)距離。
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屬性的使用,規(guī)則是“粗細(xì) 字號(hào) 字體”
注意: 字體應(yīng)該是支持path屬性的,應(yīng)該是實(shí)現(xiàn)bug并沒(méi)有不生效。 Android通過(guò)修改源碼是可以解決的,IOS沒(méi)看源碼。
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
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} />; } }
示例代碼:
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> ) } }
綜合示例
相關(guān)代碼:
/** * 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對(duì)其他組件創(chuàng)建對(duì)話 //創(chuàng)建一個(gè)灰色的新型圖片 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, } });
以上是“react-native之ART繪圖的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。