贝塞尔曲线,CANVAS模仿沙尘暴特效

高等高校时候,有壹段时间对flash相比感兴趣。去教室借了一本很厚很厚的falsh书籍。

在贝塞尔曲线(壹)中,大家介绍了贝塞尔曲线的绘图,可是那是定位的,运用处境很少,运用更加多的是有的动画效果。
而前者首要的成效正是承担貌美如花,所以明白一定的动画片技术依旧有至关重要的。
故此啊,作者就起来从简单的慢慢商讨吗。

 

下推动画

首先是之类的效益:

葡京娱乐注册 1

下拉动画.gif

先是需求分析几个难点:

  1. 怎么绘制二阶曲线
  2. 葡京娱乐注册,什么让曲线跟随着下拉拓展转变

难题的消除有广大种,笔者写下的只是作者的化解思路:

1. 创建一个视图,重写它的drawRect:方法

UIBezierPath *bezier = [UIBezierPath bezierPath];
UIColor *color = hexStrColor(@"#FF8C69");
[color set];
bezier.lineWidth = 1.0;
[bezier moveToPoint:(CGPoint){0,0}];
[bezier addQuadCurveToPoint:(CGPoint){self.width,0} controlPoint:(CGPoint){self.width/2,self.offsetY}];
[bezier fill];

经过下面的方法,大家绘制了二阶曲线,那么便是缓解第1个难题了。

想让我们的报表下拉的时候,视图也随即变动,那么一定需求掌握我们表格下拉的切切实实数值,所以要求贯彻UIScrollViewDelegate的scrollViewDidScroll方法

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{}

通过该办法我们实时的敞亮表格的Y轴偏移量,所以我们还索要在前头的视图中添加3个变量:
@property (nonatomic,readwrite,unsafe_unretained)CGFloat offsetY;
经过该变量来实时的支配二阶曲线的controlPoint参数的Y值。

接下去,我们在scrollViewDidScroll方法中写下如下代码。

self.headerView.offsetY = -scrollView.contentOffset.y;
[self.headerView setNeedsDisplay];
[self.headerView setFrame:(CGRect){0,statusBarHeight,kSCREENWIDTH,- scrollView.contentOffset.y}];

因为大家供给在滑行的时候实时的更动曲线的拐点,然后生成对应的曲线。也正是大家要求实时的再次绘制视图,也正是调用drawRect:
方法.所以大家供给调用setNeedsDisplay方法。通过在外表调用setNeedsDisplay可以是视图重新调用drawRect方法

setNeedsLayout会暗许调用layoutSubViews,
setNeedsDisplay会调用drawRect:方法

翻了几页之后,就再未有今后看过。影象相比深的是小编说她用flash达成了三个飓风效果。

加载动画

接下去大家再做个加载动画,具体的成效如下:

葡京娱乐注册 2

加载动画.gif

那是个基于路径变化的动画片,也是自个儿在无数软件上时常来看的1种加载动画。所以就融洽尝试能还是不可能做不出来。
先创立多少个球,哈哈,先把大家要玩的球画出来:

CGFloat radius = 20;
CGFloat marginLeft = 10;
//第一颗球
CAShapeLayer *fLayer = [CAShapeLayer new];
fLayer.backgroundColor =  getColor(102, 170, 238, 1).CGColor;
[fLayer setFrame:(CGRect){self.width/2-(radius*2+marginLeft*2)/2,self.height/2,radius,radius}];
fLayer.cornerRadius = radius/2;
//第二颗球
CAShapeLayer *sLayer = [CAShapeLayer new];
sLayer.backgroundColor = getColor(102, 170, 238, 0.5).CGColor;
[sLayer setFrame:(CGRect){marginLeft(fLayer)+marginLeft,self.height/2,radius,radius}];
sLayer.cornerRadius = radius/2;
//第三颗球
CAShapeLayer *tLayer = [CAShapeLayer new];
tLayer.backgroundColor = getColor(102, 170, 238, 0.2).CGColor;
[tLayer setFrame:(CGRect){marginLeft(sLayer)+marginLeft,self.height/2,radius,radius}];
tLayer.cornerRadius = radius/2;


self.layer_list = [NSArray arrayWithObjects:fLayer,sLayer,tLayer,nil];
[self.layer addSublayer:fLayer];
[self.layer addSublayer:sLayer];
[self.layer addSublayer:tLayer];

球画好了我们将要起来切磋它的移动轨迹了。通过有个别观测大家分析得出了上边包车型地铁活动轨迹:

葡京娱乐注册 3

率先课球的移位动画.gif

