網(wǎng)易新聞個(gè)人頁面的水波效果

網(wǎng)易新聞客戶端的這個(gè)水波效果出來很久了,我考慮了很長時(shí)間該如何實(shí)現(xiàn),但是都沒有很好的辦法室埋,幸好在一個(gè)動畫牛人daixunry的文章里,知道了實(shí)現(xiàn)這個(gè)動畫最關(guān)鍵的點(diǎn)伊约。他的blog上面有許多優(yōu)秀的動畫案例姚淆,非常值的學(xué)習(xí),blog的地址是http://www.reibang.com/p/272aa1f26c62

329672-8235dc58632c0963.gif

這個(gè)動畫的關(guān)鍵點(diǎn)就是正余弦函數(shù)屡律。在聽到這個(gè)的時(shí)候腌逢,我非常的震驚,原因是正余弦我們當(dāng)初在高中的時(shí)候?qū)W習(xí)的知識疹尾,不過從來沒有想過這些高中書本的知識竟然運(yùn)用到了實(shí)際上忍,非常的佩服kittenyang,同時(shí)感覺非常羞愧的纳本,高中的知識都還給老師了窍蓝,連正余弦的公式都忘記了。不熟悉的同學(xué)也可以去復(fù)習(xí)一下繁成。

正弦型函數(shù)解析式:y=Asin(ωx+φ)+h
各常數(shù)值對函數(shù)圖像的影響:
φ(初相位):決定波形與X軸位置關(guān)系或橫向移動距離(左加右減)
ω:決定周期(最小正周期T=2π/|ω|)
A:決定峰值(即縱向拉伸壓縮的倍數(shù))
h:表示波形在Y軸的位置關(guān)系或縱向移動距離(上加下減)

拆解和分析

好了吓笙,我們還是來拆解一下這個(gè)動畫吧。兩個(gè)波浪是兩個(gè)正弦函數(shù)的效果疊加巾腕。首先我們看看該如何繪制一個(gè)波的曲線面睛,如下圖:


329672-0cb75e6c6e4f120b.jpeg

我們知道絮蒿,計(jì)算機(jī)不可能繪制出一條完美的曲線,如果放大到像素的級別叁鉴,可以看到這些曲線其實(shí)都是柵格的像素點(diǎn)組成土涝。我們只能最大化的接近曲線,達(dá)到肉眼無法分辨的程度幌墓。如果想繪制出來一條正弦函數(shù)曲線但壮,可以沿著假想的曲線繪制許多個(gè)點(diǎn),然后把點(diǎn)逐一用直線連在一起常侣,如果點(diǎn)足夠多蜡饵,就可以得到一條滿足需求的曲線,這也是一種微分的思想胳施。而這些點(diǎn)的位置可以通過正弦函數(shù)的解析式求得溯祸。

如果要繪制上面這個(gè)曲線,可以觀察:波的峰值是1舞肆,周期是2π焦辅,初相位是0,h位移也是0胆绊。那么計(jì)算各個(gè)點(diǎn)的坐標(biāo)公式就是y = sin(x);獲得各個(gè)點(diǎn)的坐標(biāo)之后氨鹏,使用CGPathAddLineToPoint這個(gè)函數(shù),把這些點(diǎn)逐一連成線压状,就可以得到最后的路徑仆抵。

接下來問題來了,我們已經(jīng)繪制了一條靜態(tài)的曲線种冬,如何讓它形成一個(gè)流動的波呢镣丑?

可以這么思考:初始的曲線如上面所示,1s之后娱两,希望曲線能成為下個(gè)形態(tài):

329672-d708b887c2bbf1ee.png

接著莺匠,2s、3s...十兢,曲線分別在不停的變化趣竣,如下圖:


329672-c5856d9bc788eb54.png

那么隨著時(shí)間的流逝,這個(gè)曲線在不停的起伏變化旱物,就形成了波動的效果遥缕。我們認(rèn)真的想想,波動其實(shí)就是每一個(gè)點(diǎn)的y坐標(biāo)都在不停的做著周期變化宵呛,想要實(shí)現(xiàn)上圖1s之后的曲線形態(tài)单匣,需要設(shè)置上面公式中的φ常量(初相位),假如φ是π/2,那么y=sin(x+φ)在x=0位置的時(shí)候户秤,y的值就不在是0码秉,而是1,就得到一條變化的曲線鸡号。通過上面的分析转砖,我們知道,需要建立一個(gè)時(shí)間和φ的函數(shù)鲸伴。

