动画总结 Core Animation 贝赛尔曲线 显式动画 隐式动画

 

Core Animation

 

  1. 接下来详细介绍下动画的各个属性及作用
  • fromValue: 动画的开始值(Any类型, 根据动画不同可以是CGPoint、NSNumber等)
  • toValue: 动画的结束值, 和fromValue类似
  • beginTime: 动画的开始时间
  • duration : 动画的持续时间
  • repeatCount : 动画的重复次数
  • fillMode: 动画的运行场景
  • isRemovedOnCompletion: 完成后是否删除动画
  • autoreverses: 执行的动画按照原动画返回执行
  • path:关键帧动画中的执行路径
  • values: 关键帧动画中的关键点数组
  • animations: 组动画中的动画数组
  • delegate : 动画代理, 封装了动画的执行和结束方法
  • timingFunction: 控制动画的显示节奏, 系统提供五种值选择,分别是:

1.kCAMediaTimingFunctionDefault( 默认,中间快)

2.kCAMediaTimingFunctionLinear(线性动画)

3.kCAMediaTimingFunctionEaseIn(先慢后快 慢进快出)

4.kCAMediaTimingFunctionEaseOut(先块后慢快进慢出)

5.kCAMediaTimingFunctionEaseInEaseOut(先慢后快再慢)

  • type: 过渡动画的动画类型,系统提供了多种过渡动画, 分别是:

1: fade(淡出 默认)

2: moveIn(覆盖原图)

3: push(推出)

4: fade(淡出 默认)

5: reveal(底部显示出来)

6: cube(立方旋转)

7: suck(吸走)

8: oglFlip(水平翻转 沿y轴)

9: ripple(滴水效果)

10: curl(卷曲翻页 向上翻页)

11: unCurl(卷曲翻页返回 向下翻页)

12: caOpen(相机开启)

13: caClose(相机关闭)

  • subtype : 过渡动画的动画方向, 系统提供了四种,分别是:

1.fromLeft( 从左侧)

2.fromRight(从右侧)

3.fromTop(有上面)