葡京娱乐注册 4

第一颗球运动动画.gif

葡京娱乐注册 5

其叁颗球运动动画.gif

通过观看,第3颗球的运动轨迹其实正是3个上半圆加上三个下半圆的路线动画。那么只须要画七个半圆就解决的事咯?大家来试一下

绘制圆的参考图:

葡京娱乐注册 6

弧线参考图.png

首先个半圆是以五个圆的大旨点为圆心绘制的弧形,
第一个半圆是 π -> 0° 顺时针运动,然后π -> 0°逆时针运动

葡京娱乐注册 7

第二颗球的活动轨迹.png

CAShapeLayer *fLayer = self.layer_list[0];
CAShapeLayer *testLayer = [CAShapeLayer new];
testLayer.fillColor = [UIColor clearColor].CGColor;
testLayer.borderWidth = 1.0f;
testLayer.strokeColor = hexStrColor(@"#AA8C69").CGColor;

UIBezierPath *semicirclePath = [UIBezierPath bezierPath];
//上半圆
[semicirclePath addArcWithCenter:(CGPoint){fLayer.frame.origin.x+25,fLayer.frame.origin.y+10}
                           radius:15
                       startAngle:M_PI
                         endAngle:0
                        clockwise:YES];
//下半圆
[semicirclePath addArcWithCenter:(CGPoint){sLayer.frame.origin.x+25,sLayer.frame.origin.y+10}
                           radius:15
                       startAngle:M_PI
                         endAngle:0
                        clockwise:NO];
testLayer.path = semicirclePathA.CGPath;
[self.layer addSublayer:testLayer];

率先颗球的轨迹大家知晓今后大家来安装第贰颗球的轨道:
0° -> π 顺时针运动

葡京娱乐注册 8

第二颗球的活动轨迹.png

    CAShapeLayer *fLayer = self.layer_list[0];
    CAShapeLayer *sLayer = self.layer_list[1];
    //第二段动画
    CAShapeLayer *testLeftLayer = [CAShapeLayer new];
    testLeftLayer.fillColor = [UIColor clearColor].CGColor;
    testLeftLayer.borderWidth = 1.f;
    testLeftLayer.strokeColor = [UIColor redColor].CGColor;
    UIBezierPath *leftPath = [UIBezierPath bezierPath];
    [leftPath addArcWithCenter:(CGPoint){fLayer.frame.origin.x+25,fLayer.frame.origin.y+10} 
                        radius:15 startAngle:0 endAngle:M_PI clockwise:YES];
    testLeftLayer.path = leftPath.CGPath;
    [self.layer addSublayer:testLeftLayer];

笔者们再来看看第2颗球:
0° -> π 逆时针运动

葡京娱乐注册 9

其三颗球的运动轨迹.png

    CAShapeLayer *sLayer = self.layer_list[1];
    CAShapeLayer *tLayer = self.layer_list[2];
    //第三段动画
    CAShapeLayer *testRightLayer = [CAShapeLayer new];
    testRightLayer.fillColor = [UIColor clearColor].CGColor;
    testRightLayer.borderWidth = duration;
    testRightLayer.strokeColor = [UIColor blueColor].CGColor;

    UIBezierPath *rightPath = [UIBezierPath bezierPath];
    [rightPath addArcWithCenter:(CGPoint){sLayer.frame.origin.x+25,tLayer.frame.origin.y+10} 
                         radius:15 startAngle:0 endAngle:M_PI clockwise:NO];
    testRightLayer.path = rightPath.CGPath;
    [self.layer addSublayer:testRightLayer];

谈起底的静态效果图如下:

葡京娱乐注册 10

静态效果图.png

设定好轨迹之后我们就来设置它们的卡通片效果,动画效果就要动用我们上壹篇中涉及的根本帧动画了.
由于多个卡通具有相同的质量设置,所以大家最棒是包装一下,提取出它们会有变动的数值。
譬如说,动画的借助视图会变,它们的移位path会变,所以提取出那八个参数。

- (void)keyFrameAnimationWithLayer:(CALayer *)layer path:(UIBezierPath *)path;

    CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    keyAnimation.path = path.CGPath;
    keyAnimation.fillMode = kCAFillModeForwards;
    keyAnimation.calculationMode = kCAAnimationPaced;
    keyAnimation.removedOnCompletion = NO;
    keyAnimation.duration = duration;
    keyAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    keyAnimation.rotationMode = kCAAnimationRotateAuto;
    keyAnimation.repeatCount = MAXFLOAT;
    keyAnimation.calculationMode = kCAAnimationCubic;
    [layer addAnimation:keyAnimation forKey:@"keyFrameAnimation"];

