react-native-art path代码解析

React-Native-ART代码解析

一、探寻源码

1.如何使用

安卓自己集成,不需要额外操作,iOS需要pod添加ART库,如下:

pod 'React', :path => '../rn-source', :subspecs => [  
    'Core',  
    'RCTActionSheet',  
    'RCTText',  
    'RCTImage',  
    'ART',  
    # needed for debugging  
    # Add any other subspecs you want to use in your project  
    ]  

2.js中的使用

import { ART } from 'react-native'
const { Surface, Shape, Path, Group } = ART;

这样我们就能够使用ART的组件,总共有10个。部分组件的具体使用方法可以参考我之前写的。

3.寻找ART的源码

首先我们知道ART是在react-native 中引用的,所以我们到node_modules下找到react-native 文件夹,如图:
react-native目录
到这里,我们首先要看一下package.json 文件,这是react-native 所用的组件列表,如图:
package.json
我们可以看到react-native引用了一个库叫做art,但这里是小写的,应该是ART所用的依赖,还不确定是我们要找到ART库在哪里,我们继续找。
接下来,我们来看看Libraries文件夹,这里通常放着组件,这里我们看到了大写的ART,如图:
ReactNativeArt
在这里我们可以看到大多都是原生代码,在ReactNativeART.js中export出了一个组件ReactART,这个是我们要找的ART吗?,现在还不能确定。
我们继续看,在Libraries下我们找到react-native文件夹,可以看到react-native.js就静静的躺在这里,这个应该就是整个react-native组件的入口位置,在这里我们看到:

get ART() { return require('ReactNativeART'); },

现在,我们可以确定ReactNativeART就是我们要找的ART。

二、ART库源码

让我们回过头来看看ReactNativeART的入口文件(也就是Libraries下的ART文件夹下的 ReactNativeART.js),拉到下面我们可以看到:

var ReactART = {
  LinearGradient: LinearGradient,
  RadialGradient: RadialGradient,
  Pattern: Pattern,
  Transform: Transform,
  Path: Path,
  Surface: Surface,
  Group: Group,
  ClippingRectangle: ClippingRectangle,
  Shape: Shape,
  Text: Text,
};

这个就是ART库输出的所有组件了,我们就可以从这里开始阅读代码,我们可以看到:

var Path = require('ARTSerializablePath');
var Transform = require('art/core/transform');
// Native Components

var NativeSurfaceView = createReactNativeComponentClass({
  validAttributes: SurfaceViewAttributes,
  uiViewClassName: 'ARTSurfaceView',
});

var NativeGroup = createReactNativeComponentClass({
  validAttributes: GroupAttributes,
  uiViewClassName: 'ARTGroup',
});

var NativeShape = createReactNativeComponentClass({
  validAttributes: ShapeAttributes,
  uiViewClassName: 'ARTShape',
});

var NativeText = createReactNativeComponentClass({
  validAttributes: TextAttributes,
  uiViewClassName: 'ARTText',
});
function LinearGradient  ...
function RadialGradient  ...
function Pattern  ...
class ClippingRectangle extends React.Component {
  render() {
    var props = this.props;
    var x = extractNumber(props.x, 0);
    var y = extractNumber(props.y, 0);
    var w = extractNumber(props.width, 0);
    var h = extractNumber(props.height, 0);
    var clipping = [x, y, w, h];
    // The current clipping API requires x and y to be ignored in the transform
    var propsExcludingXAndY = merge(props);
    delete propsExcludingXAndY.x;
    delete propsExcludingXAndY.y;
    return (
      <NativeGroup
        clipping={clipping}
        opacity={extractOpacity(props)}
        transform={extractTransform(propsExcludingXAndY)}>
        {this.props.children}
      </NativeGroup>
    );
  }
}

可以看到,输出的10个组件这里都能看到出处,那到这里,其实我们关心那部分代码,就可以具体看哪里了,源码在这里其实已经暴露无疑了。

这部分的结论:

  1. surface,shape,group,text都是原生组件,
  2. ClippingRectangle是封装的一层group,
  3. LinearGradient,RadialGradient,Pattern分别是3个func,
  4. 而path和Transform则是引用的art文件,在上面我们已经看到了react-native的package.json引用了art组件。

三、ART里的path源码

上面我们看到

var Path = require('ARTSerializablePath');

ARTSerializablePath.js中,我们又能看到

/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ARTSerializablePath
 */
'use strict';

// TODO: Move this into an ART mode called "serialized" or something

var Class = require('art/core/class.js');
var Path = require('art/core/path.js');

var MOVE_TO = 0;
var CLOSE = 1;
var LINE_TO = 2;
var CURVE_TO = 3;
var ARC = 4;