4.fromBottom(从下面

 

1. 基础动画( CABasicAnimation )

基础动画主要提供了对于CALayer对象中的可变属性进行简单动画的操作。比如:位移、旋转、缩放、透明度、背景色等。

基础动画根据 keyPath来区分不同的动画,,

系统提供了多个类型,如: transform.scale(比例转换)、transform.scale.x、transform.scale.y、 transform.rotation(旋转) 、transform.rotation.x(绕x轴旋转)、transform.rotation.y(绕y轴旋转)、transform.rotation.z(绕z轴旋转)、opacity(透明度)、margin、backgroundColor(背景色)、cornerRadius(圆角)、borderWidth(边框宽)、bounds、contents、contentsRect、cornerRadius、frame、hidden、mask、masksToBounds、shadowColor(阴影色)、shadowOffset、shadowOpacity、shadowOpacity, 在使用时候, 需要根据具体的需求选择合适的.

?一个简单的动画效果

ABasicAnimation *moveAnimation = [CABasicAnimation animationWithKeyPath:@"position.y"]; moveAnimation.duration = 0.8;//动画时间 //动画起始值和终止值的设置 moveAnimation.fromValue = @(self.imageView.center.x); moveAnimation.toValue = @(self.imageView.center.x-30); //一个时间函数,表示它以怎么样的时间运行 moveAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; moveAnimation.repeatCount = HUGE_VALF; moveAnimation.repeatDuration = 2; moveAnimation.removedOnCompletion = NO; moveAnimation.fillMode = kCAFillModeForwards; //添加动画,后面有可以拿到这个动画的标识 [self.imageView.layer addAnimation:moveAnimation forKey:@"可以拿到这个动画的Key值"];

在addAnimation:forKey:方法中,也可以给这个动画设置一个键,可以在其他地方将其取出来,进行一些操作,比如删除。这也充分体现了kvc的灵活。

用到CALayer的 removeAnimationForKey:方法。

相关属性

keyPath:要改变的属性名称(传字符串)

fromValue:keyPath相应属性的初始值

toValue:keyPath相应属性的结束值

timingFunction:动画随时间运行的关系

 

2. 关键帧动画( CAKeyframeAnimation )

CAKeyframeAnimation和 CABasicAnimation都属于CAPropertyAnimatin的子类。不同的是 CABasicAnimation只能从一个数值(fromValue)变换成另一个数值(toValue),而 CAKeyframeAnimation则会使用一个数组(values) 保存一组关键帧, 也可以给定一个路径(path)制作动画。

  • values:存放关键帧(keyframe)的数组,动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧 .
  • path:可以设置一个 CGPathRef或 CGMutablePathRef,让层跟着路径移动. path只对 CALayer的 anchorPoint和 position起作用, 如果设置了path,那么values将被忽略.
  • keyTimes:可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0, keyTimes中的每一个时间值都对应 values中的每一帧.当 keyTimes没有设置的时候,各个关键帧的时间是根据 duration平分的。

 

?一个关键帧动画代码

CAKeyframeAnimation *animaiton = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"]; NSArray *rotationVelues = @[@(M_PI_4), @(-M_PI_4), @(M_PI_4)]; animaiton.values = rotationVelues; animation.rotationMode = kCAAnimationRotateAuto; //方向 animaiton.duration = 3.0f; animation.keyTimes = @[@0.2 ,@0.8 ,@1]; animation.path = bezierPath.CGPath; animaiton.repeatCount = HUGE_VALF; // #define HUGE_VALF 1e50f [self.imageView.layer addAnimation:animaiton forKey:nil];

属性说明

values:上述的NSArray对象。里面的元素称为“关键帧”(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧

path:可以设置一个CGPathRef、CGMutablePathRef,让图层按照路径轨迹移动。path只对CALayer的anchorPoint和position起作用。如果设置了path,那么values将被忽略

keyTimes:可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每一个时间值都对应values中的每一帧。如果没有设置keyTimes,各个关键帧的时间是平分的

bezierPath:贝赛尔曲线路径,为动画提供一个动画移动的路线。

UIBezierPath - 贝赛尔曲线

//创建路径 UIBezierPath *bezierPath = [[UIBezierPath alloc] init]; [bezierPath moveToPoint:CGPointMake(0, 450)]; [bezierPath addCurveToPoint:CGPointMake(370, 500) controlPoint1:CGPointMake(350, 200) controlPoint2:CGPointMake(300, 600)]; //一个曲线 //路径样式 CAShapeLayer *shapeLayer = [CAShapeLayer layer]; shapeLayer.path = bezierPath.CGPath; shapeLayer.fillColor = [UIColor clearColor].CGColor; //填充色<默认黑色> shapeLayer.strokeColor = [UIColor blueColor].CGColor; //线色 shapeLayer.lineWidth = 2; [self.view.layer addSublayer:shapeLayer];

UIBezierPath创建一个路径,画出一条曲线。栗子中 起点(0,450)、终点(370,500) 和 (350,200)、(370,500)来个点来确定线的路径。

CAShapeLayer对上面的线进行属性上的设置。最后添加到一个layer上。

++CABasicAnimation可看做是只有2个关键帧的CAKeyframeAnimation++

 

 

3. 组动画( CAAnimationGroup )

CAAnimationGroup 是 CAAnimation 的子类,可以保存一组动画对象,可以保存基础动画、关键帧动画等,数组中所有动画对象可以同时并发运行, 也可以通过实践设置为串行连续动画.

属性说明

animations:用来保存一组动画对象的NSArray

 

CAAnimationGroup *animationGroup = [CAAnimationGroup animation]; animationGroup.animations = @[animation,basicAnimation]; animationGroup.duration = 4; animation.repeatCount = 9; [_imageLayer addAnimation:animationGroup forKey:@"changeColor"];

 

4. 过渡动画( CATransition)

CATransition是 CAAnimation的子类,用于做过渡动画或者 转场动画,能够为层提供移出屏幕和移入屏幕的动画效果。

过渡动画通过 type设置不同的动画效果, CATransition有多种过渡效果, 但其实 Apple官方的SDK只提供了四种:

  • fade 淡出 默认
  • moveIn 覆盖原图
  • push 推出
  • reveal 底部显示出来

但私有API提供了其他很多非常炫的过渡动画,如 cube(立方旋转)、suckEffect(吸走)、oglFlip(水平翻转 沿y轴)、 rippleEffect(滴水效果)、pageCurl(卷曲翻页 向上翻页)、pageUnCurl(卷曲翻页 向下翻页)、cameraIrisHollowOpen(相机开启)、cameraIrisHollowClose(相机关闭)等。

注: 因 Apple不提供维护,并且有可能造成你的app审核不通过, 所以不建议开发者们使用这些私有API.

 

CATransition *caTransition = [CATransition animation]; caTransition.duration = 0.5; caTransition.delegate = self; caTransition.timingFunction = [CAMediaTimingFunction functionWithName:@"easeInEaseOut"];//切换时间函数 caTransition.type = kCATransitionReveal;//动画切换风格 caTransition.subtype = kCATransitionFromLeft;//动画切换方向 // 子视图交换位置 //[self.parentView exchangeSubviewAtIndex:0 withSubviewAtIndex:1]; //动画在父视图 [self.parentView.layer addAnimation:caTransition forKey:@"Key"];

 

动画属性

type:动画过渡类型

subtype:动画过渡方向

startProgress:动画起点(在整体动画的百分比)

endProgress:动画终点(在整体动画的百分比)

/ 导航栏切换 UIViewController *viewCtr = [[UIViewController alloc] init]; viewCtr.view.backgroundColor = [UIColor redColor]; [self.navigationController pushViewController:viewCtr animated:NO];// 动画设置 NO 效果比较好 [self.navigationController.view.layer addAnimation:caTransition forKey:@"animation"];

 

 

CALayer

它有一些方法和属性来做动画和变换,和UIView最大的不同是CALayer不处理用户的交互事件。

UIView和CALayer的关系 - 平行关系

  • 每个UIView都有一个CALayer实例的图层属性。
  • 视图的职责就是创建并管理这个图层。
  • UIView和CALyer有着平行的层级关系,职责分离。

 

解释:为什么动画结束后返回原状态?

首先我们需要搞明白一点的是,layer动画运行的过程是怎样的?其实在我们给一个视图添加layer动画时,真正移动并不是我们的视图本身,而是 presentation layer 的一个缓存。动画开始时 presentation layer开始移动,原始layer隐藏,动画结束时,presentation layer从屏幕上移除,原始layer显示。这就解释了为什么我们的视图在动画结束后又回到了原来的状态,因为它根本就没动过。

这个同样也可以解释为什么在动画移动过程中,我们为何不能对其进行任何操作。

所以在我们完成layer动画之后,最好将我们的layer属性设置为我们最终状态的属性,然后将presentation layer 移除掉。

moveAnimation.removedOnCompletion = NO;

UIView中目前最常用的动画方法应该就是这个方法了

+(void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^ nullable)(BOOL finished))completion ;

稍微复杂点的方法还是使用CALayer调用CAAnimation的API更为方便。

 

CAShapeLayer

CAShapeLayer是一个通过矢量图形而不是bitmap(位图)来挥之的图层子类。

你指定诸如颜色和线宽等属性,用CAPath来定义想绘制的图形,最后CAShapeLayer就自动渲染出来了。

优点

1.渲染快速。CAShapeLayer使用了硬件加速,绘制同一个图形会比用Core Graphics快很多。

2.高效使用内存。一个CAShapeLayer不需要像CALayer一样创建一个寄宿图,所以无论有多大,都不会占用大多的内存。

3.不会被图层边界裁剪掉。

4.不会出现像素化。当你给CAShapeLayer做3D变换时,它不像一个有寄宿图普通图层一样变得像素化。

创建一个CGPath

CAShapeLayer可以用来绘制所有能够通过CGPath来表示的形状。这个形状不一定要闭合,图层路径也不一定要不可破的,事实上你可以在一个图层上绘制好几个不同的形状。

你可以控制一些属性比如

lineWith(线宽,用点表示单位)、lineCap(线条结尾的样子)、和lineJoin(线条之间的结合点的样子)。

CAShapeLayer属性时CGPathRef类型,当时我们用 UIBezierPath 帮助类创建了图层路径,这样我们就不用考虑释放CGPath了。

 

贝赛尔曲线+CAShapeLayer

 

//创建圆角

UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, self.imageView.frame.size.width, self.imageView.frame.size.height) cornerRadius:10];

CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];

maskLayer.path = maskPath.CGPath;

self.imageView.layer.mask=maskLayer;

 

/画图

- (void)viewDidLoad { [super viewDidLoad]; //create path UIBezierPath *path = [[UIBezierPath alloc] init]; [path moveToPoint:CGPointMake(175, 100)]; [path addArcWithCenter:CGPointMake(150, 100) radius:25 startAngle:0 endAngle: 2*M_PI clockwise:YES]; [path moveToPoint:CGPointMake(150, 125)]; [path addLineToPoint:CGPointMake(150, 175)]; [path addLineToPoint:CGPointMake(125, 225)]; [path moveToPoint:CGPointMake(150, 175)]; [path addLineToPoint:CGPointMake(175, 225)]; [path moveToPoint:CGPointMake(100, 150)]; [path addLineToPoint:CGPointMake(200, 150)]; //create shape layer CAShapeLayer *shapeLayer = [CAShapeLayer layer]; shapeLayer.strokeColor = [UIColor redColor].CGColor; shapeLayer.fillColor = [UIColor clearColor].CGColor; shapeLayer.lineWidth = 5; shapeLayer.lineJoin = kCALineJoinRound; shapeLayer.lineCap = kCALineCapRound; shapeLayer.path = path.CGPath; //add it to our view [self.containerView.layer addSublayer:shapeLayer]; }

 

隐式动画

Core Animation基于一个假设,说屏幕上的任何东西都是可以(或可能) 做动画。

动画并不需要你在Core Animation中手动打开,相反需要明确的关闭,否者它一只存在。

当你改变CALayer的一个可以做动画的属性,它并不能在屏幕上体现出来。相反,它是从先前的值平滑过渡到新的值。这一切都是默认的行为,你不需要做额外的操作。

? 一个View上放一个CALayer,点击按钮改变layer的颜色<颜色是渐变过去的>

这就是隐式动画,之所以叫隐式动画是因为我们并没有指定任何的动画类型。我们仅仅改变了一个属性,然后Core Animation来决定如何并且何时去动画。

CATransaction 管理事务的类

+begin 入栈

+commit 出栈

+setAnimationDuration: 设置当前动画的时间

+AnimationDuration 获取动画时间

Core Animation在每个RUNLOOP周期中自动开始一次新的事物。

RUNROOP:iOS中负责收集用户输入,处理定时器或者网络事件并且重新绘制屏幕的东西。理解:涉及到任何主动或被动的操作改变都会唤醒run loop。

即使不显式的用[CATransaction begin]开始一次事务,任何在一次run loop循环中属性的改变都会被集中起来,然后做一次0.25秒的动画。

UIView中的layer属性 并不存在 隐形动画

总结

1.UIView关联的图层禁用了隐形动画,对这种图层做动画的唯一方法就是使用IUView的动画函数(而不是依赖CATransaction),或者继承UIView,并覆盖 -actionForLayer:forKey: 方法,或者直接创建一个显性动画。

2.对于独立的图层,我们可以通过实现图层的 -actionForLayer:forKey:委托方法,或者提供一个actions字典来控制隐形动画

 

显式动画

animationDidStop: finished:

代码demo

属性动画

基础动画

CABasicAnimation *animation = [CABasicAnimation animation]; animation.keyPath = @"backgroundColor";animation.toValue = (__bridge id)color.CGColor;animation.delegate = self;//apply animation to layer[self.colorLayer addAnimation:animation forKey:nil];

关键帧路径动画

CAKeyframeAnimation *animation = [CAKeyframeAnimation animation]; animation.keyPath = @"position"; animation.duration = 4.0; animation.path = bezierPath.CGPath; animation.rotationMode = kCAAnimationRotateAuto; [shipLayer addAnimation:animation forKey:nil];

动画组

CABasicAnimation和CAKeyframeAnimation仅仅是作用于单独的属性。

CAAnimationGroup可以把这些动画组合在一起 <CAAnimation 子类>

过渡

属性动画只对图层的可动画属性属性起作用

所以如果要改变一个不能动画的属性(比如图片),或者从层级关系中添加或者移除图层,属性动画将不起作用。

过渡:过渡并不像属性动画那样平滑的在两个值之间做动画,而是影响到整个图层的变化

。过渡动画首先展示之前的图层外观,然后通过一个交换过渡到新的外观。

CATransition:CAAnimation子类

CATransition有个type和subtype来识别变换效果,类型 NSString

type kCATransitionFade //默认 淡入淡出效果 kCATransitionMoveIn //新图片滑动进入,直接覆盖旧的图片 kCATransitionPush //边缘的一侧进来,把旧的图层从另一侧推出去 kCATransitionReveal //旧图片滑出去,来显示新图片 subtype kCATransitionFromRight kCATransitionFromLeft kCATransitionFromTop kCATransitionFromBottom

在想:控制器之间的过场动画是不是能用这个实现。

 

仿射变换

UIView可以通过transform属性做变换,transform是一个CGAffineTransform类型

CGAffineTransform 是用矩阵相乘的方法实现仿射变换

CGAffineTransformMakeRotation(CGFloat angle) 旋转

CGAffineTransformMakeScale(CGFloat sx, CGFloat sy) 缩放

CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty) 位移

