效果如圖:
魚的最大數(shù)量可控制跪帝,運(yùn)動(dòng)速度隨機(jī),大小隨機(jī)些阅,擺尾速度隨機(jī),運(yùn)動(dòng)軌跡隨機(jī)斑唬,氣泡個(gè)數(shù)隨機(jī)市埋,氣泡位置隨機(jī),出現(xiàn)時(shí)間隨機(jī)恕刘。
1.先寫一個(gè)波浪效果:TBMyInfoTopView.swift
import UIKit
class TBMyInfoTopView: UIView {
//提供給外部調(diào)用接口
///曲線振幅
var waterAmplitude: CGFloat = 8
///曲線角速度
var waterPalstance: CGFloat = 0
///曲線初相
var waterX: CGFloat = 0
///曲線偏距
let waterY: CGFloat = 100
///曲線移動(dòng)速度
var waterMoveSpeed: CGFloat = 0
//內(nèi)部接口
fileprivate let waterColor = UIColor(red: 118/255.0, green: 165/255.0, blue: 242/255.0, alpha: 0.6)
fileprivate let BackGroundColor = UIColor(red: 80/255.0, green: 140/255.0, blue: 238/255.0, alpha: 1)
//前面的波浪
fileprivate let waterLayer1 = CAShapeLayer()
fileprivate let waterLayer2 = CAShapeLayer()
fileprivate var disPlayLink = CADisplayLink()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = BackGroundColor
buildInterface()
buildData()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func buildInterface(){
//初始化波浪
waterLayer1.fillColor = waterColor.cgColor
waterLayer1.strokeColor = waterColor.cgColor
waterLayer1.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
waterLayer2.fillColor = waterColor.cgColor
waterLayer2.strokeColor = waterColor.cgColor
waterLayer2.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
layer.addSublayer(waterLayer1)
layer.addSublayer(waterLayer2)
}
///初始化數(shù)據(jù)
func buildData(){
waterPalstance = CGFloat(Double.pi) / bounds.size.width
waterMoveSpeed = 5 * waterPalstance
//以屏幕刷新速度為周期刷新曲線的位置
disPlayLink = CADisplayLink(target: self, selector: #selector(self.updatewater(link:)))
disPlayLink.add(to: RunLoop.main, forMode: .commonModes)
}
func updatewater(link: CADisplayLink){
//更新x
waterX += waterMoveSpeed
updatewater(shapLayer: waterLayer1, isSin: false)
updatewater(shapLayer: waterLayer2, isSin: true)
}
func updatewater(shapLayer: CAShapeLayer, isSin: Bool){
//波浪寬度
let waterwaterWidth = bounds.size.width
//初始化運(yùn)動(dòng)路徑
let path = CGMutablePath()
//設(shè)置起始位置
path.move(to: CGPoint(x: 0, y: waterY))
//初始化波浪,y為偏距
var tempY = waterY
//正弦曲線公式為: y=Asin(ωx+φ)+k
for x in 0..<Int(waterwaterWidth){
tempY = isSin ? waterAmplitude * sin(waterPalstance * CGFloat(x) + waterX) + waterY : waterAmplitude * cos(waterPalstance * CGFloat(x) + waterX) + waterY
path.addLine(to: CGPoint(x: CGFloat(x), y: tempY))
}
//填充底部顏色
path.addLine(to: CGPoint(x: waterwaterWidth, y: 200))
path.addLine(to: CGPoint(x: 0, y: 200))
path.closeSubpath()
shapLayer.path = path
}
}
使用方法
2.加上旋轉(zhuǎn)的陽(yáng)光
更改上面的代碼缤谎,更改后如下:
import UIKit
class TBMyInfoTopView: UIView {
//提供給外部調(diào)用接口
///曲線振幅
var waterAmplitude: CGFloat = 8
///曲線角速度
var waterPalstance: CGFloat = 0
///曲線初相
var waterX: CGFloat = 0
///曲線偏距
let waterY: CGFloat = 70
///曲線移動(dòng)速度
var waterMoveSpeed: CGFloat = 0
fileprivate let waterColor = UIColor(red: 118/255.0, green: 165/255.0, blue: 242/255.0, alpha: 0.6)
fileprivate let BackGroundColor = UIColor(red: 80/255.0, green: 140/255.0, blue: 238/255.0, alpha: 1)
//前面的波浪
fileprivate let waterLayer1 = CAShapeLayer()
fileprivate let waterLayer2 = CAShapeLayer()
fileprivate var disPlayLink = CADisplayLink()
//陽(yáng)光
var sunshinView = UIImageView()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = BackGroundColor
buildInterface()
buildData()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func buildInterface(){
//初始化波浪
waterLayer1.fillColor = waterColor.cgColor
waterLayer1.strokeColor = waterColor.cgColor
waterLayer1.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
waterLayer2.fillColor = waterColor.cgColor
waterLayer2.strokeColor = waterColor.cgColor
waterLayer2.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
layer.addSublayer(waterLayer1)
layer.addSublayer(waterLayer2)
//陽(yáng)光
sunshinView = UIImageView(frame: CGRect(x: 200, y: -100, width: 200, height: 200))
sunshinView.image = #imageLiteral(resourceName: "gx.png")
addSubview(sunshinView)
// 創(chuàng)建動(dòng)畫
let anim = CABasicAnimation(keyPath: "transform.rotation")
// 設(shè)置動(dòng)畫屬性
anim.toValue = 2 * Double.pi
anim.repeatCount = MAXFLOAT
anim.duration = 20
anim.isRemovedOnCompletion = false
// 將動(dòng)畫添加到圖層上
sunshinView.layer.add(anim, forKey: nil)
}
///初始化數(shù)據(jù)
func buildData(){
waterPalstance = CGFloat(Double.pi) / bounds.size.width
waterMoveSpeed = 5 * waterPalstance
//以屏幕刷新速度為周期刷新曲線的位置
disPlayLink = CADisplayLink(target: self, selector: #selector(self.updatewater(link:)))
disPlayLink.add(to: RunLoop.main, forMode: .commonModes)
}
func updatewater(link: CADisplayLink){
//更新x
waterX += waterMoveSpeed
updatewater(shapLayer: waterLayer1, isSin: false)
updatewater(shapLayer: waterLayer2, isSin: true)
}
func updatewater(shapLayer: CAShapeLayer, isSin: Bool){
//波浪寬度
let waterwaterWidth = bounds.size.width
//初始化運(yùn)動(dòng)路徑
let path = CGMutablePath()
//設(shè)置起始位置
path.move(to: CGPoint(x: 0, y: waterY))
//初始化波浪,y為偏距
var tempY = waterY
//正弦曲線公式為: y=Asin(ωx+φ)+k
for x in 0..<Int(waterwaterWidth){
tempY = isSin ? waterAmplitude * sin(waterPalstance * CGFloat(x) + waterX) + waterY : waterAmplitude * cos(waterPalstance * CGFloat(x) + waterX) + waterY
path.addLine(to: CGPoint(x: CGFloat(x), y: tempY))
}
//填充底部顏色
path.addLine(to: CGPoint(x: waterwaterWidth, y: 200))
path.addLine(to: CGPoint(x: 0, y: 200))
path.closeSubpath()
shapLayer.path = path
}
}
3.用幀動(dòng)畫創(chuàng)建游動(dòng)的魚群FishView.swift
值得注意的是下面這部分代碼當(dāng)時(shí)遇到一點(diǎn)小坑
arc4random() % UInt32(x)
中x必須大于1,而abs(frame.height - 35)
之類的結(jié)果是有可能小于1的褐着,所以需要做相應(yīng)處理坷澡,不能直接arc4random() % UInt32(abs(frame.height - 35))
import UIKit
class FishView: UIView {
var fishMaxNum = 5
fileprivate var imageViewArray = [UIImageView]()
fileprivate var timer: Timer!
override init(frame: CGRect) {
super.init(frame: frame)
for _ in 0..<fishMaxNum{
let tempX = abs(frame.width - 60)
let x = arc4random() % UInt32(tempX >= 1 ? tempX : 1)
let tempY = abs(frame.height - 35)
let y = arc4random() % UInt32(tempY >= 1 ? tempY : 1)
let w = arc4random() % 50 + 10
let h = arc4random() % 30 + 5
let imageView = UIImageView(frame: CGRect(x: CGFloat(x), y: CGFloat(y), width: CGFloat(w), height: CGFloat(h)))
addSubview(imageView)
imageViewArray.append(imageView)
}
self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.timerAction(timer:)), userInfo: nil, repeats: true)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func timerAction(timer:Timer){
let count = imageViewArray.count
//隨機(jī)要?jiǎng)拥聂~數(shù)量
let num = arc4random() % UInt32(count)
for _ in 0..<num{
//隨機(jī)數(shù)組中哪幾個(gè)動(dòng)
let num2 = arc4random() % UInt32(count)
changeFrame(imageView: imageViewArray[Int(num2)])
}
timer.invalidate()
self.timer = Timer.scheduledTimer(timeInterval: TimeInterval(arc4random() % 5 + 1), target: self, selector: #selector(self.timerAction(timer:)), userInfo: nil, repeats: true)
}
func changeFrame(imageView: UIImageView){
let tempX = CGFloat(arc4random() % 100) - 50
let tempY = CGFloat(arc4random() % 100) - 50
let frameOr = imageView.frame.origin
if tempX + frameOr.x > 0 && tempX + frameOr.x < frame.width - imageView.frame.width{
imageView.stopAnimating()
if tempX + frameOr.x > imageView.frame.origin.x{
animation(isLeft: false, imageView: imageView)
}else{
animation(isLeft: true, imageView: imageView)
}
UIView.animate(withDuration: TimeInterval(arc4random() % 5 + 1), animations: {
imageView.frame.origin.x += tempX
})
}
if tempY + frameOr.y > 0 && tempY + frameOr.y < frame.height - imageView.frame.height{
UIView.animate(withDuration: TimeInterval(arc4random() % 5 + 1), animations: {
imageView.frame.origin.y += tempY
})
}
}
func animation(isLeft: Bool, imageView: UIImageView){
imageView.animationImages = []
for item in 0...9{
let imageName = isLeft ? "fishl\(item).png" : "fish\(item).png"
let image = UIImage(named: imageName)
imageView.animationImages?.append(image!)
}
imageView.animationDuration = Double((arc4random() % 20) + 5) / 10.0
imageView.startAnimating()
}
}
使用
組合到上面的代碼中需要將背景色改變成透明
4.氣泡
氣泡搬了個(gè)磚,然后稍作修改含蓉,代碼是OC寫的频敛,如下
TBBubbleView.h
#import <UIKit/UIKit.h>
@interface TBBubbleView : UIView
@property (nonatomic, assign)CGFloat maxLeft;//漂浮左邊最大距離
@property (nonatomic, assign)CGFloat maxRight;//漂浮右邊最大距離
@property (nonatomic, assign)CGFloat maxHeight;//漂浮最高距離
@property (nonatomic, assign)CGFloat duration;//一組圖片播放完的時(shí)間
@property (nonatomic, copy)NSArray *images;//圖片數(shù)組
//init
-(instancetype)initWithFrame:(CGRect)frame
folatMaxLeft:(CGFloat)maxLeft
folatMaxRight:(CGFloat)maxRight
folatMaxHeight:(CGFloat)maxHeight
bubbleNum:(NSInteger)bubbleNum;
//開(kāi)始動(dòng)畫
-(void)startBubble;
@end
TBBubbleView.m
#import "TBBubbleView.h"
@interface TBBubbleView()
@property(nonatomic,strong)NSTimer *timer;
@property(nonatomic,assign)CGFloat maxWidth;
@property(nonatomic,assign)CGPoint startPoint;
@property(nonatomic,strong) NSMutableArray *layerArray;
@property(nonatomic) NSUInteger bubbleNum;
@property(nonatomic) NSInteger tempNum;
@end
@implementation TBBubbleView
//初始化
-(instancetype)initWithFrame:(CGRect)frame folatMaxLeft:(CGFloat)maxLeft folatMaxRight:(CGFloat)maxRight folatMaxHeight:(CGFloat)maxHeight bubbleNum:(NSInteger)bubbleNum
{
self = [super initWithFrame:frame];
if(self)
{
_maxLeft = maxLeft;
_maxRight = maxRight;
_maxHeight = maxHeight;
_layerArray = [NSMutableArray array];
_bubbleNum = bubbleNum;
_tempNum = 0;
}
return self;
}
//外部方法 開(kāi)始?xì)馀?-(void)startBubble
{
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(generateBubble) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop]addTimer:self.timer forMode:UITrackingRunLoopMode];
}
-(void)generateBubble
{
if (_tempNum == _bubbleNum) {
[self.timer invalidate];
return;
}
CALayer *layer =[CALayer layer];;
UIImage *image = self.images[arc4random() % self.images.count];
layer = [self createLayerWithImage:image];
[self.layer addSublayer:layer];
[self generateBubbleByLayer:layer];
_tempNum += 1;
}
//創(chuàng)建帶有Image的Layer
- (CALayer *)createLayerWithImage:(UIImage *)image
{
CGFloat scale = [UIScreen mainScreen].scale;
CALayer *layer = [CALayer layer];
layer.frame = CGRectMake(0, 0, image.size.width / scale, image.size.height / scale);
layer.contents = (__bridge id)image.CGImage;
return layer;
}
-(void)generateBubbleByLayer:(CALayer*)layer
{
_maxWidth = _maxLeft + _maxRight;
_startPoint = CGPointMake(self.frame.size.width/2, 0);
CGPoint endPoint = CGPointMake(_maxWidth * [self randomFloat] - _maxLeft, -_maxHeight);
CGPoint controlPoint1 =
CGPointMake(_maxWidth * [self randomFloat] - _maxLeft, -_maxHeight * 0.2);
CGPoint controlPoint2 =
CGPointMake(_maxWidth * [self randomFloat] - _maxLeft, -_maxHeight * 0.6);
CGMutablePathRef curvedPath = CGPathCreateMutable();
CGPathMoveToPoint(curvedPath, NULL, _startPoint.x, _startPoint.y);
CGPathAddCurveToPoint(curvedPath, NULL, controlPoint1.x, controlPoint1.y, controlPoint2.x, controlPoint2.y, endPoint.x, endPoint.y);
UIBezierPath *path = [UIBezierPath bezierPathWithCGPath:curvedPath];
//[path addCurveToPoint:endPoint controlPoint1:_startPoint controlPoint2:controlPoint1];
CAKeyframeAnimation *keyFrame = [CAKeyframeAnimation animation];
keyFrame.keyPath = @"position";
keyFrame.path = path.CGPath;
keyFrame.duration = self.duration;
keyFrame.calculationMode = kCAAnimationPaced;
[layer addAnimation:keyFrame forKey:@"keyframe"];
CABasicAnimation *scale = [CABasicAnimation animation];
scale.keyPath = @"transform.scale";
scale.toValue = @1;
scale.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.1, 0.1, 0.1)];
scale.duration = 0.5;
CABasicAnimation *alpha = [CABasicAnimation animation];
alpha.keyPath = @"opacity";
alpha.fromValue = @1;
alpha.toValue = @0;
alpha.duration = self.duration * 0.4;
alpha.beginTime = self.duration - alpha.duration;
CAAnimationGroup *group = [CAAnimationGroup animation];
group.animations = @[keyFrame, scale, alpha];
group.duration = self.duration;
group.delegate = self;
group.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
group.fillMode = kCAFillModeForwards;
group.removedOnCompletion = NO;
[layer addAnimation:group forKey:@"group"];
[self.layerArray addObject:layer];
}
-(void)dealloc
{
[self.layerArray removeAllObjects];
[self.timer invalidate];
}
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
if (flag)
{
CALayer *layer = [self.layerArray firstObject];
[layer removeAllAnimations];
[layer removeFromSuperlayer];
[self.layerArray removeObject:layer];
if (self.layerArray.count == 0) {
[self removeFromSuperview];
}
}
}
- (CGFloat)randomFloat{
return (arc4random() % 100)/100.0f;
}
@end
使用
5.組合
組合后TBMyInfoTopView文件如下
import UIKit
class TBMyInfoTopView: UIView {
//提供給外部調(diào)用接口
///曲線振幅
var waterAmplitude: CGFloat = 8
///曲線角速度
var waterPalstance: CGFloat = 0
///曲線初相
var waterX: CGFloat = 0
///曲線偏距,越小距離頂端越近
let waterY: CGFloat = 50
///曲線移動(dòng)速度
var waterMoveSpeed: CGFloat = 0
fileprivate let waterColor = UIColor(red: 118/255.0, green: 165/255.0, blue: 242/255.0, alpha: 0.3)
fileprivate let BackGroundColor = UIColor(red: 80/255.0, green: 140/255.0, blue: 238/255.0, alpha: 1)
//前面的波浪
fileprivate let waterLayer1 = CAShapeLayer()
fileprivate let waterLayer2 = CAShapeLayer()
fileprivate var disPlayLink = CADisplayLink()
//陽(yáng)光
var sunshinView = UIImageView()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = BackGroundColor
buildInterface()
buildData()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func buildInterface(){
//珊瑚
let shImageView = UIImageView(frame: CGRect(x: frame.width - 60, y: frame.height - 60, width: 60, height: 60))
shImageView.image = #imageLiteral(resourceName: "shanhu.png")
addSubview(shImageView)
let scImageView = UIImageView(frame: CGRect(x: 150, y: frame.height - 40, width: 40, height: 40))
scImageView.image = #imageLiteral(resourceName: "shuicao.png")
addSubview(scImageView)
sendSubview(toBack: scImageView)
//初始化波浪
waterLayer1.fillColor = waterColor.cgColor
waterLayer1.strokeColor = waterColor.cgColor
waterLayer1.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
waterLayer2.fillColor = waterColor.cgColor
waterLayer2.strokeColor = waterColor.cgColor
waterLayer2.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
layer.addSublayer(waterLayer1)
layer.addSublayer(waterLayer2)
//陽(yáng)光
sunshinView = UIImageView(frame: CGRect(x: 200, y: -100, width: 200, height: 200))
sunshinView.image = #imageLiteral(resourceName: "gx.png")
addSubview(sunshinView)
// 創(chuàng)建動(dòng)畫
let anim = CABasicAnimation(keyPath: "transform.rotation")
// 設(shè)置動(dòng)畫屬性
anim.toValue = 2 * Double.pi
anim.repeatCount = MAXFLOAT
anim.duration = 20
anim.isRemovedOnCompletion = false
// 將動(dòng)畫添加到圖層上
sunshinView.layer.add(anim, forKey: nil)
//魚群
let fishView = FishView(frame: CGRect(x: 0, y: waterY + 10, width: frame.width, height: frame.height - waterY - 10))
fishView.backgroundColor = UIColor.clear
insertSubview(fishView, belowSubview: shImageView)
Timer.scheduledTimer(timeInterval: TimeInterval(arc4random() % 10 + 5), target: self, selector: #selector(self.showBubble(timer:)), userInfo: nil, repeats: false)
}
///初始化數(shù)據(jù)
func buildData(){
waterPalstance = CGFloat(Double.pi) / bounds.size.width
waterMoveSpeed = 5 * waterPalstance
//以屏幕刷新速度為周期刷新曲線的位置
disPlayLink = CADisplayLink(target: self, selector: #selector(self.updatewater(link:)))
disPlayLink.add(to: RunLoop.main, forMode: .commonModes)
}
func updatewater(link: CADisplayLink){
//更新x
waterX += waterMoveSpeed
updatewater(shapLayer: waterLayer1, isSin: false)
updatewater(shapLayer: waterLayer2, isSin: true)
}
func updatewater(shapLayer: CAShapeLayer, isSin: Bool){
//波浪寬度
let waterwaterWidth = bounds.size.width
//初始化運(yùn)動(dòng)路徑
let path = CGMutablePath()
//設(shè)置起始位置
path.move(to: CGPoint(x: 0, y: waterY))
//初始化波浪,y為偏距
var tempY = waterY
//正弦曲線公式為: y=Asin(ωx+φ)+k
for x in 0..<Int(waterwaterWidth){
tempY = isSin ? waterAmplitude * sin(waterPalstance * CGFloat(x) + waterX) + waterY : waterAmplitude * cos(waterPalstance * CGFloat(x) + waterX) + waterY
path.addLine(to: CGPoint(x: CGFloat(x), y: tempY))
}
//填充底部顏色
path.addLine(to: CGPoint(x: waterwaterWidth, y: 200))
path.addLine(to: CGPoint(x: 0, y: 200))
path.closeSubpath()
shapLayer.path = path
}
func showBubble(timer:Timer){
//氣泡數(shù)量
let num = arc4random() % 5
//x坐標(biāo)
let x = arc4random() % UInt32(frame.width)
if let bubbleView = TBBubbleView(frame: CGRect(x: CGFloat(x), y: self.frame.height - 10, width: 0, height: 0), folatMaxLeft: 30, folatMaxRight: 30, folatMaxHeight: frame.height - waterY - 20, bubbleNum: Int(num)){
insertSubview(bubbleView, at: 3)
if let image = UIImage.init(named: "2"){
bubbleView.images = [image]
}
bubbleView.duration = 4
bubbleView.startBubble()
timer.invalidate()
Timer.scheduledTimer(timeInterval: TimeInterval(arc4random() % 10 + 5), target: self, selector: #selector(self.showBubble(timer:)), userInfo: nil, repeats: true)
}
}
}