我們可以創(chuàng)建一個(gè)定時(shí)器(當(dāng)然做動畫我們肯定不會使用計(jì)時(shí)器堪藐,這里舉個(gè)例子,下面詳解)挑围,假設(shè)每秒讓φ自增π/2,這樣第4s的時(shí)候糖荒,φ等于2π(一個(gè)周期)杉辙,y=sin(x+2π)和y=sin(x)等效,又回到了初初始狀態(tài)捶朵,這樣就完成了一個(gè)波動周期蜘矢,往下繼續(xù)加下去,不停的往復(fù)這個(gè)波動周期動畫综看。

如果我們希望波動的非常劇烈品腹,也就是波流速很快,那么我們可以讓初相位隨著時(shí)間的函數(shù)波動更快红碑,就可以實(shí)現(xiàn)了舞吭。

代碼實(shí)現(xiàn)

把上面的原理落實(shí)到我們需要制作的動畫上面。首先要總結(jié)出一個(gè)公式析珊,確定正弦型函數(shù)解析式:y=Asin(ωx+φ)+h中各個(gè)常數(shù)的值羡鸥。這里需要注意UIKit的坐標(biāo)系統(tǒng)y軸是向下延伸。

    1忠寻、我們的容器高度是100惧浴,我希望波的整體高度,固定在容器的一個(gè)相對的位置奕剃。
      這里設(shè)置h = 30衷旅;也就是說,當(dāng)Asin(ωx+φ)計(jì)算為0的時(shí)候纵朋,這個(gè)時(shí)候y的位置是30柿顶;
    2、決定波起伏的高度倡蝙,我們設(shè)置波峰是5九串,波峰越大,曲線越陡峭;
    3猪钮、決定波的寬度和周期品山,比如,我們可以看到上面的例子中是一個(gè)周期的波曲線烤低,
      一個(gè)波峰肘交、一個(gè)波谷,如果我們想在0到2π這個(gè)距離顯示2個(gè)完整的波曲線扑馁,那么周期就是π涯呻。
      我們這里設(shè)置波的寬度是容器的寬度_waveWidth,希望能展示2.5個(gè)波曲線腻要,周期就是_waveWidth/2.5复罐。
      那么ω常量就可以這樣計(jì)算:2.5*M_PI/_waveWidth。
    4雄家、一共有兩個(gè)波曲線效诅,形成一個(gè)落差,也就是設(shè)置不同的φ(初相位)趟济,我們這里設(shè)置落差是M_PI/4乱投。
    5、時(shí)間和初相位的函數(shù)關(guān)系:我們在計(jì)時(shí)器的函數(shù)中一直調(diào)用_offset += _speed;
      可以看到顷编,如果我們設(shè)置波的速度speed越大戚炫,波的震動將會越快。

    最后我們的公式如下:
    CGFloat y = _waveHeight*sinf(2.5*M_PI*i/_waveWidth + 3*_offset*M_PI/_waveWidth + M_PI/4) + _h;
    這些參數(shù)都可以自己調(diào)整媳纬,得到一個(gè)符合要求的效果双肤。

現(xiàn)在我們解決了項(xiàng)目中最有難度的問題,剩下的事情就非常簡單了钮惠。兩個(gè)波是兩個(gè)CAShapeLayer杨伙。我們使用CADisplayLink而不是計(jì)時(shí)器來驅(qū)動動畫,因?yàn)镃ADisplayLink觸發(fā)的時(shí)機(jī)是每隔一幀運(yùn)行一次萌腿,而NSTimer不是很精確限匣,會有阻塞的情況,照成動畫卡頓的現(xiàn)象毁菱。