var SerializablePath = Class(Path, {

  initialize: function(path) {
    this.reset();
    if (path instanceof SerializablePath) {
      this.path = path.path.slice(0);
    } else if (path) {
      if (path.applyToPath) {
        path.applyToPath(this);
      } else {
        this.push(path);
      }
    }
  },

  onReset: function() {
    this.path = [];
  },

  onMove: function(sx, sy, x, y) {
    this.path.push(MOVE_TO, x, y);
  },

  onLine: function(sx, sy, x, y) {
    this.path.push(LINE_TO, x, y);
  },

  onBezierCurve: function(sx, sy, p1x, p1y, p2x, p2y, x, y) {
    this.path.push(CURVE_TO, p1x, p1y, p2x, p2y, x, y);
  },

  _arcToBezier: Path.prototype.onArc,

  onArc: function(sx, sy, ex, ey, cx, cy, rx, ry, sa, ea, ccw, rotation) {
    if (rx !== ry || rotation) {
      return this._arcToBezier(
        sx, sy, ex, ey, cx, cy, rx, ry, sa, ea, ccw, rotation
      );
    }
    this.path.push(ARC, cx, cy, rx, sa, ea, ccw ? 0 : 1);
  },

  onClose: function() {
    this.path.push(CLOSE);
  },

  toJSON: function() {
    return this.path;
  }

});

module.exports = SerializablePath;

说明我们的path直接就使用的art/core/path.js 文件,而看这里的实现,全部使用的是this.path.push()+参数的方法,我们接下来去看下art/core/path.js 的push方法,如下:

push: function(){
        var p = Array.prototype.join.call(arguments, ' ')
            .match(/[a-df-z]|[\-+]?(?:[\d\.]e[\-+]?|[^\s\-+,a-z])+/ig);
        if (!p) return this;

        var last, cmd = p[0], i = 1;
        while (cmd){
            switch (cmd){
                case 'm': this.move(p[i++], p[i++]); break;
                case 'l': this.line(p[i++], p[i++]); break;
                case 'c': this.curve(p[i++], p[i++], p[i++], p[i++], p[i++], p[i++]); break;
                case 's': this.curve(p[i++], p[i++], null, null, p[i++], p[i++]); break;
                case 'q': this.curve(p[i++], p[i++], p[i++], p[i++]); break;
                case 't': this.curve(p[i++], p[i++]); break;
                case 'a': this.arc(p[i+5], p[i+6], p[i], p[i+1], p[i+3], !+p[i+4], p[i+2]); i += 7; break;
                case 'h': this.line(p[i++], 0); break;
                case 'v': this.line(0, p[i++]); break;

                case 'M': this.moveTo(p[i++], p[i++]); break;
                case 'L': this.lineTo(p[i++], p[i++]); break;
                case 'C': this.curveTo(p[i++], p[i++], p[i++], p[i++], p[i++], p[i++]); break;
                case 'S': this.curveTo(p[i++], p[i++], null, null, p[i++], p[i++]); break;
                case 'Q': this.curveTo(p[i++], p[i++], p[i++], p[i++]); break;
                case 'T': this.curveTo(p[i++], p[i++]); break;
                case 'A': this.arcTo(p[i+5], p[i+6], p[i], p[i+1], p[i+3], !+p[i+4], p[i+2]); i += 7; break;
                case 'H': this.lineTo(p[i++], this.penY); break;
                case 'V': this.lineTo(this.penX, p[i++]); break;

                case 'Z': case 'z': this.close(); break;
                default: cmd = last; i--; continue;
            }

            last = cmd;
            if (last == 'm') last = 'l';
            else if (last == 'M') last = 'L';
            cmd = p[i++];
        }
        return this;
    },

可以看到svg的方法,说明我们其实是可以直接使用push做svg的实现的,

命令 名称 参数
M moveto 移动到 (x y)+
Z closepath 关闭路径 (none)
L lineto 画线到 (x y)+
H horizontal lineto 水平线到 x+
V vertical lineto 垂直线到 y+
C curveto 三次贝塞尔曲线到 (x1 y1 x2 y2 x y)+
S smooth curveto 光滑三次贝塞尔曲线到 (x2 y2 x y)+
Q quadratic Bézier curveto 二次贝塞尔曲线到 (x1 y1 x y)+
T smooth quadratic Bézier curveto 光滑二次贝塞尔曲线到 (x y)+
A elliptical arc 椭圆弧 (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+
R Catmull-Rom curveto* Catmull-Rom曲线 x1 y1 (x y)+

其实这里我有个疑虑,并不了解ARTSerializablePath.js 中调用push的原理,貌似指定了0-4的参数,

var MOVE_TO = 0;
var CLOSE = 1;
var LINE_TO = 2;
var CURVE_TO = 3;
var ARC = 4;

就可以调用相应的方法函数,还望看的懂的同学说明一下,
这里我们发现ART是可以使用SVG函数的,所以SVG的能力,我们在这里可以直接使用,比如画内塞尔曲线等等,例如:

_path = new Path("M0 0 Q5 20……");

四、Android与iOS使用ART的不同

  1. 渐变色(安卓不能渐变)
  2. 画扇形图(安卓绘制方式不同)

这里说一下画扇形,iOS直接使用ART提供的path根据我之前写的使用方法就可以画出来想要的图案,但是安卓却不能,这里可以使用更底层的push方法代替,例如画扇形:

if (Platform.OS === 'ios') {
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
} else {
path.path.push(ARC, CIRCLE_X, CIRCLE_Y, RX, startAngle / 360 * TwoPI, (startAngle / 360 * TwoPI) - ((endAngle - startAngle) / 360 * TwoPI), 0)
}

通过区分平台,再具体实现上使用不同的方法,达到相同的效果。

猜你喜欢

转载自blog.csdn.net/zramals/article/details/74231526
今日推荐