开篇废话:简单的利用贝塞尔曲线实现一个简单的切图动画。
步骤一、搭建外层view视图
这里就是 创建一个 KDSShredPhotoImageView 视图展示。
- (void)customClip
{
self.gzImageView = [[KDSShredPhotoImageView alloc] init];
self.gzImageView.contentMode = UIViewContentModeScaleAspectFit;
self.gzImageView.image = [UIImage imageNamed:@"gz"];
[self addSubview:self.gzImageView];
[self.gzImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self);
}];
UIPanGestureRecognizer * pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action: @selector(panAction:)];
self.userInteractionEnabled = YES;
[self addGestureRecognizer:pan];
}
- (void)panAction:(UIPanGestureRecognizer *)pan
{
switch (pan.state) {
case UIGestureRecognizerStateBegan:
{
[self.gzImageView judgePanInSelfRect:pan];
}
break;
case UIGestureRecognizerStateChanged:
{
[self.gzImageView judgePanInSelfRect:pan];
}
break;
case UIGestureRecognizerStateEnded:
{
[self.gzImageView judgePanInSelfRect:pan];
}
break;
default:
break;
}
}
步骤二、封装 KDSShredPhotoImageView
1、声明属性
@interface** KDSShredPhotoImageView()
//进入点
@property (nonatomic,assign) CGPoint enterPoint;
//离开点
@property (nonatomic,assign) CGPoint levelPoint;
//内部移动点
@property (nonatomic,assign) CGPoint movePoint;
//刀痕贝塞尔曲线
@property (nonatomic,strong) UIBezierPath * bezierPath;
//模拟刀痕线
@property (nonatomic,strong) CAShapeLayer * shapelayer;
//隐藏消除的shapelayer
@property (nonatomic,strong) CAShapeLayer * deleteShapelayer;
@end
@implementation KDSShredPhotoImageView
- (instancetype)init
{
self = [super init];
if (self) {
self.clipsToBounds = YES;
}
return self;
}
2、跟踪贝塞尔曲线
这里记录 UIPanGestureRecognizer 手势的触电变更,并记录于贝塞尔曲线
- (void)judgePanInSelfRect:(UIPanGestureRecognizer *)pan
{
//进行剪切
if (!self.bezierPath) {
self.bezierPath = [[UIBezierPath alloc] init];
}
if (!self.shapelayer) {
self.shapelayer = [[CAShapeLayer alloc] init];
self.shapelayer.fillColor = nil;
self.shapelayer.strokeColor = [UIColor blackColor].CGColor;
self.shapelayer.lineWidth = 1;
[self.layer addSublayer:elf.shapelayer];
}
if (!self.deleteShapelayer) {
self.deleteShapelayer = [[CAShapeLayer alloc] init];
self.deleteShapelayer.fillColor = [UIColor whiteColor].CGColor;
self.deleteShapelayer.strokeColor = nil;
}
CGPoint point = [pan locationInView:**self**];
switch (pan.state) {
case UIGestureRecognizerStateBegan:
{
//记录开始点
[self recordEnterPoint:point];
}
break;
case UIGestureRecognizerStateChanged:
{
//记录当前点
[self recordMovePoint:point];
}
break;
case UIGestureRecognizerStateEnded:
{
//记录结束点
[self recordLevelPoint:point];
//开始计算切割区域
[self judgeXYDirectAndClip];
}
break;
default:
break;
}
}
//记录进入点
- (void)recordEnterPoint:(CGPoint)point
{
self.enterPoint = point;
[self.bezierPath moveToPoint:point];
self.shapelayer.path = self.bezierPath.CGPath;
}
//记录离开点
- (void)recordLevelPoint:(CGPoint)point
{
self.levelPoint = point;
[self.bezierPath addLineToPoint:point];
self.shapelayer.path = **self**.bezierPath.CGPath;
[self judgeXYDirectAndClip];
//重置进入点
self.enterPoint = CGPointZero;
//重置移动点
self.movePoint = CGPointZero;
//重置贝塞尔
self.bezierPath = nil;
}
//记录移动点
- (void)recordMovePoint:(CGPoint)point
{
self.movePoint = point;
[self.bezierPath addLineToPoint:point];
self.shapelayer.path = self.bezierPath.CGPath;
}
3、矩形任意切割得到较小的贝塞尔封闭区域
到底任意一刀如何切割这里利用的是线性方程,enterPoint开始点及levelPoint结束点来确定一条线性方程 y = kx + b
- (void)definedKTypeFunctionWithStartPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint
{
CGPoint descartesStartPoint = CGPointMake(startPoint.x, self.height - startPoint.y);
CGPoint descartesEndPoint = CGPointMake(endPoint.x, self.height - endPoint.y);
CGFloat k = (descartesStartPoint.y - descartesEndPoint.y) / (descartesStartPoint.x - descartesEndPoint.x);
//保存满足的矩形角点
NSMutableArray * saveContainCorners = @[].mutableCopy;
//需要判断的矩形4个角点。
NSArray * allNeedCheckPoint = @[@(CGPointMake(self.width, 0)),@(CGPointMake(self.width, self.height)),@(CGPointMake(0, self.height)),@(CGPointMake(0, 0))];
//这里记录 x,y的目的很简单,就是计算当一个矩形3个角都满足条件的时候,反取另一个不满足的角,组合成贝塞尔曲线。
CGFloat x = 0;
CGFloat y = 0;
for (int i = 0; i < allNeedCheckPoint.count; i++) {
//这里,配合笛卡尔坐标系将Y值反转一下。
CGPoint checkPoint = [allNeedCheckPoint[i] CGPointValue];
if ([self isCornerIsContainWithK:k startPoint:descartesStartPoint checkPoint:CGPointMake(checkPoint.x, self.height - checkPoint.y)]) {
[saveContainCorners addObject:@(checkPoint)];
}
x += checkPoint.x;
y += checkPoint.y;
}
if (saveContainCorners.count != 3) {
//满足条件的不是3个角
if (k > 0) {
for (int i = 0; i < saveContainCorners.count; i++) {
CGPoint point = [saveContainCorners[i] CGPointValue];
[self.bezierPath addLineToPoint:point];
}
} else {
for (NSInteger i = saveContainCorners.count - 1; i >= 0; i--) {
CGPoint point = [saveContainCorners[i] CGPointValue];
[self.bezierPath addLineToPoint:point];
}
}
} else {
//满足条件的是3个角,利用总数求剩余的不满足条件的角
for (int i = 0; i < saveContainCorners.count; i++) {
CGPoint point = [saveContainCorners[i] CGPointValue];
x -= point.x;
y -= point.y;
}
[self.bezierPath addLineToPoint:CGPointMake(x, y)];
}
}
//判断矩形的角点是否满足在线性方程上,在上面就记录下来。
- (BOOL)isCornerIsContainWithK:(CGFloat)k startPoint:(CGPoint)startPoint checkPoint:(CGPoint)checkPoint
{
CGFloat y = k * (checkPoint.x - startPoint.x) + startPoint.y;
return checkPoint.y > y;
}
4、切鬼子动画
这里逻辑也很简单,将根据贝塞尔曲线区域切下的部分生成一张图片赋值给新的 UIImageView ,给它一个旋转及位移动画。在原来的 KDSShredPhotoImageView 上添加一个空白的贝塞尔曲线区域,目的是遮盖。
//进行剪切
- (void)judgeXYDirectAndClip
{
//判断起始点是否在 imageView 外面,如果在里面就不进行切割,因为 拖拽手势的承载视图是当前 imageView 的父视图。
if ([self.layer containsPoint:self.enterPoint] || [self.layer containsPoint:self.levelPoint]) {
return;
}
//计算完整贝塞尔曲线
[self definedKTypeFunctionWithStartPoint:self.enterPoint endPoint:self.levelPoint];
//绘制贝塞尔曲线封闭区域图片
UIGraphicsBeginImageContextWithOptions(self.frame.size, false, 1);
UIBezierPath * bzPath = self.bezierPath;
[bzPath addClip];
UIImage * image = self.image;
[image drawInRect:self.bounds];
UIImage * clipImgae = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//添加被切割部分图片展示及动画效果
UIImageView * clipImageView = [[UIImageView alloc] initWithFrame:self.frame];
clipImageView.backgroundColor = [UIColor clearColor];
clipImageView.image = clipImgae;
[self.superview addSubview:clipImageView];
[UIView animateWithDuration:4.5 animations:^{
clipImageView.center = CGPointMake(clipImageView.center.x, clipImageView.center.y + 500);
CGAffineTransform t = CGAffineTransformIdentity;
t = CGAffineTransformRotate(t, M_PI_2 / 2.0);
clipImageView.transform = t;
}];
//原图添加遮盖
UIBezierPath *path = self.bezierPath;
self.deleteShapelayer.path = path.CGPath;
[self.layer addSublayer:self.deleteShapelayer];
}
代码拙劣,大神勿笑。