之前看貼吧的加載動畫很有意思拿撩。就打算也做一個自定義的衣厘,最后的效果:
分層
首先要解決的問題是:怎么讓一個字體以中間上下層的顏色不同,并隨著波浪也會改變顏色压恒。
- 第一個想法就是漸變層CAGradientLayer 影暴,CAGradientLayer可以做到讓一個字體上下層顏色變化,但是也無法做到隨著波浪的區(qū)域變化探赫,顏色也隨著變型宙,這個想法就給pass了。
- 然后伦吠,就想到了之前做歌詞滾動的時候就可以讓歌詞的顏色隨時間滾動妆兑,原理是兩層label,改變外邊label層 mask的bounds 達到歌詞從左到右的滾動讨勤。mask層決定一個視圖的需要顯示的大小箭跳。博客鏈接:點我.
- 這樣,變換個方向,就可以做到上下的分層顯示潭千。然后谱姓,開始思索怎么樣讓mask層顯示大小為波浪的大小,自然就想到了CAShapeLayer ∨偾纾現(xiàn)在做一個上下分層的
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(0, 0)];
[path addLineToPoint:CGPointMake(0, w/2)];//w表示園的直徑
[path addLineToPoint:CGPointMake(w, w/2)];
[path addLineToPoint:CGPointMake(w, 0)];
[path closePath];
layer = [CAShapeLayer layer];
layer.frame = label.bounds;
layer.path = path.CGPath;
layer.lineWidth = 1.f;
layer.strokeColor = [UIColor greenColor].CGColor;
//label是里面藍色背景白色字體屉来,label2是外面白色背景藍色字體.
label2.layer.mask = layer;
效果:
波浪動畫
接下來就可以畫波浪形了,有兩種方法狈癞,一種茄靠,用貝塞爾曲線。另一種用正弦函數(shù)(祭奠我那死去的數(shù)學知識),在看過daixunry(簡書作者)關于波浪的博客:點我蝶桶】考慮需要做波浪動畫 就用正弦函數(shù)去畫了。
下面照抄一下daixunry博客里面的一些函數(shù)解釋
正弦型函數(shù)解析式:y=Asin(ωx+φ)+h
各常數(shù)值對函數(shù)圖像的影響:
φ(初相位):決定波形與X軸位置關系或橫向移動距離(左加右減)
ω:決定周期(最小正周期T=2π/|ω|)
A:決定峰值(即縱向拉伸壓縮的倍數(shù))
h:表示波形在Y軸的位置關系或縱向移動距離(上加下減)
大致就是在時間增加的時候把初相位往前或者往后移動,使得畫面看起來像波浪一樣脐雪。
公式里面參數(shù)的設定
1厌小、我們的容器高度是100,我希望波的整體高度战秋,固定在容器的一個相對的位置璧亚。
這里設置h = 30;也就是說脂信,當Asin(ωx+φ)計算為0的時候癣蟋,這個時候y的位置是30;
2狰闪、決定波起伏的高度疯搅,我們設置波峰是5,波峰越大尝哆,曲線越陡峭秉撇;
3、決定波的寬度和周期秋泄,比如琐馆,我們可以看到上面的例子中是一個周期的波曲線,
一個波峰恒序、一個波谷瘦麸,如果我們想在0到2π這個距離顯示2個完整的波曲線,那么周期就是π歧胁。
我們這里設置波的寬度是容器的寬度_waveWidth滋饲,希望能展示2.5個波曲線,周期就是_waveWidth/2.5喊巍。
那么ω常量就可以這樣計算:2.5*M_PI/_waveWidth屠缭。
4、一共有兩個波曲線崭参,形成一個落差呵曹,也就是設置不同的φ(初相位),我們這里設置落差是M_PI/4何暮。
5奄喂、時間和初相位的函數(shù)關系:我們在計時器的函數(shù)中一直調(diào)用_offset += _speed;
可以看到,如果我們設置波的速度speed越大海洼,波的震動將會越快跨新。
最后我們的公式如下:
CGFloat y = _waveHeight*sinf(2.5*M_PI*i/_waveWidth + 3*_offset*M_PI/_waveWidth + M_PI/4) + _h;
這些參數(shù)都可以自己調(diào)整,得到一個符合要求的效果坏逢。
現(xiàn)在來看代碼
//一些配置
_waveWidth = w;//w位圓的直徑
_waveHeight = 6;//振幅
_h = w/2;//圓的中心
_speed = 6.f;
- (void)wave
{
_link = [CADisplayLink displayLinkWithTarget:self selector:@selector(doAni)];
[_link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)doAni
{
//加減決定正反方向域帐,也可以speed為負的
_offset += _speed;
//設置第一條波曲線的路徑
CGMutablePathRef pathRef = CGPathCreateMutable();
//起始點
CGFloat startY = _waveHeight*sinf(_offset*M_PI/_waveWidth) + _h;
CGPathMoveToPoint(pathRef, NULL, 0, startY);
//第一個波的公式
for (CGFloat i = 0.0; i < _waveWidth; i ++) {
CGFloat y = 1.1*_waveHeight*sinf(2*M_PI*i/_waveWidth + _offset*M_PI/_waveWidth) + _h;
CGPathAddLineToPoint(pathRef, NULL, i, y);
}
//上邊是畫波浪線赘被,下邊是畫邊界,直接用直線就可以了
CGPathAddLineToPoint(pathRef, NULL, _waveWidth, 0);
CGPathAddLineToPoint(pathRef, NULL, 0, 0);
CGPathCloseSubpath(pathRef);
//設置第一個波layer的path
layer.path = pathRef;
layer.fillColor = [UIColor lightGrayColor].CGColor;
CGPathRelease(pathRef);
}
先手動調(diào)用一次doAni
OK俯树,然后調(diào)用
wave
啟動計時器改變offset值就可以讓波浪形形成向前或向后移動的動畫帘腹。現(xiàn)在波浪動畫完成了,字體的顏色也會隨波浪的動畫而改變许饿。這就完了嗎? 當然還沒有舵盈,仔細看貼吧的水波浪效果 會發(fā)現(xiàn)它還有一層立體效果陋率,使水波浪看起來有一種立體感。
立體感
在之前的基礎上思考如何再做出這一層立體感秽晚,很明顯的發(fā)現(xiàn)有兩層波浪瓦糟,兩個波浪重疊的部分的顏色會更藍色,字體顏色會偏灰色赴蝇。那我現(xiàn)在把下層的label也做一個波浪菩浙,兩個波浪重疊后鏤空的那部分就是要顯示立體效果。很簡單句伶,在底層再加一個label劲蜻, label的背景色和字體色就是你想要的立體效果。OK 這樣效果就完全達到了考余。
首先再填加一個label(深藍色背景,灰色字體,這里不貼代碼了)先嬉,然后先給下邊的label(藍色背景白色字體)添加一個layer 控制
layer2 = [CAShapeLayer layer];
layer2.frame = label.bounds;
layer2.path = path.CGPath;
layer2.strokeColor = [UIColor clearColor].CGColor;
label.layer.mask = layer2;
然后在doAni
里面添加一個label的路徑變化,在初相位上比上層的label快了M_PI/3
;
//設置第二條波曲線的路徑
CGMutablePathRef pathRef2 = CGPathCreateMutable();
CGFloat startY2 = _waveHeight*sinf(_offset*M_PI/_waveWidth + M_PI/3)+_h;
CGPathMoveToPoint(pathRef2, NULL, 0, startY2);
//第二個波曲線的公式
for (CGFloat i = 0.0; i < _waveWidth; i ++) {
CGFloat y = 1.1 *_waveHeight*sinf(2*M_PI*i/_waveWidth + 1*_offset*M_PI/_waveWidth + M_PI/3) + _h;
CGPathAddLineToPoint(pathRef2, NULL, i, y);
}
CGPathAddLineToPoint(pathRef2, NULL, _waveWidth, label.frame.size.height);
CGPathAddLineToPoint(pathRef2, NULL, 0, label.frame.size.height);
CGPathCloseSubpath(pathRef2);
layer2.path = pathRef2;
layer2.fillColor = [UIColor blackColor].CGColor;
CGPathRelease(pathRef2);
最后來看看效果:
Demo項目地址:https://github.com/yxsufaniOS/SFWaterLoadingView
喜歡就收藏一下吧楚堤,謝謝疫蔓。
下面貼一下一些常用函數(shù)
算術函數(shù)
函數(shù)名 | 說明 |
---|---|
int rand() | 隨機數(shù)生成。 調(diào)用之前需要srand((unsigned)time(0)); //隨機數(shù)初期化身冬,不然隨機數(shù)不會變 |
int abs(int a) | 整數(shù)的絕對值 |
double fabs(double a) | 浮點數(shù)的絕對值 |
double floor(double a) | 返回浮點數(shù)整數(shù)部分(舍棄小數(shù)點) |
double ceil(double a) | 返回浮點數(shù)整數(shù)部分(舍棄小數(shù)點部分衅胀,往個位數(shù)進1) |
double pow(double a, double b) | a的b次方 |
double sqrt(double a) | a的平方根 |
三角函數(shù)
函數(shù)名 | 說明 |
---|---|
double cos(double a) | 余弦函數(shù) (a:弧度) |
double sin(double a) | 正弦函數(shù)∷煮荨(a:弧度) |
double tan(double a) | 正切函數(shù)」銮(a:弧度) |
double asin(double a) | 反正弦值 (a:弧度) |
double acos(double a) | 反余弦函數(shù)(a:弧度) |
double atan(double a) | 反正切函數(shù) |
double atan2(double a, double b) | 返回給定的 a 及 b 坐標值的反正切值 |
指數(shù)函數(shù)
函數(shù)名 | 說明 |
---|---|
double log(double a) | 以e 為底的對數(shù)值 |
double log10(double a) | 對數(shù)函數(shù)log |
常數(shù)
常數(shù)名 | 說明 |
---|---|
M_PI | 圓周率(=π) |
M_PI_2 | 圓周率的1/2(=π/2) |
M_PI_4 | 圓周率的1/4(=π/4) |
M_1_PI | =1/π |
M_2_PI | =2/π |
M_E | =e |
M_LOG2E | log_2(e) |
M_LOG10E | log_10(e) |