现实的参数音信已经在上一篇中提过了,这里就隐瞒了。
然后大家开首调用:

//调用第一颗球
[self keyFrameAnimationWithLayer:fLayer path:semicirclePath];

葡京娱乐注册 11

率先颗球员卡顿的动画.gif

笔者们运转之后会发觉在三个圆的交界处,卡顿了一下下(关于那个标题,笔者尝试调整了动画片的timingFunction属性还有calculationMode都心有余而力不足很好的消除这些题材,作者可疑是由于三个途径是分手绘制的原因,具体的缘故还在商量个中。)
然则作者大概找到了消除办法:
设置三个途径,然后将路径进行合并成二个路径:

    UIBezierPath *semicirclePathA = [UIBezierPath bezierPath];
    [semicirclePathA addArcWithCenter:(CGPoint){fLayer.frame.origin.x+25,fLayer.frame.origin.y+10} radius:15 startAngle:M_PI endAngle:0 clockwise:YES];
    UIBezierPath *semicirclePathB = [UIBezierPath bezierPath];
    [semicirclePathB addArcWithCenter:(CGPoint){sLayer.frame.origin.x+25,sLayer.frame.origin.y+10} radius:15 startAngle:M_PI endAngle:0 clockwise:NO];
    [semicirclePathA appendPath:semicirclePathB];
    testLayer.path = semicirclePathA.CGPath;
    [self.layer addSublayer:testLayer];

运行之后,能够见见功能,如巧克力般丝滑的觉得:

葡京娱乐注册 12

首先课球的移位动画.gif

画好第陆个球之后,大家再来画任何的多个球,基本就一样了,只要调用

- (void)keyFrameAnimationWithLayer:(CALayer *)layer path:(UIBezierPath *)path;

方法就能够了。

成效如下:

葡京娱乐注册 13

3颗球运动动画.gif

唯独落实的作用并不及意,它们的颜料变化并不是潜移默化的,看起来并倒霉,所以大家还亟需丰富颜色变换的卡通
观测原来的效果计算如下:

  1. 第二颗球从初叶到转到最终的时候,颜色从深色变为了浅色。所以大家能够设定它的变化值为
    一 -> 0.2.
  2. 其次颗球原本是浅色然后旋转到第一的职位时是深色。所以大家得以设定它的变化值为
    0.5 -> 1.
  3. 其3颗球原本是最浅的下一场旋转到第3的岗位时,颜色变深了少数。所以大家设定它的变化值
    0.二 -> 0.5.

卡通因为只是四个值得变化 , FromValue -> toValue ,
大家选择CABasicAnimation就足以轻松完毕效果.
小编们1致来封装该动画:

- (void)colorGradientAnimationWithLayer:(CALayer *)layer fromValue:(id)fromValue toValue:(id)toValue{
    CABasicAnimation *alphaAnimation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"];
    alphaAnimation.fromValue = fromValue;
    alphaAnimation.toValue = toValue;
    alphaAnimation.duration = duration;
    alphaAnimation.repeatCount = MAXFLOAT;
    [layer addAnimation:alphaAnimation forKey:@"anmiationAlpha"];
}

调用:

//第一颗球的变化
[self colorGradientAnimationWithLayer:fLayer
                                    fromValue:(__bridge id _Nullable)(getColor(102, 170, 238, 1)).CGColor
                                      toValue:(__bridge id _Nullable)(getColor(102, 170, 238, 0.2)).CGColor];
//第二颗球的变化
[self colorGradientAnimationWithLayer:sLayer
                                    fromValue:(__bridge id _Nullable)(getColor(102, 170, 238, 0.5)).CGColor
                                      toValue:(__bridge id _Nullable)(getColor(102, 170, 238, 0.1)).CGColor];
//第三颗球的变化
[self colorGradientAnimationWithLayer:tLayer
                                fromValue:(__bridge id _Nullable)(getColor(102, 170, 238, 0.2)).CGColor
                                  toValue:(__bridge id _Nullable)(getColor(102, 170, 238, 0.5)).CGColor];

平素到方今笔者也绝非见到那2个效果。

在炮制进度中出现的情况:

1.动画片闪烁
keyAnimation.fillMode = kCAFillModeForwards;
能够添加该属性,该属性表示保留动画结束时的意义。
但增进之后察觉依然不行,最终发现实际上动画结束的坐标点卓殊。

__bridge 首如果因为大家在编写程序的时候还会用到 CoreFoundation(CF)
框架的指标,CF和 OC 对象时期的类型转换就需求选用__bridge

