上一期动画特效,我讲解了《图片翻转》。这一节,我们继续动画之旅,讲解一下图片折叠效果。
图片折叠:顾名思义就是绕着图片的x或者y轴进行折叠,并且看上去有透视效果。先看看最终的效果图:
编程思路分析:
1. 使用一张图片 (Failed) 因为进行图片旋转的时候,如果下半部分的图片往前翻转了,上半部分的图片就会同时往后翻转了。
2. 使用两张图片 (Failed) 通过控制图片的显示与隐藏,但是折叠效果看不出来。
3. 使用一个View(它的大小保持和传递进来的图片大小一致),两张图片,而这两张图片是通过代码裁切,将一张图片裁切为上下两部分,然后分别放置在View的上半部分和下半部分,看起来好像是一张图片(Succeed)。
自定义FoldingView
@interface FoldingView : UIView
- (instancetype)initWithImage:(UIImage *)image;
@end
一、进行初始化工作
- (instancetype)initWithImage:(UIImage *)image {
if (self = [super init]) {
self.image = image;
// 父View的尺寸等于传递过来的图片的尺寸
self.frame = CGRectMake(0, 0, image.size.width, image.size.height);
[self addTopView];
[self addBottomView];
[self addGestures];
}
return self;
}
二、addTopView用来裁切图片的上半部分并且放置在FoldingView的上面。
- (void)addTopView {
UIImage *topImage = [self cutImageWithOrignalImage:self.image type:LayerSectionTop];
// 设置图片的frame是父View的frame一半
self.topImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.bounds), CGRectGetMidY(self.bounds))];
self.topImageView.userInteractionEnabled = YES;
self.topImageView.image = topImage;
// 设置内容模式为ScaleAspectFit,图片不拉伸
self.topImageView.contentMode = UIViewContentModeScaleAspectFit;
// 下面设置的position和anchorPoint不写,其实图片已经显示在父控件上面的一半了,但是动画操作的layer是要针对position和anchorPoint的,所以设置他们!!!
self.topImageView.layer.position = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
self.topImageView.layer.anchorPoint = CGPointMake(0.5, 1.0);
self.topImageView.layer.mask = [self maskForSection:LayerSectionTop withRect:self.topImageView.bounds];
//self.topImageView.layer.transform = CATransform3DMakePerspective(CGPointZero);
self.topImageView.layer.transform = [self transform3D];
[self addSubview:self.topImageView];
}
三、裁切图片的方法: cutImageWithOrignalImage:type方法。为了区分是裁切的上半部分还是下半部分的图片,我定义了一个枚举,用来区别。
typedef NS_ENUM(NSInteger, LayerSection){
LayerSectionTop,
LayerSectionBottom
};
// 裁切图片的上面一半或者下面一半
- (UIImage *)cutImageWithOrignalImage:(UIImage *)image type:(LayerSection)section {
CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height * 0.5);
if (section == LayerSectionBottom) {
rect.origin.y = image.size.height * 0.5;
}
CGImageRef imageRef = CGImageCreateWithImageInRect(image.CGImage, rect);
UIImage *finalImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
return finalImage;
}
然后,我们接着看第二点的代码;有两点我需要说明:1.position和anchorPoint的使用(如果想了解具体使用,请参照我以前写的博客《position和anchorPoint》); 2. 一个View设置部分圆角效果(《Core Animation高级理论知识汇总》中的专用图层有介绍过)。其实,我们完全可以通过设置被裁切图片的frame来控制图片在FoldingView中的显示位置来达到效果,之所以设置position和anchorPoint就是为了后面的折叠效果做准备。下面一张图说明了折叠原理。
四、添加手势
- (void)addGestures {
UIPanGestureRecognizer *topPan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panTopEvent:)];
[self.topImageView addGestureRecognizer:topPan];
}
- (void)panTopEvent:(UIPanGestureRecognizer *)recognizer {
CGPoint location = [recognizer locationInView:self];
if (recognizer.state == UIGestureRecognizerStateBegan) {
self.initialLocation = location.y;
[self bringSubviewToFront:self.topImageView];
}
// 上面一半的图片只允许往下拉
CGFloat delta = location.y - self.initialLocation;
if (delta < 0) return;
CGFloat conversionFactor = -M_PI / CGRectGetHeight(self.bounds);
CGFloat angle = delta * conversionFactor;
if (angle <= -M_PI) {
angle = -M_PI;
}
self.topImageView.layer.transform = CATransform3DConcat(CATransform3DMakeRotation(angle, 1, 0, 0), [self transform3D]);
if (recognizer.state == UIGestureRecognizerStateEnded ||
recognizer.state == UIGestureRecognizerStateCancelled) {
[UIView animateWithDuration:0.25 animations:^{
self.topImageView.layer.transform = [self transform3D];
} completion:^(BOOL finished) {
//[self shakeTop];
}];
}
}
// 透视效果
- (CATransform3D)transform3D {
CATransform3D transform = CATransform3DIdentity;
transform.m34 = kPerspective;
return transform;
}
关于透视效果有什么不明白的,可以参照
《CATransform3D 特效详解》。
至此,上半部分图片折叠效果已经实现了,而下半部分很相似,我就不再说明了。