簡析iOS動畫原理及實現——Core Animation

來源:互聯網
上載者:User

標籤:

本文轉載至 http://www.tuicool.com/articles/e2qaYjA原文  https://tech.imdada.cn/2016/06/21/ios-core-animation/主題 Core Animation背景

隨著達達業務的擴大,越來越多的人開始使用達達用戶端,參加到眾包物流的行業中。達達用戶端分為iOS平台和安卓平台。

APP開發也從快速迭代的粗曠性開發轉向高可複用,提升使用者提現的精細化方向發展。iOS動畫互動良好,使用廣泛,良好的使用者體驗離不開流暢的介面變換。為此,達達iOS團隊對動畫實現以及背後原理做了學習探索。

目的
  • 瞭解Core Animation 基本架構
  • 理解圖層的定義
  • 解析動畫的流程
  • 掌握3D動畫的原理
  • 實現3D動畫的代碼實現
基本架構

Core Animation位於AppKit、UIKit下方,整合於View的Cocoa、Cocoa Touch中。當然Core Animation向view提供很多介面,好讓開發人員更好的控制動畫。

圖層的定義
  1. 開發人員所有關於Core Animation的操作都是基於圖層,圖層對象是3D空間向後垂直投影的一個2D平面。
  2. 一個圖層抓取由View提供的內容,並把這些內容緩衝在一張位元影像中。
  3. 在硬體層面中對位元影像的操作遠遠快於在軟體層面。
理解
  1. 垂直投影可以想象成,一個物體站在一面牆前,一個垂直於牆面的光源,照射物體,在牆上留下的陰影,由於是垂直光源,無論物體是遠離牆面,還是靠近牆面,陰影的大小都不會發生變化。

  2. iOS程式中每個View控制項都有自己的Layer,View上的子控制項,例如:Label,Image等,便是View向Layer提供的內容,而平移,旋轉,縮放等參數,便是狀態資訊。

  3. 基於Layer的動畫,Core Animation把layer保留的bitmap和狀態資訊傳給圖形硬體,圖形硬體負責使用新的資訊操作bitmap。基於View 繪製,是通過調用view的drawRect方法來使用新的參數重繪內容改變view。這種繪製代價很高,因為繪製在匯流排程消耗CPU完成工作。

動畫的流程

動畫的原理

下面內容將會涉及的知識,依次是數學座標系,線性代數矩陣,物理成像原理,數學相似三角形,數學方程組。不用擔心,我們會從基礎入手,讓理解更加高效。

座標系

在圖層平面座標系統中,使用兩種座標系。

  • 基於點的座標系
原點位於圖層的左上方,向右為x軸的正方向,向下為y軸的正方向,一個點的x、y座標以點為單位。
  • 單位座標系
原點位於圖層的左上方,向右為x軸的正方向,向下為y軸的正方向,一個點的x、y座標以相對x軸、y軸的比例為值,取值範圍[0,1]。錨點(anchorPoint)使用單位座標系, 如所示position根據錨點而變。

錨點的作用

錨點決定了動畫在變化時,z軸的位置。如,由於錨點不同,圖層繞z軸的旋轉效果也一樣。

矩陣
  1. 通過矩陣對圖層位元影像進行平移、旋轉、縮放變換。
  2. 矩陣相乘只有在第一個矩陣的列數(column)和第二個矩陣的行數(row)相同時才有意義。
  3. 圖層的bitmap由點組成,每個點可以對應1×4矩陣,乘以一個4×4變換矩陣,得到一個1×4矩陣,即為變換後的結果。

矩陣乘法

  1. 矩陣C的行數等於矩陣A的行數,C的列數等於B的列數。
  2. 乘積C的第m行第n列的元素等於矩陣A的第m行的元素與矩陣B的第n列對應元素乘積之和。
思考?
1. 點座標為什麼要轉換為1×4矩陣2. 變換矩陣為什麼必須是4×4矩陣3. 如何?移動,縮放,旋轉
齊次矩陣
  1. 齊次座標就是將一個原本是n維的向量用一個n+1維向量來表示。
  2. 使用1×4矩陣,是相對點的三維座標進行齊次座標。
齊次座標變換 (x, y, z) -> (x × h,  y × h,  z × h,  h) -> (xˊ, yˊ, zˊ, h)齊次座標還原 (xˊ, yˊ, zˊ, h) -> (x / h,  y / h,  z / h,  1) -> (x, y, z)
如果不使用1×4齊次矩陣和4×4變換矩陣?

只使用3×3變換矩陣:

              m11, m12, m13{x, y, z} * { m21, m22, m23 } = {x‘, y‘, z‘}              m31, m32, m33

xˊ=x × m11 + y × m21 + z × m31 在預先不對變數係數(m11, m21, m31)做其他計算的情況下,只能實現在各個座標軸的縮放

但是使用使用1×4齊次矩陣和4×4變換矩陣後

xˊ= x × m11 + y × m21 + z × m31 + 1 × m41m11=2  m21=0 m31=0 m41=8可同時實現向x軸正方向放大2倍,在沿著x軸正方向平移8個單位
引入齊次座標的目的主要是合并矩陣運算中的乘法和加法。基本變換矩陣
  • 矩陣就是利用矩陣內特殊位置的值,在做矩陣乘法時,達到對點座標進行變換,下面時常用變換矩陣

3D動畫效果

iOS中的CALayer的3D本質上並不能算真正的3D,而只是3D在二維平面上的投影,檢視平面就是手機螢幕也就是xy軸組成的平面。

如此,只使用基本變換矩陣實現的平移、縮放、旋轉,不會有近大遠小的透視效果。

那該如何產生近大遠小呢?
  • 要達到近大遠小目的,需要在系統做垂直投影前,先對圖層做一次視點變換。如此垂直投影別是視點觀察到的近大遠小的物體。

  • Layer的z軸的位置則是通過anchorPoint來指定的,所謂的anchorPoint(錨點)就是在變換中保持不變的點,也就是某個Layer在變換中的原點,xyz三軸相交於此點。為錨點常用位置

  • 在原點(0 , 0)沿著Y軸的正方向,得到座標系, 首先在Z軸選擇一個視點

  • 添加兩個child layer,觀察地區便能看到兩個child layer頂部的短線,綠色在前,紅色在後,且長度相等

  • 通過視點對頂部,作相對X軸的投影,得到視點投影

  • 綠線、紅線本來長度相等,通過視點投影后造成了“近大遠小”的透視效果

所以只要在iOS垂直投影前,對layer作視點投影變換,就能得到透視效果實踐透視原理
  • 使用的座標系,紅點為觀察地區一點,對紅點做視點投影,得到綠點,同時對紅點做z軸的垂直線得到黑點。

  • 使用相似三角形原理,得到如下公式

  • 簡化公式後,得到 方程1 ,綠點x軸的值只於視點z軸值有關

  • 對紅點做h = 1的齊次座標(6, 0, 5, 1),通過乘以一個矩陣,得到變換後的綠點的齊次矩陣

  • 變換後的矩陣只與視點z軸值有關,所以只設定m34,對(6, 0, 5, 1 + 5r)還原得到 方程2

  • 結合 方程1 和 方程2 ,最後得到

至此只要修改變換矩陣m34的值為視點z軸值,便能得到相應的視點投影變換矩陣動畫的代碼實現
  • 使用達達啟動頁面來實踐以上部分內容。PS:為了查看簡介,未對方法封裝

  • 屬性申明

    @property (weak, nonatomic) IBOutlet UIImageView *logoImg; //達達Logo@property (weak, nonatomic) IBOutlet UILabel *nameLab;// 達達@property (weak, nonatomic) IBOutlet UILabel *desLab;// 可靠配送,在你身邊
  • 初始化設定,對兩個Label設定透明度為0,縮小到原來的0.5倍

     - (void)viewDidLoad{   [super viewDidLoad];    self.nameLab.alpha = 0.f;   self.nameLab.layer.transform = CATransform3DMakeScale(0.5f, 0.5f, 1.f);   self.desLab.alpha = 0.f;   self.desLab.layer.transform = CATransform3DMakeScale(0.5f, 0.5f, 1.f); }
  • 動畫設定,對Logo和Label的分開實現動畫

     - (void)viewDidAppear:(BOOL)animated{   [self animationDaDaLabel];   [self animationDaDaLogo];}
  • 對Label的動畫,使用UIView內建的block方式

    - (void) animationDaDaLabel{  [UIView animateWithDuration:0.5f animations:^{      // 放大並模糊      self.nameLab.alpha = 0.5f;      self.nameLab.layer.transform = CATransform3DMakeScale(1.2f, 1.2f, 1.f);      self.desLab.alpha = 0.5f;      self.desLab.layer.transform = CATransform3DMakeScale(1.2f, 1.2f, 1.f);        } completion:^(BOOL finished) {             [UIView animateWithDuration:0.5f animations:^{          // 恢複並清晰          self.nameLab.alpha = 1.f;          self.nameLab.layer.transform = CATransform3DMakeScale(1.f, 1.f, 1.f);          self.desLab.alpha = 1.f;          self.desLab.layer.transform = CATransform3DMakeScale(1.f, 1.f, 1.f);       }];   }];}
  • 對Logo的動畫,使用CABasicAnimation對象

       - (void) animationDaDaLogo{    CATransform3D transform = CATransform3DIdentity;    transform.m34 = - 1 / 100.0f;   // 設定視點在Z軸正方形z=100        // 動畫結束時,在Z軸負方向60    CATransform3D startTransform = CATransform3DTranslate(transform, 0, 0, -60);    // 動畫結束時,繞Y軸逆時針旋轉90度    CATransform3D firstTransform = CATransform3DRotate(startTransform, M_PI_2, 0, 1, 0);        // 通過CABasicAnimation修改transform屬性    CABasicAnimation *animation1 = [CABasicAnimation animationWithKeyPath:@"transform"];    // 向後移動同時繞Y軸逆時針旋轉90度    animation1.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];    animation1.toValue = [NSValue valueWithCATransform3D:firstTransform];        // 雖然只有一個動畫,但用Group只為以後好擴充    CAAnimationGroup *animationGroup = [CAAnimationGroup animation];    animationGroup.animations = [NSArray arrayWithObjects:animation1, nil];    animationGroup.duration = 0.5f;    animationGroup.delegate = self;     // 動畫回調,在動畫結束調用animationDidStop    animationGroup.removedOnCompletion = NO;    // 動畫結束時停止,不回複原樣        // 對logoImg的圖層應用動畫    [self.logoImg.layer addAnimation:animationGroup forKey:@"FristAnimation"];}
  • 實際上,只對Logo使用“一半動畫”,Logo一邊向後移動,一邊逆時針繞Z軸旋轉90度,在此動畫結束後,通過回調補全剩下的“一半動畫”。利用這兩部分,實現,向後移動同時逆時針旋轉,旋轉到90度時,向前移動,同時繼續逆時針旋轉90度

      - (void) animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{    if (flag)    {        if (anim == [self.logoImg.layer animationForKey:@"FristAnimation"])        {            CATransform3D transform = CATransform3DIdentity;            transform.m34 = - 1 / 100.0f;   // 設定視點在Z軸正方形z=100                        // 動畫開始時,在Z軸負方向60            CATransform3D startTransform = CATransform3DTranslate(transform, 0, 0, -60);            // 動畫開始時,繞Y軸順時針旋轉90度            CATransform3D secondTransform = CATransform3DRotate(startTransform, -M_PI_2, 0, 1, 0);                        // 通過CABasicAnimation修改transform屬性            CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"];            // 向前移動同時繞Y軸逆時針旋轉90度            animation.fromValue = [NSValue valueWithCATransform3D:secondTransform];            animation.toValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];            animation.duration = 0.5f;                        // 對logoImg的圖層應用動畫            [self.logoImg.layer addAnimation:animation forKey:@"SecondAnimation"];        }    }}
  • 最終效果(PS:僅用於講解)

小結

根據以上內容,總結以下Core Animation相關重點

  • 理解圖層意義,圖層是動畫的核心和載體
  • 理解兩種平面座標系統的用途,在做3D視點變換的時,要通過三維座標系來協助思考
  • 理解矩陣,齊次座標的使用目的
  • 如果對成像原理不瞭解,可以搜尋相關資料
  • 通過代碼進一步實踐

申明:本文的圖片源於蘋果CoreAnimation Programming Guide,如果想進一步瞭解,推薦學習蘋果官方文檔

簡析iOS動畫原理及實現——Core Animation

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.