• <ul id="cgeq2"></ul>
  • 歡迎您光臨深圳塔燈網(wǎng)絡(luò)科技有限公司!
    電話圖標(biāo) 余先生:13699882642

    網(wǎng)站百科

    為您解碼網(wǎng)站建設(shè)的點(diǎn)點(diǎn)滴滴

    Flutter動(dòng)畫設(shè)計(jì)原理

    發(fā)表日期:2018-12 文章編輯:小燈 瀏覽次數(shù):2789

    動(dòng)畫實(shí)現(xiàn)的方式

    Flutter中,我們可以簡(jiǎn)單的把調(diào)用this.setState()理解為渲染一幀。那么只要我們不停的在調(diào)用這個(gè)方法的同時(shí)更新位置信息,就能實(shí)現(xiàn)平移動(dòng)畫了,其他動(dòng)畫也是如此。

    而Flutter也是這么做的。

    這個(gè)方式說起來很簡(jiǎn)單,但是想要將它封裝成一個(gè)使用簡(jiǎn)單的框架,卻很不容易。

    Flutter的實(shí)現(xiàn)

    在Flutter中有兩大基類 Animatable 和 Animation

    • Animatable
      這個(gè)控制的是動(dòng)畫的類型的類。例如平移動(dòng)畫我們關(guān)心的是x,y。那么Animatable就需要控制x,y的變化。顏色動(dòng)畫我們關(guān)心的是色值得變化,那么Animatable就需要控制色值。
      貝塞爾曲線運(yùn)動(dòng),我們關(guān)心的是路徑是按照貝塞爾方程式來生成x y,所以Animatable要有按照貝塞爾方程式的方式改變x,y。

    • Animation
      這個(gè)是控制動(dòng)畫運(yùn)動(dòng)過程的類,不關(guān)心動(dòng)畫的類型。例如動(dòng)畫開始,停止,反轉(zhuǎn),還有各種ease得效果。
      并不關(guān)心你是平移,縮放還是貝塞爾曲線動(dòng)畫。因?yàn)樗械膭?dòng)畫這些狀態(tài)都是一樣的。

    假如我們想實(shí)現(xiàn)一個(gè)從0平移到200位置的動(dòng)畫該怎么做呢?

    按照Flutter的實(shí)現(xiàn)方式我們要先要實(shí)現(xiàn)一個(gè)對(duì)應(yīng)的Animatable。當(dāng)然flutter已經(jīng)為我門預(yù)制了很多類,Tween這個(gè)類就可已實(shí)現(xiàn)。
    很多說Tween是補(bǔ)間動(dòng)畫,自認(rèn)為很是不準(zhǔn)確。這里的Tween其實(shí)是一個(gè)一元一次函數(shù)的實(shí)現(xiàn)。簡(jiǎn)單的說就是單個(gè)維度的漸變動(dòng)畫。
    如果要實(shí)現(xiàn)多維度的動(dòng)畫,就需要自己實(shí)現(xiàn)Animatable。

    T lerp(double t) { assert(begin != null); assert(end != null); return begin + (end - begin) * t; }T transform(double t) { if (t == 0.0) return begin; if (t == 1.0) return end; return lerp(t); } 

    有了Animatable,我們還需要一個(gè)Animation,要不然怎么開始動(dòng)畫?

    當(dāng)然Animation Flutter也為我們預(yù)制了。AnimationController就是一個(gè)。

    各種類都有了就開始寫代碼

    class _SimpleRouteState extends State<SimpleRoute> with SingleTickerProviderStateMixin{ ...AnimationController _controller;@override void initState() { _controller = AnimationController( duration : Duration(seconds: 1) , vsync: this ); ... }//點(diǎn)擊按鈕 開始動(dòng)畫 _offsetAnim(bool isForward){ Animation<double> animation = Tween( begin:0.0 , end: 200.0).animate(_controller); animation.addListener((){ this.setState((){ this.left = animation.value; }); }); if(isForward){ _controller.forward(); }else{ _controller.reverse(); } } } 

    上面的代碼先創(chuàng)建一個(gè)Animatable,然后調(diào)用animate(),傳入Animation
    Animation 開始動(dòng)畫。
    每刷一幀都會(huì)執(zhí)行一次

    this.setState((){ this.left = animation.value; }); 

    動(dòng)畫就實(shí)現(xiàn)了

    動(dòng)畫實(shí)現(xiàn)原理

    帶著幾個(gè)問題分析:
    1.AnimationController在動(dòng)畫中扮演一個(gè)什么角色?
    2.調(diào)用forward之后,為什么動(dòng)畫就會(huì)開始?
    3.是誰驅(qū)動(dòng)動(dòng)畫一直執(zhí)行,難道有for循環(huán)嗎?

    AnimationController的角色

    • Overview

    我的理解:AnimationController 將動(dòng)畫描述成一個(gè)可以量化的過程,這個(gè)量化的值就是從0.0到1.0的過程(采用默認(rèn)的下限值和上限值)。
    從0.0到1.0就是forward , 從1.0到0.0就是reverse。而這個(gè)值就是_value這個(gè)變量。

    • 如何實(shí)現(xiàn)從0.0到1.0的過程?

    通過Ticker來接受GPU的垂直同步信號(hào),在每次接受到信號(hào)后更新這個(gè)值。

    //收到垂直信號(hào)后的回調(diào)處理方法 void _tick(Duration elapsed) { _lastElapsedDuration = elapsed; final double elapsedInSeconds = elapsed.inMicroseconds.toDouble() / Duration.microsecondsPerSecond; assert(elapsedInSeconds >= 0.0); _value = _simulation.x(elapsedInSeconds).clamp(lowerBound, upperBound); if (_simulation.isDone(elapsedInSeconds)) { _status = (_direction == _AnimationDirection.forward) ? AnimationStatus.completed : AnimationStatus.dismissed; stop(canceled: false); } notifyListeners(); _checkStatusChanged(); } 

    這也就是為什么構(gòu)建必須要傳入vsync的原因,AnimationController用他來創(chuàng)建一個(gè)Ticker的。

    • forward之后做了啥?

    先停止當(dāng)前正在進(jìn)行的動(dòng)畫,然后調(diào)用Ticker的start(),開始接受垂直同步的回調(diào),然后再回調(diào)中根據(jù)流失的時(shí)間,來計(jì)算當(dāng)前的value值。
    從而達(dá)到控制動(dòng)畫進(jìn)程的目的。

    TickerFuture forward({ double from }) { ... //如果from沒有值,就默認(rèn)是動(dòng)畫到1.0(upperBound的默認(rèn)值是1.0) _direction = _AnimationDirection.forward; if (from != null) value = from; return _animateToInternal(upperBound); }//構(gòu)建一個(gè)Simulation TickerFuture _animateToInternal(double target, { Duration duration, Curve curve = Curves.linear, AnimationBehavior animationBehavior }) { ...if (simulationDuration == null) { ... // final double range = upperBound - lowerBound; final double remainingFraction = range.isFinite ? (target - _value).abs() / range : 1.0; simulationDuration = this.duration * remainingFraction; } else if (target == value) { // Already at target, don't animate. simulationDuration = Duration.zero; } stop(); ...return _startSimulation(_InterpolationSimulation(_value, target, simulationDuration, curve, scale)); }//開啟_ticker value開始從0.0到1.0變化。 TickerFuture _startSimulation(Simulation simulation) {... _value = simulation.x(0.0).clamp(lowerBound, upperBound); final TickerFuture result = _ticker.start(); ...return result; } 

    由此可見 AnimationController 在動(dòng)畫中扮演著控制動(dòng)畫過程的角色,通過維護(hù)著一個(gè)從0.0到1.0的變量來控制動(dòng)畫的進(jìn)行。

    Tween的角色

    • Overview

      配合AnimationController,將一個(gè)變量從begin變化到end的過程。這個(gè)變化的過程是一個(gè)簡(jiǎn)單的一元一次函數(shù)。
      t的取值范圍是[0,1]。若t是均勻變化的,就是線性的從begin到end。

      T lerp(double t) { return begin + (end - begin) * t; } 

      若這個(gè)變量是多個(gè)維度,例如是一個(gè)Rect,有四個(gè)變量,那就要從寫這個(gè)方法了。可以參見RectTween。

    • Animation<T> animate(Animation parent) 這個(gè)方法做了啥?

    這個(gè)就需要了解一個(gè)Flutter的私有類 _AnimatedEvaluation 它的父類是Animation。
    它起到了連接Tween和AnimationController的作用,具體體現(xiàn)在它的get value的實(shí)現(xiàn)

    @override T get value => _evaluatable.evaluate(parent); 

    這個(gè)_evaluatable就是構(gòu)建_AnimatedEvaluation傳入的Tween,這句話會(huì)調(diào)用Tween的evaluate(),最終會(huì)調(diào)用上面的lerp();lerp參數(shù)t就是AnimationController維護(hù)的那個(gè)從0.0到1.0的變量。

    當(dāng)你每次在setState()后調(diào)用animation.value,就會(huì)走上面這個(gè)個(gè)方法,得到的就是當(dāng)前時(shí)間點(diǎn)的動(dòng)畫的值。這樣,整個(gè)動(dòng)畫就被驅(qū)動(dòng)起來了。所以Flutter動(dòng)畫里面不存在for循環(huán)。

    總結(jié)

    * 如何用flutter實(shí)現(xiàn)一個(gè)貝塞爾曲線運(yùn)動(dòng)?

    原理:繼承Animatable實(shí)現(xiàn)一個(gè)_BezierTween,并重寫他的transform();這里直接將貝塞爾曲線方程式帶進(jìn)去即可。
    當(dāng)然重寫Tween的lerp方法也是可行的。

    bei.jpeg i2lhs-vedko.gif

    實(shí)現(xiàn)源碼:

    class _Point{ const _Point({this.x , this.y});final double x; final double y; }class _BezierTween extends Animatable<_Point>{ _BezierTween({ this.p0, this.p1, this.p2 }):assert(p0 != null), assert(p1 != null), assert(p2 != null);final _Point p0; //起始點(diǎn) final _Point p1; //途徑點(diǎn) final _Point p2; //終點(diǎn)@override transform(double t) { double x = (1-t) * (1-t) * p0.x + 2 * t * (1-t) * p1.x + t * t * p2.x; double y = (1-t) * (1-t) * p0.y + 2 * t * (1-t) * p1.y + t * t * p2.y; return _Point( x:x , y:y ); } } 

    使用

    @override void initState() { super.initState();_controller = AnimationController(duration:Duration(seconds: 2) , vsync: this);_p0= _Point( x:30,y:30); _p1= _Point( x:30,y:200); _p2= _Point( x:200,y:200); _animation = _BezierTween(p0: _p0 , p1: _p1 , p2: _p2).animate(_controller); _animation.addListener((){ this.setState((){}); }); }

    本頁內(nèi)容由塔燈網(wǎng)絡(luò)科技有限公司通過網(wǎng)絡(luò)收集編輯所得,所有資料僅供用戶學(xué)習(xí)參考,本站不擁有所有權(quán),如您認(rèn)為本網(wǎng)頁中由涉嫌抄襲的內(nèi)容,請(qǐng)及時(shí)與我們聯(lián)系,并提供相關(guān)證據(jù),工作人員會(huì)在5工作日內(nèi)聯(lián)系您,一經(jīng)查實(shí),本站立刻刪除侵權(quán)內(nèi)容。本文鏈接:http://www.juherenli.com/17759.html
    上一篇:Flutter 初嘗 下一篇:Flutter VS Weex
    相關(guān)APP開發(fā)
     八年  行業(yè)經(jīng)驗(yàn)

    多一份參考,總有益處

    聯(lián)系深圳網(wǎng)站公司塔燈網(wǎng)絡(luò),免費(fèi)獲得網(wǎng)站建設(shè)方案及報(bào)價(jià)

    咨詢相關(guān)問題或預(yù)約面談,可以通過以下方式與我們聯(lián)系

    業(yè)務(wù)熱線:余經(jīng)理:13699882642

    Copyright ? 2013-2018 Tadeng NetWork Technology Co., LTD. All Rights Reserved.    

    99热在线精品观看| 国产精品自产拍在线18禁| 国产成人精品高清在线观看93| 国产精品白嫩在线观看| 亚洲精品国产啊女成拍色拍| 中文字幕久久精品| 欧洲精品一区二区三区在线观看 | 香蕉依依精品视频在线播放| 精品国产专区91在线尤物| 久久精品视频观看| 99精品免费视品| 国产精品成人h片在线| 99久久精品国产第一页| 日韩精品免费一级视频| 性色精品视频网站在线观看| 中文字幕无码精品三级在线电影| 国产成人精品无码一区二区老年人| 麻豆精品在线播放| 亚洲国产成人精品无码区在线网站| 亚洲国产精品无码成人片久久| 99精品久久精品一区二区| 五月天婷婷精品免费视频| 国产精品亚洲专区在线播放| 欧美精品久久久久久精品爆乳| 日本亚洲精品色婷婷在线影院| 91精品天美精东蜜桃传媒入口| 国产l精品国产亚洲区在线观看| 99re这里只有精品热久久| 久久精品国产只有精品66| 国产精品免费久久久久电影网| 国产亚洲综合精品一区二区三区| 日韩精品无码成人专区| 国内精品免费视频自在线| 国产成人午夜精品一区二区三区| 麻豆精品在线观看| 国产精品嫩草视频永久网址| 久クク成人精品中文字幕| 国产精品吹潮香蕉在线观看| 精品久久久无码21p发布| 中文字幕日韩精品麻豆系列| 四虎精品视频在线永久免费观看 |