提起底正是大家须求的效果了。

葡京娱乐注册 14

加载动画.gif

DEMO:
https://github.com/yanggenwei/GWAnimation/tree/master

本身也曾经想过完成一下。可是高校时候的技术水平,也帮助不起那几个想法。慢慢就淡忘了。

有时候间自个儿来看了离心运动。突然就想到1个写法。

以身作则地址如下:

http://suohb.com/work/wind2.htm

点击查阅效果

末段效果图如下:

葡京娱乐注册 15

 

龙卷风,大家能够作为3个更上一层楼旋转的气流。

风本身是不可知的,我们就用某些质点的位移轨迹画出来,表示风。

从上向下俯视,就是这么,叁个质点做离心运动的门径。

葡京娱乐注册 16

从侧面看,是这么在,3个质点绕Y轴左右摇摆,摆动越来越大的的运动的路子。

葡京娱乐注册 17

我们就用这么些来做为侧视图的效果,来做一个二D的沙暴。

 

那便是说早先代码设计:

咱俩定义3个点,那几个点Y轴上恒定速率运动,X轴上,向那中线方向有二个向心力G。一旦点运动超越那条中线,向心力倒转为-G。

如此就会画出地点侧视图的职能。

 

葡京娱乐注册 18

 

接下来每一种周期都新增部分如此的点,画出轨道。如图;

葡京娱乐注册 19

大概那样就已经到位了,大家不须要画出总体的门道,只要画出新型的一段就足以。

当到达一定高度之后,就将那条线稳步移除出去。就收获终极效果

葡京娱乐注册 20

 

代码如下:

 1 <!doctype html>
 2 <html>
 3 <head>
 4 <meta http-equiv="Pragma" content="no-cache" />
 5 <meta http-equiv="Cache-Control" content="no-cache" />
 6 <meta http-equiv="Expires" content="0" />
 7 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
 8 <meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no" />
 9 <style type="text/css">
10 html{
11     height: 100%;
12 }
13 html,body,ul,li,canvas{
14     margin: 0;
15     padding: 0;
16 }
17 </style>
18 </head>
19 <body bgcolor="#000000">
20 <canvas id="knife"></canvas>
21 </body>
22 <script>
23 var canvas = document.getElementById("knife");
24 canvas.style.position = "absolute" ;
25 canvas.style.top = 0 ;
26 var w = window.innerWidth ;
27 var h = window.innerHeight ;
28 canvas.width = w ;
29 canvas.height = h ;
30 var cxt = canvas.getContext("2d");
31 cxt.strokeStyle = "#FFF" ;
32 var list = [];
33 var G = 0.4 ;//向心加速度
34 var SPEED_Y = -1 ;//向上速度
35 var centerLine = w/2 ;//龙卷风中线
36 function addLine(){
37     var LEN = 2 ;
38     for(var i = 0 ;i < LEN ; i ++){
39         list.push({
40             x:w/2,
41             y:h/1.3,
42             g:G,
43             c:centerLine+2*Math.random(),
44             sx:(Math.random()-0.5)*4,
45             sy:SPEED_Y+0.5*(Math.random()-0.5),
46             len:Math.round(Math.random()*10+5),
47             list:[{x:w/2,y:h/1.3}]
48         });
49     }
50 }
51 function step(){
52     cxt.clearRect(0,0,w,h);
53     addLine();
54     var obj ;
55     for(var i = 0 ; i < list.length; i ++){
56         obj = list[i] ;
57         if(obj.y < h/2.5){//如果超过这个高度,就删除一个点
58             obj.len -- ;
59             if(obj.len == 0){
60                 list.splice(i,1);
61                 i -- ;
62                 continue ;
63             }
64         }
65         obj.x += obj.sx ;
66         obj.y += obj.sy ;
67         obj.sx += obj.g ;
68         obj.g = obj.x > obj.c ? -G : G ;
69         obj.list.unshift({x:obj.x,y:obj.y});//记录下质点运动轨迹
70         obj.list = obj.list.slice(0,obj.len);//仅仅画出其中一段线就好
71         //画出所有点的连线
72         cxt.beginPath();
73         cxt.moveTo(obj.list[0].x,obj.list[0].y);
74         for(var j = 1 ; j < obj.list.length; j ++){
75             cxt.lineTo(obj.list[j].x,obj.list[j].y);
76 
77         }
78         cxt.stroke();
79     }
80     requestAnimationFrame(step);
81 }
82 requestAnimationFrame(step);
83 </script>
84 </html>

 

越多特效请关切群众号:

葡京娱乐注册 21