├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets └── AppIcon.appiconset │ └── Contents.json ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── DoubleWaves.h ├── DoubleWaves.m ├── Info.plist ├── README.md ├── ViewController.h ├── ViewController.m └── main.m /AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // DoubleWaves 4 | // 5 | // Created by cAibDe on 2016/12/21. 6 | // Copyright © 2016年 cAibDe. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // DoubleWaves 4 | // 5 | // Created by cAibDe on 2016/12/21. 6 | // Copyright © 2016年 cAibDe. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /DoubleWaves.h: -------------------------------------------------------------------------------- 1 | // 2 | // DoubleWaves.h 3 | // DoubleWaves 4 | // 5 | // Created by cAibDe on 2016/12/21. 6 | // Copyright © 2016年 cAibDe. All rights reserved. 7 | // 8 | 9 | #import 10 | @interface DoubleWaves : UIView 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /DoubleWaves.m: -------------------------------------------------------------------------------- 1 | // 2 | // DoubleWaves.m 3 | // DoubleWaves 4 | // 5 | // Created by cAibDe on 2016/12/21. 6 | // Copyright © 2016年 cAibDe. All rights reserved. 7 | // 8 | 9 | #import "DoubleWaves.h" 10 | 11 | @interface DoubleWaves () 12 | @property (nonatomic,strong)CADisplayLink *firstWaveDisplayLink; 13 | 14 | @property (nonatomic,strong)CAShapeLayer *firstWavesLayer; 15 | 16 | @property (nonatomic,strong)UIColor *firstWavesColor; 17 | 18 | @property (nonatomic,strong)CADisplayLink *secondWaveDisplayLink; 19 | 20 | @property (nonatomic,strong)CAShapeLayer *secondWavesLayer; 21 | 22 | @property (nonatomic,strong)UIColor *secondWavesColor; 23 | 24 | @property (nonatomic,strong)UIView *sinView; 25 | 26 | @property (nonatomic,strong)UIView *cosView; 27 | 28 | 29 | @end 30 | 31 | @implementation DoubleWaves 32 | { 33 | CGFloat waveA;//水纹振幅 34 | CGFloat waveW ;//水纹周期 35 | CGFloat offsetX1; //位移 36 | CGFloat currentK; //当前波浪高度Y 37 | CGFloat offsetX2; //位移 38 | CGFloat wavesSpeed;//水纹速度 39 | CGFloat WavesWidth; //水纹宽度 40 | } 41 | - (UIView *)sinView{ 42 | if (!_sinView) { 43 | _sinView = [[UIView alloc]initWithFrame:self.bounds]; 44 | _sinView.alpha = 0.6; 45 | } 46 | return _sinView; 47 | } 48 | - (UIView *)cosView{ 49 | if (!_cosView) { 50 | _cosView = [[UIView alloc]initWithFrame:self.bounds]; 51 | _cosView.alpha = 0.6; 52 | } 53 | return _cosView; 54 | } 55 | - (instancetype)initWithFrame:(CGRect)frame{ 56 | self = [super initWithFrame:frame]; 57 | if (self) { 58 | self.backgroundColor = [UIColor clearColor]; 59 | [self addSubview:self.sinView]; 60 | [self addSubview:self.cosView]; 61 | [self setUpFirstWave]; 62 | [self setUpSecondWave]; 63 | 64 | } 65 | return self; 66 | } 67 | - (void)setUpSecondWave{ 68 | 69 | //设置波浪的宽度 70 | WavesWidth = self.frame.size.width; 71 | 72 | //第二个波浪颜色 73 | self.secondWavesColor = [UIColor colorWithRed:86/255.0f green:202/255.0f blue:139/255.0f alpha:1]; 74 | 75 | //设置波浪的速度 76 | wavesSpeed = 1/M_PI; 77 | 78 | //初始化layer 79 | if (self.secondWavesLayer == nil) { 80 | 81 | //初始化 82 | self.secondWavesLayer = [CAShapeLayer layer]; 83 | //设置闭环的颜色 84 | self.secondWavesLayer.fillColor = self.secondWavesColor.CGColor; 85 | //设置边缘线的颜色 86 | //_firstWaveLayer.strokeColor = [UIColor blueColor].CGColor; 87 | //设置边缘线的宽度 88 | //self.firstWavesLayer.lineWidth = 1.0; 89 | // self.firstWavesLayer.strokeStart = 0.0; 90 | // self.firstWavesLayer.strokeEnd = 0.8; 91 | 92 | [self.cosView.layer addSublayer:self.secondWavesLayer]; 93 | } 94 | 95 | 96 | //同正弦函数相同,无交错效果 97 | wavesSpeed = 0.02; 98 | //设置振幅 99 | waveA = 12; 100 | //设置周期 101 | waveW = 0.5/30.0; 102 | 103 | //同正弦函数不同,会有交错效果 104 | //设置波浪流动速度 105 | // wavesSpeed = 0.04; 106 | // //设置振幅 107 | // waveA = 13; 108 | // //设置周期 109 | // waveW = 0.5/30.0; 110 | 111 | 112 | //设置波浪纵向位置 113 | currentK = self.frame.size.height/2;//屏幕居中 114 | 115 | //启动定时器 116 | self.secondWaveDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(getSecondWave:)]; 117 | 118 | [self.secondWaveDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; 119 | } 120 | -(void)getSecondWave:(CADisplayLink *)displayLink{ 121 | 122 | //实时的位移 123 | //实时的位移 124 | offsetX2 += wavesSpeed; 125 | 126 | [self setSecondWaveLayerPath]; 127 | } 128 | -(void)setSecondWaveLayerPath{ 129 | 130 | //创建一个路径 131 | CGMutablePathRef path = CGPathCreateMutable(); 132 | 133 | CGFloat y = currentK; 134 | //将点移动到 x=0,y=currentK的位置 135 | CGPathMoveToPoint(path, nil, 0, y); 136 | 137 | for (NSInteger i =0.0f; i<=WavesWidth; i++) { 138 | //余弦函数波浪公式 139 | y = waveA * cos(waveW*i + offsetX2)+currentK; 140 | 141 | //如果需要正弦函数的峰顶和余弦函数的峰底对应,可以替换成下方公式均可 142 | //y = waveA * cos(waveW*i + offsetX+M_PI_2)+currentK; 143 | 144 | //y = waveA * sin(-(waveW*i + offsetX))+currentK; 145 | 146 | //将点连成线 147 | CGPathAddLineToPoint(path, nil, i, y); 148 | } 149 | 150 | CGPathAddLineToPoint(path, nil, WavesWidth, 0); 151 | CGPathAddLineToPoint(path, nil, 0, 0); 152 | 153 | CGPathCloseSubpath(path); 154 | self.secondWavesLayer.path = path; 155 | 156 | //使用layer 而没用CurrentContext 157 | CGPathRelease(path); 158 | 159 | } 160 | 161 | 162 | 163 | - (void)setUpFirstWave{ 164 | 165 | //设置波浪的宽度 166 | WavesWidth = self.frame.size.width; 167 | 168 | //第一个波浪颜色 169 | self.firstWavesColor = [UIColor colorWithRed:86/255.0f green:202/255.0f blue:139/255.0f alpha:1]; 170 | 171 | //设置波浪的速度 172 | wavesSpeed = 1/M_PI; 173 | 174 | //初始化layer 175 | if (self.firstWavesLayer == nil) { 176 | 177 | //初始化 178 | self.firstWavesLayer = [CAShapeLayer layer]; 179 | //设置闭环的颜色 180 | self.firstWavesLayer.fillColor = self.firstWavesColor.CGColor; 181 | //设置边缘线的颜色 182 | //_firstWaveLayer.strokeColor = [UIColor blueColor].CGColor; 183 | //设置边缘线的宽度 184 | //self.firstWavesLayer.lineWidth = 1.0; 185 | // self.firstWavesLayer.strokeStart = 0.0; 186 | // self.firstWavesLayer.strokeEnd = 0.8; 187 | 188 | [self.sinView.layer addSublayer:self.firstWavesLayer]; 189 | } 190 | 191 | 192 | //设置波浪流动速度 193 | wavesSpeed = 0.02; 194 | //设置振幅 195 | waveA = 12; 196 | //设置周期 197 | waveW = 0.5/30.0; 198 | 199 | //设置波浪纵向位置 200 | currentK = self.frame.size.height/2;//屏幕居中 201 | 202 | //启动定时器 203 | self.firstWaveDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(getFirstWave:)]; 204 | 205 | [self.firstWaveDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; 206 | } 207 | 208 | -(void)getFirstWave:(CADisplayLink *)displayLink{ 209 | 210 | //实时的位移 211 | //实时的位移 212 | offsetX1 += wavesSpeed; 213 | 214 | [self setFirstWaveLayerPath]; 215 | } 216 | 217 | -(void)setFirstWaveLayerPath{ 218 | 219 | //创建一个路径 220 | CGMutablePathRef path = CGPathCreateMutable(); 221 | 222 | CGFloat y = currentK; 223 | //将点移动到 x=0,y=currentK的位置 224 | CGPathMoveToPoint(path, nil, 0, y); 225 | 226 | for (NSInteger i =0.0f; i<=WavesWidth; i++) { 227 | //正弦函数波浪公式 228 | y = waveA * sin(waveW * i+ offsetX1)+currentK; 229 | 230 | //将点连成线 231 | CGPathAddLineToPoint(path, nil, i, y); 232 | } 233 | 234 | CGPathAddLineToPoint(path, nil, WavesWidth, 0); 235 | CGPathAddLineToPoint(path, nil, 0, 0); 236 | 237 | CGPathCloseSubpath(path); 238 | self.firstWavesLayer.path = path; 239 | 240 | //使用layer 而没用CurrentContext 241 | CGPathRelease(path); 242 | 243 | } 244 | 245 | 246 | -(void)dealloc 247 | { 248 | [self.firstWaveDisplayLink invalidate]; 249 | [self.secondWaveDisplayLink invalidate]; 250 | } 251 | 252 | @end 253 | -------------------------------------------------------------------------------- /Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DoubleWaves 2 | 一个正弦&&余弦波纹图 3 | 4 | #创作来源 5 | 双十一买东西的时候突然发现淘宝的顶部有一个微小的动画效果,感觉很炫。 6 | 实现这个效果的方案就是在一个View上面添加两个View 7 | 一个是sin函数图,另一个是cos函数图 8 | 9 | #使用方法 10 | ```objc 11 | DoubleWaves *doubleWaves = [[DoubleWaves alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 26)]; 12 | [self.view addSubview:doubleWaves]; 13 | ``` 14 | 就是最简单的创建视图的方法就可以了。 15 | 16 | #参数 17 | CGFloat waveA;//水纹振幅 18 | CGFloat waveW ;//水纹周期 19 | CGFloat offsetX1; //位移 20 | CGFloat currentK; //当前波浪高度Y 21 | CGFloat offsetX2; //位移 22 | CGFloat wavesSpeed;//水纹速度 23 | CGFloat WavesWidth; //水纹宽度 24 | 可以根据自己项目的需求,去调整相应的参数,实现相应的效果。 25 | 26 | #更新 27 | 对于懒加载的cosView 有一点小小的bug 但是已经更新 28 | -------------------------------------------------------------------------------- /ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // DoubleWaves 4 | // 5 | // Created by cAibDe on 2016/12/21. 6 | // Copyright © 2016年 cAibDe. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // DoubleWaves 4 | // 5 | // Created by cAibDe on 2016/12/21. 6 | // Copyright © 2016年 cAibDe. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "DoubleWaves.h" 11 | 12 | @interface ViewController () 13 | @property (nonatomic, strong) DoubleWaves *wavesView; 14 | 15 | @end 16 | 17 | @implementation ViewController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | // Do any additional setup after loading the view, typically from a nib. 22 | 23 | DoubleWaves *doubleWaves = [[DoubleWaves alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 26)]; 24 | [self.view addSubview:doubleWaves]; 25 | } 26 | 27 | 28 | - (void)didReceiveMemoryWarning { 29 | [super didReceiveMemoryWarning]; 30 | // Dispose of any resources that can be recreated. 31 | } 32 | 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // DoubleWaves 4 | // 5 | // Created by cAibDe on 2016/12/21. 6 | // Copyright © 2016年 cAibDe. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | --------------------------------------------------------------------------------