贝塞尔曲线&CAShapeLayer超炫动画
2016-05-05 10:39
441 查看
//
// LYJBezierPathCuteView.m
// UIBezierPathAndCAShapeLayer超炫动画
//
// Created by ~~~ on 16/5/4.
// Copyright © 2016年 ~~~. All rights reserved.
//
#import "LYJBezierPathCuteView.h"
#define SCREEN_WIDTH ([[UIScreen mainScreen] bounds].size.width)
#define SCREEN_HEIGHT ([[UIScreen mainScreen] bounds].size.height)
#define MIN_HEIGHT 100
// 图形最小高度
@interface
LYJBezierPathCuteView()
@property (nonatomic,assign)
CGFloat mHeight;
@property (nonatomic,assign)
CGFloat curveX;
// r5点x坐标
@property (nonatomic,assign)
CGFloat curveY;
// r5点y坐标
@property (nonatomic,strong)
UIView *curveView;
// r5红点
@property (nonatomic,strong)
CAShapeLayer *shapeLayer;
@property (nonatomic,strong)
CADisplayLink *displayLink;
@property (nonatomic,assign)
BOOL isAnimating;
@end
@implementation LYJBezierPathCuteView
static NSString *kX =
@"curveX";
static NSString *kY =
@"curveY";
-(instancetype)initWithFrame:(CGRect)frame
{
self = [super
initWithFrame:frame];
if (self) {
[self
addObserver:self
forKeyPath:kX
options:NSKeyValueObservingOptionNew
context:nil];
[self
addObserver:self
forKeyPath:kY
options:NSKeyValueObservingOptionNew
context:nil];
[self
configShapeLayer];
[self
configCurveView];
[self
configAction];
}
return
self;
}
- (void)dealloc {
[self
removeObserver:self
forKeyPath:kX];
[self
removeObserver:self
forKeyPath:kY];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString
*,id> *)change context:(void *)context {
if ([keyPath
isEqualToString:kX] || [keyPath
isEqualToString:kY]) {
[self
updateShapeLayerPath];
}
}
#pragma mark 初始化
// 初始化CAShapeLayer
- (void)configShapeLayer
{
_shapeLayer = [CAShapeLayer
layer];
_shapeLayer.fillColor = [UIColor
colorWithRed:57/255.0
green:67/255.0
blue:89/255.0
alpha:1.0].CGColor;
[self.layer
addSublayer:_shapeLayer];
}
// 初始化r5点
- (void)configCurveView
{
// _curveView就是r5点
self.curveX =
SCREEN_HEIGHT / 2.0;
// r5点x坐标
self.curveY =
MIN_HEIGHT;
// r5点y坐标
_curveView = [[UIView
alloc] initWithFrame:CGRectMake(_curveX,
_curveY,
3, 3)];
_curveView.backgroundColor = [UIColor
redColor];
[self
addSubview:_curveView];
}
// 添加移动手势&CADisplayLink
- (void)configAction
{
_mHeight =
100;
// 手势移动时相对高度
_isAnimating =
NO;
// 是否处于动效状态
// 手势
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer
alloc]
initWithTarget:self
action:@selector(handlePanAction:)];
self.userInteractionEnabled =
YES;
[self
addGestureRecognizer:pan];
// CADisplayLink默认每秒运行60次calculatePath是算出在运行期间_curveView的坐标,从而确定_shapeLayer的形状
_displayLink = [CADisplayLink
displayLinkWithTarget:self
selector:@selector(calculatePath)];
[_displayLink
addToRunLoop:[NSRunLoop
currentRunLoop] forMode:NSDefaultRunLoopMode];
// 在手势结束的时候才调用calculatePath方法,所以一开始是暂停的
_displayLink.paused =
YES;
}
/*
手势移动时,r5红点跟着手势移动,_shapeLayer则根据r5的坐标来扩大自己的区域
手势结束时,r5红点通过UIView的动画方法来改变r5的坐标,同时_shapeLayer根据r5的坐标缩小自己的区域并最终返回原形
*/
- (void)handlePanAction:(UIPanGestureRecognizer *)pan
{
if(!_isAnimating)
{
if(pan.state ==
UIGestureRecognizerStateChanged)
{
// 手势移动时,_shapeLayer跟着手势向下扩大区域
CGPoint point = [pan
translationInView:self];
// 这部分代码使r5红点跟着手势走
_mHeight = point.y*0.7 +
MIN_HEIGHT;
self.curveX =
SCREEN_WIDTH/2.0 + point.x;
self.curveY =
_mHeight > MIN_HEIGHT ?
_mHeight : MIN_HEIGHT;
_curveView.frame =
CGRectMake(_curveX,
_curveY,
_curveView.frame.size.width,
_curveView.frame.size.height);
}
else
if (pan.state ==
UIGestureRecognizerStateCancelled ||
pan.state ==
UIGestureRecognizerStateEnded ||
pan.state ==
UIGestureRecognizerStateFailed)
{
// 手势结束时,_shapeLayer返回原状并产生弹簧动效
_isAnimating =
YES;
_displayLink.paused =
NO;
//开启displaylink,会执行方法calculatePath.
// 弹簧动效
[UIView
animateWithDuration:1.0
delay:0.0
usingSpringWithDamping:0.5
initialSpringVelocity:0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
//
曲线点(r5点)是一个view.所以在block中有弹簧效果.然后根据他的动效路径,在calculatePath中计算弹性图形的形状
_curveView.frame =
CGRectMake(SCREEN_WIDTH/2.0,
MIN_HEIGHT, 3,
3);
} completion:^(BOOL finished) {
if(finished)
{
_displayLink.paused =
YES;
_isAnimating =
NO;
}
}];
}
}
}
// 根据r5的位置,更新_shapeLayer形状
- (void)updateShapeLayerPath
{
// 更新_shapeLayer形状
UIBezierPath *tPath = [UIBezierPath
bezierPath];
[tPath moveToPoint:CGPointMake(0,
0)];
// r1点
[tPath addLineToPoint:CGPointMake(SCREEN_WIDTH,
0)]; // r2点
[tPath addLineToPoint:CGPointMake(SCREEN_WIDTH,
MIN_HEIGHT)];
// r4点
[tPath addQuadCurveToPoint:CGPointMake(0,
MIN_HEIGHT)
controlPoint:CGPointMake(_curveX,
_curveY)]; // r3,r4,r5确定的一个弧线
[tPath closePath];
_shapeLayer.path = tPath.CGPath;
}
// 计算弹簧效果坐标
- (void)calculatePath
{
// 由于手势结束时,r5执行了一个UIView的弹簧动画,把这个过程的坐标记录下来,并相应的画出_shapeLayer形状
CALayer *layer =
_curveView.layer.presentationLayer;
self.curveX = layer.position.x;
self.curveY = layer.position.y;
}
/*
r5点的作用非常重要,因为直接对CAShapeLayer实现动效不太好实现。所以通过对r5点实现弹簧动效,记录r5点的坐标,再用UIBezierPath形成路径,最后赋予CAShapeLayer,间接的完成了CAShapeLayer的弹簧动效。
*/
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
@end
// LYJBezierPathCuteView.m
// UIBezierPathAndCAShapeLayer超炫动画
//
// Created by ~~~ on 16/5/4.
// Copyright © 2016年 ~~~. All rights reserved.
//
#import "LYJBezierPathCuteView.h"
#define SCREEN_WIDTH ([[UIScreen mainScreen] bounds].size.width)
#define SCREEN_HEIGHT ([[UIScreen mainScreen] bounds].size.height)
#define MIN_HEIGHT 100
// 图形最小高度
@interface
LYJBezierPathCuteView()
@property (nonatomic,assign)
CGFloat mHeight;
@property (nonatomic,assign)
CGFloat curveX;
// r5点x坐标
@property (nonatomic,assign)
CGFloat curveY;
// r5点y坐标
@property (nonatomic,strong)
UIView *curveView;
// r5红点
@property (nonatomic,strong)
CAShapeLayer *shapeLayer;
@property (nonatomic,strong)
CADisplayLink *displayLink;
@property (nonatomic,assign)
BOOL isAnimating;
@end
@implementation LYJBezierPathCuteView
static NSString *kX =
@"curveX";
static NSString *kY =
@"curveY";
-(instancetype)initWithFrame:(CGRect)frame
{
self = [super
initWithFrame:frame];
if (self) {
[self
addObserver:self
forKeyPath:kX
options:NSKeyValueObservingOptionNew
context:nil];
[self
addObserver:self
forKeyPath:kY
options:NSKeyValueObservingOptionNew
context:nil];
[self
configShapeLayer];
[self
configCurveView];
[self
configAction];
}
return
self;
}
- (void)dealloc {
[self
removeObserver:self
forKeyPath:kX];
[self
removeObserver:self
forKeyPath:kY];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString
*,id> *)change context:(void *)context {
if ([keyPath
isEqualToString:kX] || [keyPath
isEqualToString:kY]) {
[self
updateShapeLayerPath];
}
}
#pragma mark 初始化
// 初始化CAShapeLayer
- (void)configShapeLayer
{
_shapeLayer = [CAShapeLayer
layer];
_shapeLayer.fillColor = [UIColor
colorWithRed:57/255.0
green:67/255.0
blue:89/255.0
alpha:1.0].CGColor;
[self.layer
addSublayer:_shapeLayer];
}
// 初始化r5点
- (void)configCurveView
{
// _curveView就是r5点
self.curveX =
SCREEN_HEIGHT / 2.0;
// r5点x坐标
self.curveY =
MIN_HEIGHT;
// r5点y坐标
_curveView = [[UIView
alloc] initWithFrame:CGRectMake(_curveX,
_curveY,
3, 3)];
_curveView.backgroundColor = [UIColor
redColor];
[self
addSubview:_curveView];
}
// 添加移动手势&CADisplayLink
- (void)configAction
{
_mHeight =
100;
// 手势移动时相对高度
_isAnimating =
NO;
// 是否处于动效状态
// 手势
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer
alloc]
initWithTarget:self
action:@selector(handlePanAction:)];
self.userInteractionEnabled =
YES;
[self
addGestureRecognizer:pan];
// CADisplayLink默认每秒运行60次calculatePath是算出在运行期间_curveView的坐标,从而确定_shapeLayer的形状
_displayLink = [CADisplayLink
displayLinkWithTarget:self
selector:@selector(calculatePath)];
[_displayLink
addToRunLoop:[NSRunLoop
currentRunLoop] forMode:NSDefaultRunLoopMode];
// 在手势结束的时候才调用calculatePath方法,所以一开始是暂停的
_displayLink.paused =
YES;
}
/*
手势移动时,r5红点跟着手势移动,_shapeLayer则根据r5的坐标来扩大自己的区域
手势结束时,r5红点通过UIView的动画方法来改变r5的坐标,同时_shapeLayer根据r5的坐标缩小自己的区域并最终返回原形
*/
- (void)handlePanAction:(UIPanGestureRecognizer *)pan
{
if(!_isAnimating)
{
if(pan.state ==
UIGestureRecognizerStateChanged)
{
// 手势移动时,_shapeLayer跟着手势向下扩大区域
CGPoint point = [pan
translationInView:self];
// 这部分代码使r5红点跟着手势走
_mHeight = point.y*0.7 +
MIN_HEIGHT;
self.curveX =
SCREEN_WIDTH/2.0 + point.x;
self.curveY =
_mHeight > MIN_HEIGHT ?
_mHeight : MIN_HEIGHT;
_curveView.frame =
CGRectMake(_curveX,
_curveY,
_curveView.frame.size.width,
_curveView.frame.size.height);
}
else
if (pan.state ==
UIGestureRecognizerStateCancelled ||
pan.state ==
UIGestureRecognizerStateEnded ||
pan.state ==
UIGestureRecognizerStateFailed)
{
// 手势结束时,_shapeLayer返回原状并产生弹簧动效
_isAnimating =
YES;
_displayLink.paused =
NO;
//开启displaylink,会执行方法calculatePath.
// 弹簧动效
[UIView
animateWithDuration:1.0
delay:0.0
usingSpringWithDamping:0.5
initialSpringVelocity:0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
//
曲线点(r5点)是一个view.所以在block中有弹簧效果.然后根据他的动效路径,在calculatePath中计算弹性图形的形状
_curveView.frame =
CGRectMake(SCREEN_WIDTH/2.0,
MIN_HEIGHT, 3,
3);
} completion:^(BOOL finished) {
if(finished)
{
_displayLink.paused =
YES;
_isAnimating =
NO;
}
}];
}
}
}
// 根据r5的位置,更新_shapeLayer形状
- (void)updateShapeLayerPath
{
// 更新_shapeLayer形状
UIBezierPath *tPath = [UIBezierPath
bezierPath];
[tPath moveToPoint:CGPointMake(0,
0)];
// r1点
[tPath addLineToPoint:CGPointMake(SCREEN_WIDTH,
0)]; // r2点
[tPath addLineToPoint:CGPointMake(SCREEN_WIDTH,
MIN_HEIGHT)];
// r4点
[tPath addQuadCurveToPoint:CGPointMake(0,
MIN_HEIGHT)
controlPoint:CGPointMake(_curveX,
_curveY)]; // r3,r4,r5确定的一个弧线
[tPath closePath];
_shapeLayer.path = tPath.CGPath;
}
// 计算弹簧效果坐标
- (void)calculatePath
{
// 由于手势结束时,r5执行了一个UIView的弹簧动画,把这个过程的坐标记录下来,并相应的画出_shapeLayer形状
CALayer *layer =
_curveView.layer.presentationLayer;
self.curveX = layer.position.x;
self.curveY = layer.position.y;
}
/*
r5点的作用非常重要,因为直接对CAShapeLayer实现动效不太好实现。所以通过对r5点实现弹簧动效,记录r5点的坐标,再用UIBezierPath形成路径,最后赋予CAShapeLayer,间接的完成了CAShapeLayer的弹簧动效。
*/
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
@end
相关文章推荐
- Gifski:一个跨平台的高质量 GIF 编码器
- 模仿动画的放大缩小容器
- jQuery 1.9.1源码分析系列(十五)动画处理之缓动动画核心Tween
- Android实现定制返回按钮动画效果的方法
- Android中ViewFlipper的使用及设置动画效果实例详解
- jQuery实现美观的多级动画效果菜单代码
- php判断GIF图片是否为动画的方法
- jQuery实现动画效果circle实例
- HTML5游戏引擎LTweenLite实现的超帅动画效果(附demo源码下载)
- 浅析JavaScript动画
- js排序动画模拟-插入排序
- javascript+HTML5的Canvas实现Lab单车动画效果
- 基于javascript实现漂亮的页面过渡动画效果附源码下载
- js实现按钮颜色渐变动画效果
- 超赞的jQuery图片滑块动画特效代码汇总
- jQuery实现连续动画效果实例分析
- 利用jquery制作滚动到指定位置触发动画
- jQuery实现的给图片点赞+1动画效果(附在线演示及demo源码下载)
- jQuery实现带有洗牌效果的动画分页实例
- jQuery动画效果相关方法实例分析