美文网首页iOS开发
iOS 波浪效果(根据正弦函数)

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

作者: 找不到好的ID | 来源:发表于2017-07-08 21:16 被阅读32次

    当时写这个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

    相关文章

      网友评论

        本文标题:iOS 波浪效果(根据正弦函数)

        本文链接:https://www.haomeiwen.com/subject/hihbgttx.html