首頁  >  文章  >  web前端  >  關於react-native之ART繪圖的方法

關於react-native之ART繪圖的方法

不言
不言原創
2018-06-30 11:57:082213瀏覽

本篇文章主要介紹了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' 新增;

關於react-native之ART繪圖的方法

2、選取專案根目錄— —> 點選'Build Phases' ——> 點選'Link Binary With Libraries' ——> 點選左下方' ' ——> 選取'libART.a'新增。

關於react-native之ART繪圖的方法

基礎元件

ART暴露的元件共有7個,本文介紹常用的四個元件:Surface、Group、Shape 、Text。

  • Surface - 一個矩形可渲染的區域,是其他元素的容器

  • Group - 可容納多個形狀、文字和其他的分組

  • Shape - 形狀定義,可填入

  • 文字 - 文字形狀定義

屬性

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() : 封閉空間

程式碼範例

繪製直線

關於react-native之ART繪圖的方法

#
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像素空白

關於react-native之ART繪圖的方法

import React from &#39;react&#39;
import {
  View,
  ART
} from &#39;react-native&#39;

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做色彩填滿.

關於react-native之ART繪圖的方法

import React from &#39;react&#39;
import {
  View,
  ART
} from &#39;react-native&#39;

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)的使用, 终点坐标距离起点坐标的相对距离。

關於react-native之ART繪圖的方法

import React from &#39;react&#39;
import {
  View,
  ART
} from &#39;react-native&#39;

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没看源码。

關於react-native之ART繪圖的方法

import React, {Component} from &#39;react&#39;;
import {
  AppRegistry,
  StyleSheet,
  ART,
  View
} from &#39;react-native&#39;;

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: &#39;center&#39;,
    alignItems: &#39;center&#39;,
    backgroundColor: &#39;#F5FCFF&#39;,
  },

});

绘制扇形

關於react-native之ART繪圖的方法

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

Wedge.js

import React, { Component, PropTypes } from &#39;react&#39;;
import { ART } from &#39;react-native&#39;;
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&#39;clock
   * @param {number} endAngle The ending degrees relative to 12 o&#39;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 &#39;react&#39;
import {
  View,
  ART
} from &#39;react-native&#39;

const {Surface} = ART;
import Wedge from &#39;./Wedge&#39;

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>
    )
  }
}

综合示例

關於react-native之ART繪圖的方法

相关代码:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, {
  Component
}from &#39;react&#39;;
import {
  ART as Art,
  StyleSheet,
  View,
  Dimensions,
  TouchableWithoutFeedback,
  Animated
} from &#39;react-native&#39;;

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 = &#39;rgb(226,38,77,1)&#39;;
var GRAY_HEART_COLOR = "rgb(204,204,204,1)";

var FILL_COLORS = [
  &#39;rgba(221,70,136,1)&#39;,
  &#39;rgba(212,106,191,1)&#39;,
  &#39;rgba(204,142,245,1)&#39;,
  &#39;rgba(204,142,245,1)&#39;,
  &#39;rgba(204,142,245,1)&#39;,
  &#39;rgba(0,0,0,0)&#39;
];

var PARTICLE_COLORS = [
  &#39;rgb(158, 202, 250)&#39;,
  &#39;rgb(161, 235, 206)&#39;,
  &#39;rgb(208, 148, 246)&#39;,
  &#39;rgb(244, 141, 166)&#39;,
  &#39;rgb(234, 171, 104)&#39;,
  &#39;rgb(170, 163, 186)&#39;
]

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(&#39;window&#39;);

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: &#39;clamp&#39;
      });

      var moveUp = this.state.animation.interpolate({
        inputRange: [0, 5.99, 14],
        outputRange: [0, 0, -15],
        extrapolate: &#39;clamp&#39;
      });

      var moveDown = this.state.animation.interpolate({
        inputRange: [0, 5.99, 14],
        outputRange: [0, 0, 15],
        extrapolate: &#39;clamp&#39;
      });

      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: &#39;clamp&#39;
    });

    var heart_fill = this.state.animation.interpolate({
      inputRange: [0, 2],
      outputRange: [GRAY_HEART_COLOR, HEART_COLOR],
      extrapolate: &#39;clamp&#39;
    })

    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: &#39;clamp&#39;
    });

    var circle_stroke_width = this.state.animation.interpolate({
      inputRange: [0, 5.99, 6, 7, 10],
      outputRange: [0, 0, 15, 8, 0],
      extrapolate: &#39;clamp&#39;
    });

    var circle_fill_colors = this.state.animation.interpolate({
      inputRange: [1, 2, 3, 4, 4.99, 5],
      outputRange: FILL_COLORS,
      extrapolate: &#39;clamp&#39;
    })

    var circle_opacity = this.state.animation.interpolate({
      inputRange: [1, 9.99, 10],
      outputRange: [1, 1, 0],
      extrapolate: &#39;clamp&#39;
    })


    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中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn