iOS 動畫 - 窗景篇(三·完結(jié))

這篇文章是系列文章的第三篇谴古。

看過上一篇文章的朋友撞羽,已經(jīng)知道標(biāo)題中的“景”指代 view掏缎,“窗”指代 view.mask皱蹦,窗景篇就是在梳理 mask 及 mask 動畫。如果你還不熟悉 iOS 的 mask眷蜈,建議先看一下第一篇沪哺。

前兩篇我們介紹了 mask、mask 動畫的一些用法酌儒。

這一篇作為收尾辜妓,我們來實(shí)現(xiàn)一個效果練練手,
也借這個效果,讓大家回憶起一個簡單的道理:復(fù)雜的效果籍滴,可以等價于簡單效果的組合酪夷。

一、效果

這個效果如下面的動圖所示:

我們截取比較有代表性的一幀孽惰,如下圖所示:

從圖中可以看到捶索,波浪由兩種顏色組成弟孟,各部分顏色不同别惦。

這個效果看上去有點(diǎn)復(fù)雜茫孔,如果不熟悉 mask呻澜,可能一時半會兒沒有思路绪励。
但看過前兩篇的朋友休雌,可能已經(jīng)暗暗在想址芯,是窗在動哥捕?還是景在動要销?會不會有多套窗景构回?

那么接下來,我們先通過一個簡單的效果來看一下原理疏咐。

注:波浪動畫的實(shí)現(xiàn)和本文關(guān)系不大纤掸,本文不會講述。
網(wǎng)上有成熟的波浪動畫的教程浑塞,本文 demo 中 WaveView 類也有簡要的注釋借跪。

二、一個簡單的效果

這個效果如下圖所示:

從圖中可以看到酌壕,一張黑白圖片上有一部分是彩色的掏愁。
我們當(dāng)然可以通過圖像處理來實(shí)現(xiàn)這個效果,但在本文中卵牍,我們還是使用 mask 的方式來實(shí)現(xiàn)果港。

我們回憶一下前文中的一張圖:

通過對frontView 添加一個圓 mask,就形成了圖中的效果糊昙。

也許有的朋友已經(jīng)想到辛掠,把上圖中 backView 的圖換成和 frontView 一樣的黑白圖片,不就是本例的效果嗎释牺,如下圖所示:

也就是說萝衩,這個效果看上去是黑白圖片上有一部分變成了彩色,
但其實(shí)只是兩張內(nèi)容一樣的圖片重疊船侧,黑白圖片在后欠气,彩色圖片在前,而前方的彩色圖片镜撩,被施加了圓形的 mask预柒。

這個效果很簡單队塘,但能讓我們意識到一件事:看上去是一張圖,其實(shí)可能是多張圖組合而成宜鸯。

既然如此憔古,那本文的波浪動畫中,各部分顏色不同的波浪淋袖,真的只是一個波浪嗎鸿市?

沒錯,本例中的波浪即碗,也是多個波浪組合而成的焰情,接下來,我們就詳細(xì)的看一看剥懒。

三内舟、多景合一

和黑白、彩色圖片重疊效果一樣初橘,多色波浪也是由一組重疊的波浪 view 組合而成验游。

每層 view 的波浪只有一種顏色,各層 view 的波浪動畫都一致保檐,對于每一幀耕蝉,所有波浪都是完全重合的。

每個波浪 view 都有自己的 mask夜只,在 mask 們 的控制下垒在,每層波浪 view 只顯示了波浪的一部分,我們看到的多色波浪盐肃,就是各層波浪 view 可見部分的組合爪膊。

我們?nèi)蓪觼硎疽庖幌拢缦聢D所示:

從圖中可以看到砸王,白底紅波浪 view 有個上半圓 mask、黑底藍(lán)波浪 view 有個下半圓 mask峦阁,兩個 view 的波浪進(jìn)度完全一致谦铃,組合之后就成了最右邊的的效果。

捅破了這層窗戶紙后榔昔,其實(shí)原理就是如此簡單驹闰。

