iOS 波浪曲線的繪製

weixin_33935777發表於2017-02-27

iOS 波浪曲線的繪製

還是先上圖:

波浪

其實之前就一直想看一下 iOS 關於曲線繪製的相關類容,但由於剛剛步入職場,事情比較多。所以也沒什麼時間來看這些。剛好前段時間有空看了下,做了個 Demo,就上面那個左右搖擺的頭像。由於現在是在一家外包公司,也不追求什麼炫酷的特效,之前有個做 Android 的同事說研究這些純粹是浪費時間,但是我覺得吧,其實並不是這樣的,像我們 iOS 和 Android 做前端開發的, 80%的時間是在寫介面,還是很有必要了解一下這些影象究竟是如何展現在螢幕上的,我們又該如何去控制顯示的內容呢?

其實這方面的資料有很多,網上一搜一大把,主要還是要花時間去看。我們要分清楚 UIView 和 CALayer 之間的區別和聯絡,這裡我就不細說了,好了,步入正題:如何完成上圖所展示的效果呢?

讓我們來分解一下:要實現上圖的效果要實現哪些東西:

  • 實現一個波浪曲線
  • 加入一個頭像,並根據曲線的浮動調整頭像的位置
  • 更具曲線的切線斜率調整頭像的左右偏移

只需要完成上面三個步驟,這個效果就實現了。首先,我們來看第一個:如何實現一個波浪曲線。

實現一個波浪曲線

像這樣的一條曲線,肯定是需要我們手動去繪製的,然後設定一個定時器,再去繪製它下一次出現的位置,但是我們該如何去繪製這樣的波浪曲線呢?

仔細看一下,這個曲線是不是很眼熟?是的,就是我們高中還是初中所學的正弦曲線,來想一下正弦曲線是怎麼畫的。

2972380-0ebba7f3166562ab.png
y=Asin(wx+u)+K

在想一下通式:y=Asin(wx+u)+K 對就是這麼簡單,那引數代表的又是什麼呢?

  • A :振幅
  • w :角速度 w = 2PI/T T:週期
  • u :初相
  • K :y 軸偏移

有了這個公式,我們又該如何在螢幕上繪製正弦曲線呢?

    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, nil, 0, 0);

    CGFloat y = 0.0f;
    
    for (float x = 0.f; x <= self.width; x++) {
        y = self.A1 * sin(self.w*x + self.u1) + self.K1;
        CGPathAddLineToPoint(path, nil, x, y);
        x++;
    }

    CGPathAddLineToPoint(path, nil, self.width, self.height);
    CGPathAddLineToPoint(path, nil, 0, self.height);
    CGPathCloseSubpath(path);
    self.waveLayer1.path = path;
    CGPathRelease(path);

Build 執行一下,是不是就有了一個正弦曲線呢?接下來就要讓正弦曲線動起來,這就要設定定時器去重繪這條曲線了。

CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(wave)];
        [displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
- (void)wave {

    offset += self.offsetX;
    
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, nil, 0, (self.A1*sin(offset + self.u1) + self.K1));
    
    CGFloat y = 0.0f;
    
    for (float x = 0.f; x <= self.width; x++) {
        y = self.A1 * sin(self.w*x + offset + self.u1) + self.K1;
        CGPathAddLineToPoint(path, nil, x, y);
        x++;
    }
    
    CGPathAddLineToPoint(path, nil, self.width, self.height);
    CGPathAddLineToPoint(path, nil, 0, self.height);
    CGPathCloseSubpath(path);
    self.waveLayer1.path = path;
    CGPathRelease(path);

}

offset : 波浪曲線的初始偏移量

​ offsetX : 波浪曲線每次重新整理偏移量

再附一個引數初始化:

- (void)initData {
    
    self.A1 = 8;
  
    self.K1 = 80;
  
    self.u1 = M_PI*3/8.0f;
    
    self.offsetX = 0.03;
    offset = 0;
    
    self.w = M_PI/180;

}

這樣波浪曲線就完成了。

新增頭像

初始化一個 UIImageView 放在螢幕中間,每次重新整理波浪曲線的時候,都去改變頭像位置

- (void)wave {

    offset += self.offsetX;
    
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, nil, 0, (self.A1*sin(offset + self.u1) + self.K1));
    
    CGFloat y = 0.0f;
    
    for (float x = 0.f; x <= self.width; x++) {
        y = self.A1 * sin(self.w*x + offset + self.u1) + self.K1;
        CGPathAddLineToPoint(path, nil, x, y);
        x++;
    }
  
    if (self.floatImageView != nil) {
    
        y = self.A1 * sin(self.w*(self.width/2.0 ) + offset + self.u1) + self.K1;
        self.floatImageView.centerY = y - 30;
    }
  
    CGPathAddLineToPoint(path, nil, self.width, self.height);
    CGPathAddLineToPoint(path, nil, 0, self.height);
    CGPathCloseSubpath(path);
    self.waveLayer1.path = path;
    CGPathRelease(path);

}

頭像偏移

關於頭像的偏移,這裡需要算出頭像偏移角度,也就是與 X 軸的夾角。我們需要先算出正弦曲線在中間點的斜率。關於切線斜率怎麼求呢? 高數老師告訴你:對函式求導後 x 處的值為斜率。那麼對 y=Asin(wx+u)+K 求導:

y`= Awcos(wx + u)

獲得螢幕中心的斜率為 k, 根據公式 k = tana

角度:a = arctank

這個角度便是我們需要的頭像的偏移角度了,加在迴圈裡面,每次再改變頭像的角度。

- (void)wave {

    offset += self.offsetX;
    
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, nil, 0, (self.A1*sin(offset + self.u1) + self.K1));
    
    CGFloat y = 0.0f;
    
    for (float x = 0.f; x <= self.width; x++) {
        y = self.A1 * sin(self.w*x + offset + self.u1) + self.K1;
        CGPathAddLineToPoint(path, nil, x, y);
        x++;
    }
  
    if (self.floatImageView != nil) {
    
        y = self.A1 * sin(self.w*(self.width/2.0 ) + offset + self.u1) + self.K1;
        self.floatImageView.centerY = y - 30;
      
        CGFloat angel = atan(self.A1*(M_PI/60)*cos(self.w*(ScreenWidth/2.0f) + offset + self.u1));
        
        self.floatImageView.transform = CGAffineTransformMakeRotation(angel / M_PI);
    }
  
    CGPathAddLineToPoint(path, nil, self.width, self.height);
    CGPathAddLineToPoint(path, nil, 0, self.height);
    CGPathCloseSubpath(path);
    self.waveLayer1.path = path;
    CGPathRelease(path);

}

搞定!

相關文章