成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

探究 React Native 中 Props 驅(qū)動的 SVG 動畫和 Value 驅(qū)動動畫

Codeing_ls / 2521人閱讀

摘要:再來看前端,前端的動畫實(shí)現(xiàn),經(jīng)過多年的發(fā)展,已分為動畫和動畫。此外還探究了驅(qū)動動畫在實(shí)現(xiàn)方法上的不同之處。驅(qū)動的動畫接下來看驅(qū)動的動畫。改造后的函數(shù)如下關(guān)鍵修改,強(qiáng)制刷新。對于,函數(shù)是可行的,然而對無效。

引言

一直以來,動畫都是移動開發(fā)中極為特殊的一塊。一方面,動畫在交互體驗(yàn)上有著不可替代的優(yōu)越處,然而另一方面,動畫的開發(fā)又極為的耗時(shí),需要消耗工程師大量的時(shí)間用于開發(fā)和調(diào)試。再來看前端,前端的動畫實(shí)現(xiàn),經(jīng)過多年的發(fā)展,已分為 CSS3 動畫和 JavaScript 動畫。

React Native 作為一個(gè)復(fù)用前端思想的移動開發(fā)框架,并沒有完整實(shí)現(xiàn)CSS,而是使用JavaScript來給應(yīng)用添加樣式。這是一個(gè)有爭議的決定,可以參考這個(gè)幻燈片來了解 Facebook 做的理由。自然,在動畫上,因?yàn)槿鄙俅罅康?CSS 屬性,React Naive 中的動畫均為 JavaScript 動畫,即通過 JavaScript 代碼控制圖像的各種參數(shù)值的變化,從而產(chǎn)生時(shí)間軸上的動畫效果。

React Native 的官方文檔已經(jīng)詳細(xì)地介紹了 React Native 一般動畫的使用方法和實(shí)例,在此不再贅述。然而閱讀官方文檔后可知,官方的動畫往往是給一個(gè)完整的物體添加各種動畫效果,如透明度,翻轉(zhuǎn),移動等等。但是對于物體的自身變化,比如如下這個(gè)進(jìn)度條,明顯是在旋轉(zhuǎn)的同時(shí)也在伸縮,則缺乏必要的實(shí)現(xiàn)方法。這是因?yàn)?,動畫的本質(zhì)既是圖形的各種參數(shù)的數(shù)值變化的過程,文檔中的 Animated.Value 就是用作被驅(qū)動的參數(shù),可以,想要讓一個(gè)圓環(huán)能夠伸縮,就必須讓數(shù)值變化的過程,深入到圖形生成的過程中,而不是如官方文檔的例子一樣,僅僅是施加于圖形生成完畢后的過程,那么也就無法實(shí)現(xiàn)改變圖形自身的動畫效果了。

拙作初窺基于 react-art 庫的 React Native SVG已討論了 React Native 中靜態(tài) SVG 的開發(fā)方法,本文則致力于探究 React Native 中 SVG 與 Animation 結(jié)合所實(shí)現(xiàn)的 SVG 動畫。也就是可以改變圖形自身的動畫效果。此外還探究了 Value 驅(qū)動動畫在實(shí)現(xiàn)方法上的不同之處。

Props 驅(qū)動的 SVG 動畫

本節(jié)即以實(shí)現(xiàn)一個(gè)下圖所示的旋轉(zhuǎn)的進(jìn)度條的例子,講述 React Native SVG 動畫的開發(fā)方法。

Wedge.art.js 位于 react-art 庫下 lib/ 文件夾內(nèi),提供了 SVG 扇形的實(shí)現(xiàn),然而缺乏對 cx, cy 屬性的支持。另外拙作之前也提到了,Wedge中的扇形較為詭異,只有一條半徑,為了實(shí)現(xiàn)進(jìn)度條效果我把另一條半徑也去掉了。我將 Wedge.art.js 拷貝到工程中,自行小修改后的代碼如下。

// wedge.js

/**
 * Copyright 2013-2014 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 Wedge.art
 * @typechecks
 *
 * Example usage:
 * 
 *
 * Additional optional property:
 *   (Int) innerRadius
 *
 */
