iOS开发

iOS 波浪效果(根据正弦函数)

2017-07-08  本文已影响32人  找不到好的ID

当时写这个demo,是现公司出的面试题,题目的大致意思是通过随机数映射成实物,例如0~255,可以映射成颜色,然后问还有没有其他好的点子。
我当时想了很多的点子,我开始想模仿 GarageBand ,自己组合一些乐器来变成不同的音乐,但是我发现实现起来有很多的难点,可能来不及完成,就放弃了。还想了一些点子,但是没有算法支持,也放弃了,最后我还是决定通过已有的函数来写个 demo,最下面有 demo 的地址,可以感受一下,波形与音乐结合起来比较优美。

根据音乐中产生的分贝数值映射成波浪线的振幅
原理说明:
1.根据正弦函数:f(x) = Asin(2πωx+φ);
2.设置默认周期 T = 1;
3.曲线往右移平移,φ值需要减小;
4.如果φ值每次都减小固定的数值,视觉上看起来曲线是匀速往右移动;
#import <UIKit/UIKit.h>

@interface SoundWaveView : UIView

@property (nonatomic, copy) void (^levelCallback)(SoundWaveView * waveView);
@property (nonatomic, strong) CADisplayLink *displayLink;
@property (nonatomic, assign) CGFloat level;

@end
#import "SoundWaveView.h"

@interface SoundWaveView ()
//相位变化,用来移动曲线
@property (nonatomic, assign) CGFloat offX;
//存放线的数组
@property (nonatomic, strong) NSMutableArray * linesArr;
//高度
@property (nonatomic, assign) CGFloat maxHeight;
//宽度
@property (nonatomic, assign) CGFloat maxWidth;
//最大振幅
@property (nonatomic, assign) CGFloat maxAmplitude;
//振幅系数
@property (nonatomic, assign) CGFloat amplitudeLevel;

@end

@implementation SoundWaveView

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        [self setup];
    }
    return self;
}

- (void)setup
{
    self.linesArr = [[NSMutableArray alloc]init];
    self.maxHeight = CGRectGetHeight(self.bounds);
    self.maxWidth = CGRectGetWidth(self.bounds);
    self.maxAmplitude = self.maxHeight - 2;
    self.amplitudeLevel = 1;
}

- (void)setLevelCallback:(void (^)(SoundWaveView *waveView))levelCallback
{
    _levelCallback = levelCallback;
    [self.displayLink invalidate];
    self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(invokeLevelCallback)];
    [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    for(int i=0; i < 5; i++){
        CAShapeLayer *line = [CAShapeLayer layer];
        line.fillColor = [[UIColor clearColor] CGColor];
        line.lineCap = kCALineCapSquare;
        line.lineJoin = kCALineJoinRound;
        line.lineWidth = i == 0 ? 2 : 1;
        CGFloat progress = 1.0f - (CGFloat)i / 5;
        UIColor *color = [[UIColor whiteColor]colorWithAlphaComponent:(i == 0 ? 1.0 : progress *progress)];
        line.strokeColor = color.CGColor;
        [self.layer addSublayer:line];
        [self.linesArr addObject:line];
    }
}

- (void)invokeLevelCallback
{
    self.levelCallback(self);
}

- (void)setLevel:(CGFloat)level
{
    _level = level;
    self.offX -= 0.25f;
    self.amplitudeLevel = fmax(level, 0.01f);
    [self refreshLines];
}

- (void)refreshLines
{
    UIGraphicsBeginImageContext(self.frame.size);
    for(int i=0; i < 5; i++) {
        UIBezierPath *wavelinePath = [UIBezierPath bezierPath];
        CGFloat progress = 1.0f - (CGFloat)i / 5;
        CGFloat nowAmplitudeLevel = (1.5f * progress - 0.5f) * self.amplitudeLevel;
        for(CGFloat x = 0; x < self.maxWidth; x ++) {
            CGFloat midScale = 1 - pow(x / (self.maxWidth / 2)  - 1, 2);
            CGFloat y = midScale * self.maxAmplitude * nowAmplitudeLevel * sinf(2 * M_PI *(x / self.maxWidth) * 1 + self.offX) + (self.maxHeight * 0.5);
            if (x==0) {
                [wavelinePath moveToPoint:CGPointMake(x, y)];
            }
            else {
                [wavelinePath addLineToPoint:CGPointMake(x, y)];
            }
        }
        CAShapeLayer *waveline = [self.linesArr objectAtIndex:i];
        waveline.path = [wavelinePath CGPath];
    }
    UIGraphicsEndImageContext();
}

- (void)dealloc
{

}

@end
其中的 pow 函数(1 - pow(x / (self.maxWidth / 2)  - 1, 2))
是为了让越靠近屏幕中间的波形振幅越明显。

demo地址:http://git.oschina.net/Fucc/soundwave

上一篇下一篇

猜你喜欢

热点阅读