- (void)wave
{
    _link = [CADisplayLink displayLinkWithTarget:self selector:@selector(doAni)];
    [_link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}

- (void)doAni
{
    _offset += _speed;
    //設(shè)置第一條波曲線的路徑
    CGMutablePathRef pathRef = CGPathCreateMutable();
    //起始點(diǎn)
    CGFloat startY = _waveHeight*sinf(_offset*M_PI/_waveWidth);
    CGPathMoveToPoint(pathRef, NULL, 0, startY);
    //第一個(gè)波的公式
    for (CGFloat i = 0.0; i < _waveWidth; i ++) {
        CGFloat y = 1.1*_waveHeight*sinf(2.5*M_PI*i/_waveWidth + _offset*M_PI/_waveWidth) + _h;
        CGPathAddLineToPoint(pathRef, NULL, i, y);
    }
    CGPathAddLineToPoint(pathRef, NULL, _waveWidth, 40);
    CGPathAddLineToPoint(pathRef, NULL, 0, 40);
    CGPathCloseSubpath(pathRef);
    //設(shè)置第一個(gè)波layer的path
    _layer.path = pathRef;
    _layer.fillColor = [UIColor lightGrayColor].CGColor;
    CGPathRelease(pathRef);

    //設(shè)置第二條波曲線的路徑
    CGMutablePathRef pathRef2 = CGPathCreateMutable();
    CGFloat startY2 = _waveHeight*sinf(_offset*M_PI/_waveWidth + M_PI/4);
    CGPathMoveToPoint(pathRef2, NULL, 0, startY2);
    //第二個(gè)波曲線的公式
    for (CGFloat i = 0.0; i < _waveWidth; i ++) {
        CGFloat y = _waveHeight*sinf(2.5*M_PI*i/_waveWidth + 3*_offset*M_PI/_waveWidth + M_PI/4) + _h;
        CGPathAddLineToPoint(pathRef2, NULL, i, y);
    }
    CGPathAddLineToPoint(pathRef2, NULL, _waveWidth, 40);
    CGPathAddLineToPoint(pathRef2, NULL, 0, 40);
    CGPathCloseSubpath(pathRef2);

    _layer2.path = pathRef2;
    _layer2.fillColor = [UIColor lightGrayColor].CGColor;
    CGPathRelease(pathRef2);
}

我們可以看到米死,兩個(gè)波曲線不但初相位不同,形成一個(gè)落差贮庞,而且相位隨著時(shí)間的改變速度也不同峦筒,帶來兩個(gè)波的流速不同的視覺差異。CADisplayLink每幀都會調(diào)用wave方法窗慎,wave不停的改變著offset的值物喷,也就是改變著初相位卤材,最后形成了波動動畫。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末峦失,一起剝皮案震驚了整個(gè)濱河市扇丛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌尉辑,老刑警劉巖帆精,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異隧魄,居然都是意外死亡卓练,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進(jìn)店門购啄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來襟企,“玉大人,你說我怎么就攤上這事狮含≌海” “怎么了?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵辉川,是天一觀的道長。 經(jīng)常有香客問我拴测,道長乓旗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任集索,我火速辦了婚禮屿愚,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘务荆。我一直安慰自己妆距,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布函匕。 她就那樣靜靜地躺著娱据,像睡著了一般。 火紅的嫁衣襯著肌膚如雪盅惜。 梳的紋絲不亂的頭發(fā)上中剩,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天,我揣著相機(jī)與錄音抒寂,去河邊找鬼结啼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛屈芜,可吹牛的內(nèi)容都是我干的郊愧。 我是一名探鬼主播朴译,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼属铁!你這毒婦竟也來了眠寿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤红选,失蹤者是張志新(化名)和其女友劉穎澜公,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體喇肋,經(jīng)...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡坟乾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蝶防。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片甚侣。...
    茶點(diǎn)故事閱讀 38,664評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖间学,靈堂內(nèi)的尸體忽然破棺而出殷费,到底是詐尸還是另有隱情,我是刑警寧澤低葫,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布详羡,位于F島的核電站,受9級特大地震影響嘿悬,放射性物質(zhì)發(fā)生泄漏实柠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一善涨、第九天 我趴在偏房一處隱蔽的房頂上張望窒盐。 院中可真熱鬧,春花似錦钢拧、人聲如沸蟹漓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽葡粒。三九已至,卻和暖如春膜钓,著一層夾襖步出監(jiān)牢的瞬間塔鳍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工呻此, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留轮纫,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓焚鲜,卻偏偏與公主長得像掌唾,于是被迫代替她去往敵國和親放前。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評論 2 349

推薦閱讀更多精彩內(nèi)容