CALayer 同样又个transform属性,transform的属性是CATransform3D

CALayer对应UIView的transform属性叫做affineTransform

混合变换

当操纵一个变换的时候,初始生成一个什么都不做的变换很重要-也就是创建一个CGAffineTransform类型的空值。<CGAffineTransformIdentity>

CGAffineTransformRotate(CGAffineTransform t, CGFloat angle) CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy) CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty)

最后,如果需要混合两个已经存在的矩阵,就可以使用下面方法,在两个基础上新建一个变换

CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2);

 

先缩小50%-再旋转30度-最后向右移动200像素

- (void)viewDidLoad { [super viewDidLoad]; //create a new transform CGAffineTransform transform = CGAffineTransformIdentity; //scale by 50%transform = CGAffineTransformScale(transform, 0.5, 0.5); //rotate by 30 degreestransform = CGAffineTransformRotate(transform, M_PI / 180.0 * 30.0); //translate by 200 points transform = CGAffineTransformTranslate(transform, 200, 0);//apply transform to layer self.layerView.layer.affineTransform = transform; }

结果并不是预期的结果:这意味着变换的顺序会影响最终的结果,也就是说旋转之后的平移和平移之后的的旋转结果可能不同

CG - Core Graphics 框架 :严格意义上说是2D绘图API

CGAffineTransform 仅仅对2D变换有效

3D变换

CALayer的transform属性是CATransform3D类型,就能实现3D空间内移动或者旋转

CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z)

CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz) CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz)

想象x、y、z向旋转,缩放、位移的效果

 

绕Y轴做45度角的旋转

- (void)viewDidLoad { [super viewDidLoad]; //rotate the layer 45 degrees along the Y axis CATransform3D transform = CATransform3DMakeRotation(M_PI_4, 0, 1, 0); self.layerView.layer.transform = transform; }

sublayerTransform:CALayer的属性,可以对所有子视图操作

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现加入购物车飞入动画,可以使用塞尔曲线来控制动画路径。具体实现步骤如下: 1. 在购买按钮点击事件中,获取购买按钮的位置和购物车图标的位置,并计算出两者之间的距离。 2. 创建一个塞尔曲线,控制点设置为购买按钮位置和购物车图标位置的中心点,终点设置为购物车图标位置。 3. 使用CSS3动画或JavaScript来实现动画效果,让购买按钮沿着塞尔曲线飞入购物车。 下面是一个简单的实现示例: ```html <template> <div> <button @click="addToCart">加入购物车</button> <div class="cart-icon"></div> <div class="ball"></div> </div> </template> <script> export default { methods: { addToCart() { const button = document.querySelector('button') const cartIcon = document.querySelector('.cart-icon') const ball = document.querySelector('.ball') const buttonRect = button.getBoundingClientRect() const cartIconRect = cartIcon.getBoundingClientRect() const deltaX = cartIconRect.left - buttonRect.left const deltaY = cartIconRect.top - buttonRect.top const controlPointX = (buttonRect.left + cartIconRect.left) / 2 const controlPointY = buttonRect.top - 100 ball.style.left = buttonRect.left + 'px' ball.style.top = buttonRect.top + 'px' const bezierPath = `M${buttonRect.left},${buttonRect.top} Q${controlPointX},${controlPointY} ${cartIconRect.left},${cartIconRect.top}` ball.animate( [ { transform: `translate(0,0)` }, { transform: `translate(${deltaX}px, ${deltaY}px)` } ], { duration: 500, easing: 'ease-out', fill: 'forwards' } ) ball.animate( [ { transform: `translate(${deltaX}px, ${deltaY}px)` }, { transform: `translate(0,0)` } ], { duration: 500, easing: 'ease-in', fill: 'forwards', delay: 500 } ) ball.animate( [ { opacity: 1 }, { opacity: 0 } ], { duration: 500, fill: 'forwards', delay: 1000 } ) const path = document.createElementNS('http://www.w3.org/2000/svg', 'path') path.setAttribute('d', bezierPath) path.setAttribute('fill', 'none') path.setAttribute('stroke', 'red') path.setAttribute('stroke-width', '2') document.querySelector('.container').appendChild(path) } } } </script> <style> .cart-icon { position: absolute; top: 20px; right: 20px; width: 50px; height: 50px; background: url('cart.png'); background-size: cover; } .ball { position: absolute; width: 20px; height: 20px; border-radius: 50%; background: red; } .container { position: relative; } </style> ``` 这里使用了CSS3动画和JavaScript动画结合的方式来实现购买按钮飞入购物车的效果。在addToCart方法中,首先获取购买按钮和购物车图标的位置,并计算出两者之间的距离。然后根据塞尔曲线的控制点计算出塞尔曲线路径,并创建一个SVG path元素来显示路径。最后使用CSS3动画和JavaScript动画来实现购买按钮沿着塞尔曲线飞入购物车的效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值