场景:直播间刷礼物时,会从发送按钮处飘一个选中的礼物到主播的头像处。
原理:找到发送按钮和主播头像所在它们共同的父视图的位置,然后进行组合动画操作
实现一个类,来完成这个功能
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface GiftAnimation : NSObject
//这个的gift可以作为外部送礼物的URL进行展示,userID可以作为唯一标识来处理特殊业务逻辑
+ (void)animationGiftWithFromRect:(CGRect)from startPoint:(CGPoint)startPoint toRect:(CGRect)to giftBean:(NSString *)gift micUser:(NSInteger)userID superView:(nonnull UIView *)aView;
@end
NS_ASSUME_NONNULL_END
#import "GiftAnimation.h"
@implementation GiftAnimation
+ (void)animationGiftWithFromRect:(CGRect)from startPoint:(CGPoint)startPoint toRect:(CGRect)to giftBean:(NSString *)gift micUser:(NSInteger)userID superView:(nonnull UIView *)aView{
if (to.size.width == 0) {
return;
}
UIImageView * contentView = [UIImageView new];
contentView.frame = from;
//可以使用传过来的入参进行网络图片加载,暂时用本地演示
contentView.image = IMAGE(@"gift");
//业务逻辑中可以使用userID作为唯一标识
contentView.tag = userID;
CGPoint endPoitnt = CGPointMake(to.origin.x+ to.size.width/2, to.origin.y + to.size.height/2);
CGFloat height = startPoint.y - endPoitnt.y;
CGFloat width = startPoint.x - endPoitnt.x;
CGPoint ceterPoint1 = CGPointMake(startPoint.x - width/2, startPoint.y - height /3);
CGPoint ceterPoint2 = CGPointMake(startPoint.x - 2*width/3, startPoint.y - 2*height /3);
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:startPoint]; // 起点
// 设置终点 和两个控制点(拐点)
[path addCurveToPoint:endPoitnt controlPoint1:ceterPoint1 controlPoint2:ceterPoint2];
//第二步:创建关键帧动画
CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
//pathAnimation.calculationMode = kCAAnimationPaced;// 我理解为节奏
pathAnimation.fillMode = kCAFillModeForwards;
pathAnimation.removedOnCompletion = NO;// 是否在动画完成后从 Layer 层上移除 回到最开始状态
pathAnimation.duration = 1.0f;// 动画时间
pathAnimation.delegate = (id)aView;
pathAnimation.repeatCount = 1;// 动画重复次数```
pathAnimation.path = path.CGPath;
///第三步:透明动画
CABasicAnimation *opacityAnim = [CABasicAnimation animationWithKeyPath:@"opacity"];
opacityAnim.fromValue = [NSNumber numberWithFloat:1.0];
opacityAnim.toValue = [NSNumber numberWithFloat:0.0];
opacityAnim.beginTime = 1.0;
opacityAnim.duration = 2.0;
opacityAnim.removedOnCompletion = NO;
opacityAnim.fillMode = kCAFillModeForwards;
//动画组
CAAnimationGroup *animGroup = [CAAnimationGroup animation];
animGroup.animations = [NSArray arrayWithObjects:pathAnimation, opacityAnim, nil];
animGroup.duration = 3.0;
animGroup.delegate = (id)aView;
animGroup.fillMode = kCAFillModeForwards;
animGroup.removedOnCompletion = NO;
//第四步: 添加动画
[contentView.layer addAnimation:animGroup forKey:[NSString stringWithFormat:@"myAnimation"]];// 添加动画
[aView addSubview:contentView];
//防止UIImage只生成不销毁,占用内存,动画完实时销毁
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(20 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[contentView removeFromSuperview];
});
}
@end
外部使用:
例子:一个发送按钮fromBtn,点击fromBtn,会从fromBtn处飘一个礼物到头像toIcon处
//获取fromBtn所在父视图的坐标位置CGRect
CGRect fromRec = [self.fromBtn convertRect:self.fromBtn.bounds toView:self.view];
//获取礼物初始位置,即fromBtn的中心点位置
CGPoint point = CGPointMake(fromRec.origin.x + (fromRec.size.width/2), fromRec.origin.y + (fromRec.size.height/2));
//获取toIcon所在父视图的坐标位置CGRect
CGRect toRec = [self.toIcon convertRect:self.toIcon.bounds toView:self.view];
//调用方法,开始动画,micUser和giftBean可以根据具体业务具体处理,不需要的可以去除
[GiftAnimation animationGiftWithFromRect:rec startPoint:point toRect:toRec giftBean:@"" micUser:32 superView:self.view];