"use strict";
var React = require("react-native");
var ReactART = React.ART;

var $__0 =  React,PropTypes = $__0.PropTypes;
var Shape = ReactART.Shape;
var Path = ReactART.Path;

/**
 * Wedge is a React component for drawing circles, wedges and arcs.  Like other
 * ReactART components, it must be used in a .
 */
var Wedge = React.createClass({displayName: "Wedge",

  propTypes: {
    outerRadius: PropTypes.number.isRequired,
    startAngle: PropTypes.number.isRequired,
    endAngle: PropTypes.number.isRequired,
    innerRadius: PropTypes.number,
    cx: PropTypes.number,
    cy: PropTypes.number
  },

  circleRadians: Math.PI * 2,

  radiansPerDegree: Math.PI / 180,

  /**
   * _degreesToRadians(degrees)
   *
   * Helper function to convert degrees to radians
   *
   * @param {number} degrees
   * @return {number}
   */
  _degreesToRadians: function(degrees) {
    if (degrees !== 0 && degrees % 360 === 0) { // 360, 720, etc.
      return this.circleRadians;
    } else {
      return degrees * this.radiansPerDegree % this.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: function(or, ir) {
    var path = Path();

    path.move(this.props.cx, or + this.props.cy)
        .arc(or * 2, 0, or)
        .arc(-or * 2, 0, or);

    if (ir) {
      path.move(this.props.cx + or - ir, this.props.cy)
          .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: function(startAngle, endAngle, or, ir) {
      var path = Path();

      // angles in radians
      var sa = this._degreesToRadians(startAngle);
      var ea = this._degreesToRadians(endAngle);

      // central arc angle in radians
      var ca = sa > ea ? this.circleRadians - sa + ea : ea - sa;

      // cached sine and cosine values
      var ss = Math.sin(sa);
      var es = Math.sin(ea);
      var sc = Math.cos(sa);
      var ec = Math.cos(ea);

      // cached differences
      var ds = es - ss;
      var dc = ec - sc;
      var dr = ir - or;

      // if the angle is over pi radians (180 degrees)
      // we will need to let the drawing method know.
      var 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 + this.props.cx, or - or * sc + this.props.cy) // 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: function() {
    // angles are provided in degrees
    var startAngle = this.props.startAngle;
    var endAngle = this.props.endAngle;
    if (startAngle - endAngle === 0) {
      return;
    }

    // radii are provided in pixels
    var innerRadius = this.props.innerRadius || 0;
    var outerRadius = this.props.outerRadius;

    // sorted radii
    var ir = Math.min(innerRadius, outerRadius);
    var or = Math.max(innerRadius, outerRadius);

    var path;
    if (endAngle >= startAngle + 360) {
      path = this._createCirclePath(or, ir);
    } else {
      path = this._createArcPath(startAngle, endAngle, or, ir);
    }

    return React.createElement(Shape, React.__spread({},  this.props, {d: path}));
  }

});

module.exports = Wedge;

然后就是實(shí)現(xiàn)的主體。其中值得關(guān)注的點(diǎn)是:

并非任何 Component 都可以直接用 Animated.Value 去賦值 Props,而需要對 Component 做一定的改造。Animated.createAnimatedComponent(Component component),是 Animated 庫提供的用于把普通 Component 改造為 AnimatedComponent 的函數(shù)。閱讀 React Native 源代碼會發(fā)現(xiàn),Animated.Text, Animated.View, Animated.Image,都是直接調(diào)用了該函數(shù)去改造系統(tǒng)已有的組件,如Animated.createAnimatedComponent(React.Text)。

Easing 庫較為隱蔽,明明在react-native/Library/Animated/路徑下,卻又需要從React中直接引出。它為動畫的實(shí)現(xiàn)提供了許多緩動函數(shù),可根據(jù)實(shí)際需求選擇。如 linear() 線性,quad() 二次(quad明明是四次方的意思,為毛代碼實(shí)現(xiàn)是t*t....),cubic() 三次等等。官方文檔中吹噓 Easing 中提供了 tons of functions(成噸的函數(shù)),然而我數(shù)過了明明才14個(gè),233333。

該動畫由起始角度和終止角度兩個(gè)變化的參數(shù)來控制,因此,兩個(gè)Animated.Value需要同時(shí)啟動,這涉及到了動畫的組合問題。React Native 為此提供了 parallelsequence,staggerdelay 四個(gè)函數(shù)。其主要實(shí)現(xiàn)均可在react-native/Library/Animated/Animate中找到,官方文檔中亦有說明。這里用的是Animated.parallel。

開發(fā)中遇到的問題有:

該動畫在 Android 上可以運(yùn)行,但是刷新頻率看上去只有兩幀,無法形成一個(gè)自然過渡的動畫,筆者懷疑是 React Native Android 對 SVG 的支持仍有缺陷。

SVG 圖形和普通 React Native View 的疊加問題,目前我還沒有找到解決方法。感覺只能等 React Native 開發(fā)組的進(jìn)一步支持。

動畫播放總會有一個(gè)莫名其妙的下拉回彈效果,然而代碼上沒有任何額外的控制。

// RotatingWedge.js
"use strict";

var React = require("react-native");

var {
  ART,
  View,
  Animated,
  Easing,
} = React;

var Group = ART.Group;
var Surface = ART.Surface;
var Wedge = require("./Wedge");

var AnimatedWedge = Animated.createAnimatedComponent(Wedge);

var VectorWidget = React.createClass({

  getInitialState: function() {
    return {
      startAngle: new Animated.Value(90),
      endAngle: new Animated.Value(100),
    };
  },

  componentDidMount: function() {
    Animated.parallel([
      Animated.timing(
        this.state.endAngle,
        {
          toValue: 405,
          duration: 700,
          easing: Easing.linear,
        }
      ),
      Animated.timing(
        this.state.startAngle,
        {
          toValue: 135,
          duration: 700,
          easing: Easing.linear,
        })
    ]).start();
  },
  
  render: function() {
    return (
      
        
          {this.renderGraphic()}
        
      
    );
  },

  renderGraphic: function() {
    console.log(this.state.endAngle.__getValue());
    return (
      
        
      
    );
  }
});

module.exports = VectorWidget;
Value 驅(qū)動的動畫

接下來看 Value 驅(qū)動的 SVG 動畫。先解釋一下 Value 和 Props 的區(qū)別。,這里的 color 就是 Props,black這里的 black 就是 value。

為什么要特意強(qiáng)調(diào)這一點(diǎn)呢,如果我們想要做一個(gè)如下圖所示的從10到30變動的數(shù)字,按照上節(jié)所述的方法,直接調(diào)用 Animated.createAnimatedComponent(React.Text)所生成的 Component ,然后給 Value 賦值一個(gè)Animated.Value(),然后Animated.timing...,是無法產(chǎn)生這樣的效果的。

必須要對庫中的createAnimatedComponent()函數(shù)做一定的改造。改造后的函數(shù)如下:

var AnimatedProps = Animated.__PropsOnlyForTests;

function createAnimatedTextComponent() {
    var refName = "node";

    class AnimatedComponent extends React.Component {
        _propsAnimated: AnimatedProps;

        componentWillUnmount() {
            this._propsAnimated && this._propsAnimated.__detach();
        }

        setNativeProps(props) {
            this.refs[refName].setNativeProps(props);
        }

        componentWillMount() {
            this.attachProps(this.props);
        }

        attachProps(nextProps) {
            var oldPropsAnimated = this._propsAnimated;

            /** 關(guān)鍵修改,強(qiáng)制刷新。
            原來的代碼是:
             var callback = () => {
               if (this.refs[refName].setNativeProps) {
                 var value = this._propsAnimated.__getAnimatedValue();
                 this.refs[refName].setNativeProps(value);
               } else {
                 this.forceUpdate();
               }
             };
            **/
            var callback = () => {
                this.forceUpdate();
            };

            this._propsAnimated = new AnimatedProps(
                nextProps,
                callback,
            );

            oldPropsAnimated && oldPropsAnimated.__detach();
        }

        componentWillReceiveProps(nextProps) {
            this.attachProps(nextProps);
        }

        render() {
            var tmpText = this._propsAnimated.__getAnimatedValue().text;
            return (
                
                    {Math.floor(tmpText)}
                
            );
        }
    }

    return AnimatedComponent;
}

為了獲取必須要用到的AnimatedProps,筆者甚至違背了道德的約束,訪問了雙下劃線前綴的變量Animated.__PropsOnlyForTests,真是罪惡啊XD。

言歸正傳,重要的修改有:

修改了 attachProps 函數(shù)。對于任何變動的 props,原來的代碼會試圖使用 setNativeProps 函數(shù)進(jìn)行更新,若 setNativeProps 函數(shù)為空,才會使用 forceUpdate() 函數(shù)。對于 props,setNativeProps 函數(shù)是可行的,然而對 value 無效。我猜測,setNativeProps 方法在 Android 底層可能就是 setColor() 類似的 Java 方法,然而并沒有得到實(shí)證。目前這種 forceUpdate,由注釋知,是徹底更新了整個(gè) Component,相當(dāng)于先從 DOM 樹上取下一個(gè)舊節(jié)點(diǎn),再放上一個(gè)新節(jié)點(diǎn),在性能的利用上較為浪費(fèi)。

使用 PropTypes.xxx.isRequired 來進(jìn)行參數(shù)的類型檢查。PropTypes 檢查支持的類型可在 react-native/node_modules/react/lib/ReactPropTypes.js 中看到,在此不再贅述。

Animated.value() 從10到30變化的過程是一個(gè)隨機(jī)采樣的過程,并不一定會卡在整數(shù)值上,因此還需要做一些小處理。

值得注意的是,該動畫在 Android 上雖然可以正常運(yùn)行,但也存在丟幀的問題,遠(yuǎn)遠(yuǎn)不能如 iOS 上流暢自然。對于這一點(diǎn),只能等待 Facebook 的進(jìn)一步優(yōu)化。

全部的代碼如下:

// RisingNumber.js
"use strict";

var React = require("react-native");

var {
    Text,
    Animated,
    Easing,
    PropTypes,
    View,
    StyleSheet,
} = React;

var AnimatedText = createAnimatedTextComponent();
var AnimatedProps = Animated.__PropsOnlyForTests;

function createAnimatedTextComponent() {
    var refName = "node";

    class AnimatedComponent extends React.Component {
        _propsAnimated: AnimatedProps;

        componentWillUnmount() {
            this._propsAnimated && this._propsAnimated.__detach();
        }

        setNativeProps(props) {
            this.refs[refName].setNativeProps(props);
        }

        componentWillMount() {
            this.attachProps(this.props);
        }

        attachProps(nextProps) {
            var oldPropsAnimated = this._propsAnimated;

            var callback = () => {
                this.forceUpdate();
            };

            this._propsAnimated = new AnimatedProps(
                nextProps,
                callback,
            );

            oldPropsAnimated && oldPropsAnimated.__detach();
        }

        componentWillReceiveProps(nextProps) {
            this.attachProps(nextProps);
        }

        render() {
            var tmpText = this._propsAnimated.__getAnimatedValue().text;
            return (
                
                    {Math.floor(tmpText)}
                
            );
        }
    }

    return AnimatedComponent;
}

var RisingNumber = React.createClass({
    propTypes: {
        startNumber: PropTypes.number.isRequired,
        toNumber: PropTypes.number.isRequired,
        startFontSize: PropTypes.number.isRequired,
        toFontSize: PropTypes.number.isRequired,
        duration: PropTypes.number.isRequired,
        upperText: PropTypes.string.isRequired,
    },

    getInitialState: function() {
        return {
            number: new Animated.Value(this.props.startNumber),
            fontSize: new Animated.Value(this.props.startFontSize),
        };
    },

    componentDidMount: function() {
        Animated.parallel([
            Animated.timing(
                this.state.number,
                {
                    toValue: this.props.toNumber,
                    duration: this.props.duration,
                    easing: Easing.linear,
                },
            ),
            Animated.timing(
                this.state.fontSize,
                {
                    toValue: this.props.toFontSize,
                    duration: this.props.duration,
                    easing: Easing.linear,
                }
            )
        ]).start();
    },

    render: function() {
        return (
            
                {this.props.upperText}
                
            
        );
    },
});

var styles = StyleSheet.create({
    kind: {
        fontSize: 15,
        color: "#01A971",
    },
    number: {
        marginLeft: 15,
    },
});

module.exports = RisingNumber;

====================================
如果您覺得我的文章對您有所啟迪,請點(diǎn)擊文末的推薦按鈕,您的鼓勵將會成為我堅(jiān)持寫作的莫大激勵。 by DesGemini

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/79036.html

相關(guān)文章

  • 11個(gè)React Native 組件庫 Javascript 數(shù)據(jù)可視化庫

    摘要:數(shù)據(jù)可視化庫超過的的可能是最流行和最廣泛的數(shù)據(jù)可視化庫。是一組組件,用于高效地渲染大型列表和表格數(shù)據(jù)。一種優(yōu)雅而靈活的方式,可以利用組件來支持實(shí)際的數(shù)據(jù)可視化。 想閱讀更多優(yōu)質(zhì)文章請猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你! React Native 組件庫 1. NativeBase showImg(https://segmentfault.com/img/bVbrLHH?w=...

    tangr206 評論0 收藏0
  • 前端每周清單半年盤點(diǎn)之 ReactReactNative

    摘要:前端每周清單半年盤點(diǎn)之與篇前端每周清單專注前端領(lǐng)域內(nèi)容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn)分為新聞熱點(diǎn)開發(fā)教程工程實(shí)踐深度閱讀開源項(xiàng)目巔峰人生等欄目。與求同存異近日,宣布將的構(gòu)建工具由遷移到,引發(fā)了很多開發(fā)者的討論。 前端每周清單半年盤點(diǎn)之 React 與 ReactNative 篇 前端每周清單專注前端領(lǐng)域內(nèi)容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn);分為...

    Barry_Ng 評論0 收藏0
  • 初窺基于 react-art 庫 React Native SVG

    摘要:語法更近似于移動端。當(dāng)參數(shù)為兩個(gè)時(shí),等同于,繪制光滑二次貝塞爾曲線。有些精通的同學(xué)這時(shí)候可能就要問我了,不對啊,二次貝塞爾曲線和光滑三次貝塞爾曲線的參數(shù)都是個(gè),你這里沒有光滑三次啊因?yàn)殚_發(fā)的同學(xué)留坑沒寫了呀微笑。和則是用于指定旋轉(zhuǎn)的原點(diǎn)。 技術(shù)背景 在移動應(yīng)用的開發(fā)過程中,繪制基本的二維圖形或動畫是必不可少的。然而,考慮到Android和iOS均有一套各自的API方案,因此采用一種更普...

    xiaowugui666 評論0 收藏0
  • 前端動畫調(diào)研-V1

    摘要:支持動畫狀態(tài)的,在動畫開始,執(zhí)行中,結(jié)束時(shí)提供回調(diào)函數(shù)支持動畫可以自定義貝塞爾曲線任何包含數(shù)值的屬性都可以設(shè)置動畫倉庫文檔演示功能介紹一定程度上,也是一個(gè)動畫庫,適用所有的屬性,并且實(shí)現(xiàn)的能更方便的實(shí)現(xiàn)幀動畫,替代復(fù)雜的定義方式。 動畫調(diào)研-V1 前言:動畫從用途上可以分為兩種,一種是展示型的動畫,類似于一張GIF圖,或者一段視頻,另一種就是交互性的動畫。這兩種都有具體的應(yīng)用場景,比如...

    ddongjian0000 評論0 收藏0
  • 14個(gè)最好 JavaScript 數(shù)據(jù)可視化庫

    摘要:適用于,演示這是開發(fā)的一個(gè)簡單的可視化庫,它允許你創(chuàng)建所有常用的圖表類型條形圖,樹形圖,折線圖,面積圖等。可以輕松地對折線圖和條形圖進(jìn)行混合和匹配以組合不同的數(shù)據(jù)集,這是非常棒的功能。 翻譯:瘋狂的技術(shù)宅原文:https://www.monterail.com/blo... 本文首發(fā)微信公眾號:jingchengyideng歡迎關(guān)注,每天都給你推送新鮮的前端技術(shù)文章 你的程序有多...

    Mertens 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<