知道了原理,其實(shí)大家可以自己去動手去實(shí)現(xiàn)效果了撒会,
當(dāng)然嘹朗,如果不著急的話,那咱們一塊把流程走一遍诵肛。

四屹培、創(chuàng)建4層 view

這一步很簡單,創(chuàng)建 frame 完全一致的 4 層 view,本例中使用 WaveView 作為 view褪秀,
根據(jù)需要蓄诽,設(shè)置不同的背景色(黑、白)和波浪色(紅媒吗、藍(lán))仑氛。

這一步后,4 層波浪 view 如圖所示:

示意代碼如下:

// A3WaveViewController
private func addSubViews() {
    // 上大半圓 view
    view.addSubview(bigTopView)
    // 上小半圓 view
    view.addSubview(smallTopView)
    // 下大半圓 view
    view.addSubview(bigBottomView)
    // 下小半圓 view
    view.addSubview(smallBottomView)
}

五闸英、為 4 層 view 設(shè)置 mask

這一步就是做出合適的 mask 锯岖。
本例中使用 HalfCircleView 作為 mask,分別為各層 view 設(shè)置兩個大半圓和兩個小半圓的 mask甫何。

這一步后嚎莉,4層 view 在 mask 的影響下如下圖所示,大家可以和前文圖中的 4個 view 對照著看:

示意代碼如下:

// A3WaveViewController
private func makeLayout() {
    // 設(shè)置4個 view 的 mask
    
    // 大上半圓
    let width: CGFloat = 200
    let marginX = (UIScreen.main.bounds.width - width) / 2
    bigTopView.frame = CGRect(x: marginX, y: 200, width: width, height: 200)
    // 大上半圓 mask
    let bigTopMask = HalfCircleView()
    bigTopMask.part = .top
    bigTopMask.frame = CGRect(x: 0, y: 0, width: bigTopView.bounds.width, height: bigTopView.bounds.height / 2)
    bigTopView.mask = bigTopMask
    
    // 小上半圓(半徑是大半圓的一半)
    smallTopView.frame = bigTopView.frame
    // 小上半圓 mask
    let smallTopMask = HalfCircleView()
    smallTopMask.part = .top
    smallTopMask.frame = CGRect(x: smallTopView.bounds.width / 4,
                                y: smallTopView.bounds.height / 4,
                                width: smallTopView.bounds.width / 2,
                                height: smallTopView.bounds.height / 4)
    smallTopView.mask = smallTopMask
    
    // 大下半圓
    bigBottomView.frame = bigTopView.frame
    // 大下半圓 mask
    let bigBottomMask = HalfCircleView()
    bigBottomMask.part = .bottom
    bigBottomMask.frame = CGRect(x: 0,
                                 y: bigBottomView.bounds.height / 2,
                                 width: bigBottomView.bounds.width,
                                 height: bigBottomView.bounds.height / 2)
    bigBottomView.mask = bigBottomMask
    
    // 小下半圓
    smallBottomView.frame = bigBottomView.frame
    // 小下半圓 mask
    let smallBottomMask = HalfCircleView()
    smallBottomMask.part = .bottom
    smallBottomMask.frame = CGRect(x: smallBottomView.bounds.width / 4,
                                   y: smallBottomView.bounds.height / 2,
                                   width: smallBottomView.bounds.width / 2,
                                   height: smallBottomView.bounds.height / 4)
    smallBottomView.mask = smallBottomMask
}

六沛豌、讓4層 view 一致地執(zhí)行動畫

為了讓各層波浪動畫完全一致趋箩,我們在外部啟動一個 CADisplayLink,來同時改變 4 個波浪 view 的 progress(前文圖中使用了 progress 為 0.5 時作為示例)加派。

也就實(shí)現(xiàn)了本篇開始的效果:

示意代碼如下:

// A3WaveViewController
func start() {
    if let displayLink = displayLink {
        displayLink.invalidate()
        self.displayLink = nil
        progress = 0
    }
    // 啟動 CADisplayLink
    let displayLink = CADisplayLink(target: WeakProxy(self), selector: #selector(work))
    displayLink.add(to: RunLoop.current, forMode: .common)
    self.displayLink = displayLink
}

@objc private func work() {
    if progress < 1 {
        progress += 0.0025
        progress = min(progress, 1)
    } else {
        progress = 0
    }
    // CADisplayLink 回調(diào)時叫确,設(shè)置4個波浪 view 的 progress
    bigTopView.progress = progress
    bigBottomView.progress = progress
    smallTopView.progress = progress
    smallBottomView.progress = progress
}

至此,效果就完成了芍锦。

當(dāng)然竹勉,你可以用自己的動畫 view 替換掉 WaveView、改變 view 的個數(shù)娄琉、使用其他的 mask等次乓,
來實(shí)現(xiàn)自己想要的拼接效果。

當(dāng)然孽水,這種動畫方式的性能未評估票腰,也許不適合用在生產(chǎn)環(huán)境。
這個例子更多地是想讓大家回憶起一個簡單的道理:

復(fù)雜的動畫女气,可以等價為簡單動畫的組合杏慰;只要還覺得復(fù)雜,就可以繼續(xù)拆分炼鞠。

如果你想要拆分缘滥,覺得不知道從何處下手,那么可以這么嘗試:

只要某一部分的動畫和其他部分有區(qū)別谒主,就可以拆分朝扼。

簡單的動畫一旦實(shí)現(xiàn)了,組合起來了就完成了復(fù)雜的動畫霎肯。

組合是我們常用的方法擎颖,比如下圖的雙波浪:

我們當(dāng)然可以直接寫個雙波浪的類榛斯,但也可以組合兩個波浪 view 來形成雙波浪。

組合看上去有點(diǎn)傻肠仪,不過也有它的優(yōu)勢肖抱,
比如我們想復(fù)用的效果很復(fù)雜,難以改寫异旧,或者我們根本沒有該效果的源碼時意述,組合可能就是最簡單的方式。

尾聲

本系列只是展示了常見的 mask 效果吮蛹,掛一漏萬荤崇,畢竟窗無限,景無限潮针,組合無限术荤,效果無限。

如果你發(fā)現(xiàn)了有意思的 mask 動畫每篷,歡迎在評論區(qū)留言瓣戚,愿我們共同進(jìn)步。

本文所有示例焦读,在 GitHub 庫 里都有完整的代碼子库。

本系列至此完結(jié),感謝您的閱讀矗晃。

傳送門

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末仑嗅,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子张症,更是在濱河造成了極大的恐慌仓技,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件俗他,死亡現(xiàn)場離奇詭異脖捻,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)拯辙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進(jìn)店門郭变,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人涯保,你說我怎么就攤上這事≈苈祝” “怎么了夕春?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長专挪。 經(jīng)常有香客問我及志,道長片排,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任速侈,我火速辦了婚禮率寡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘倚搬。我一直安慰自己冶共,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布每界。 她就那樣靜靜地躺著捅僵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪眨层。 梳的紋絲不亂的頭發(fā)上庙楚,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天,我揣著相機(jī)與錄音趴樱,去河邊找鬼馒闷。 笑死,一個胖子當(dāng)著我的面吹牛叁征,可吹牛的內(nèi)容都是我干的纳账。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼航揉,長吁一口氣:“原來是場噩夢啊……” “哼塞祈!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起帅涂,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤议薪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后媳友,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體斯议,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年醇锚,在試婚紗的時候發(fā)現(xiàn)自己被綠了哼御。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡焊唬,死狀恐怖恋昼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情赶促,我是刑警寧澤液肌,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站鸥滨,受9級特大地震影響嗦哆,放射性物質(zhì)發(fā)生泄漏谤祖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一老速、第九天 我趴在偏房一處隱蔽的房頂上張望粥喜。 院中可真熱鬧,春花似錦橘券、人聲如沸额湘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缩挑。三九已至,卻和暖如春鬓梅,著一層夾襖步出監(jiān)牢的瞬間供置,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工绽快, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留芥丧,地道東北人。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓坊罢,卻偏偏與公主長得像续担,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子活孩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評論 2 355