// // XPRoomAnimationView.m // xplan-ios // // Created by 冯硕 on 2021/12/6. // #import "XPRoomAnimationView.h" ///Third #import #import #import #import ///Tool #import "XPGiftStorage.h" #import "XPMacro.h" #import "ThemeColor+Room.h" #import "NetImageView.h" ///Model #import "MicroQueueModel.h" #import "MicroStateModel.h" #import "UserInfoModel.h" #import "GiftReceiveInfoModel.h" #import "GiftInfoModel.h" #import "RoomInfoModel.h" #import "AttachmentModel.h" #import "XPGiftBigPrizeModel.h" #import "XPGiftBannerUserInfoModel.h" #import "XPRoomGiftBroadCastModel.h" ///View #import "XPRoomGiftBannerView.h" #import "XPRoomGiftBroadcastView.h" #import "XPRoomCandyGiftView.h" @interface XPRoomAnimationView () ///展示的不同层级 ///最底层的 @property (nonatomic,strong) XPRoomAnimationHitView * lowLevelView; ///中层的 @property (nonatomic,strong) XPRoomAnimationHitView * middleLevelView; ///高层的 @property (nonatomic,strong) XPRoomAnimationHitView * highLevleView; ///动画管理类 @property (strong, nonatomic) SVGAParser *parser; ///送礼物的特效 @property (nonatomic,strong) SVGAImageView *giftEffectView; ///代理 @property (nonatomic,weak) iddelegate; ///礼物特效的队列 @property (nonatomic,strong) NSMutableArray *giftEffectQueue; ///复用池 @property (strong,nonatomic)NSMutableSet * bannerDequePool; ///可见池 @property (strong,nonatomic)NSMutableSet * bannerVisiablePool; ///礼物定时器 6s刷新一次 @property (nonatomic,strong)dispatch_source_t giftEffectTimer; ///福袋大礼物的队列 @property (nonatomic,strong) NSMutableArray *bigPrizeGiftQueue; ///福袋 @property (nonatomic,strong)dispatch_source_t luckyprizeTimer; ///全服广播 礼物飘屏 @property (nonatomic,strong) NSMutableArray *giftBroadcastQueue; #pragma mark - 送礼物的动画的 ///礼物的队列 @property (nonatomic,strong) NSMutableArray *giftQueue; ///定时器 @property (nonatomic ,strong)dispatch_source_t giftTimer; ///复用池 应为送礼物的动画是一个很频繁的事情 所以可以减少内存的消耗 @property (nonatomic,strong) NSMutableSet * giftReuseArray; ///可见池 @property (nonatomic,strong) NSMutableSet * giftVisibleArray; ///糖果树礼物的队列 @property (nonatomic,strong) NSMutableArray *candyTreegiftQueue; @end @implementation XPRoomAnimationView - (void)dealloc { [NSObject cancelPreviousPerformRequestsWithTarget:self]; } - (instancetype)initWithdelegate:(id)delegate { self = [super init]; if (self) { [[NIMSDK sharedSDK].chatManager addDelegate:self]; [[NIMSDK sharedSDK].broadcastManager addDelegate:self]; self.delegate = delegate; [self initSubViews]; [self initSubViewConstraints]; } return self; } #pragma mark - Private Method - (void)initSubViews { [self addSubview:self.lowLevelView]; [self addSubview:self.middleLevelView]; [self addSubview:self.highLevleView]; } - (void)initSubViewConstraints { [self.lowLevelView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.mas_equalTo(self); }]; [self.middleLevelView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.mas_equalTo(self); }]; [self.highLevleView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.mas_equalTo(self); }]; } #pragma mark - NIMChatManagerDelegate - (void)onRecvMessages:(NSArray *)messages { for (NIMMessage * message in messages) { [self handleMessage:message]; } } #pragma mark - NIMBroadcastDelegate // 广播消息 - (void)onReceiveBroadcastMessage:(NIMBroadcastMessage *)broadcastMessage{ if (broadcastMessage.content) { NSDictionary *msgDictionary = [broadcastMessage.content toJSONObject]; AttachmentModel *attachment = [AttachmentModel modelWithJSON:msgDictionary[@"body"]]; if (attachment.first == CustomMessageType_Gift && attachment.second == Custom_Message_Sub_Gift_ChannelNotify){///广播 全服礼物飘屏 [self receiveBroadcastGift:attachment]; } } } //发送消息成功回调 - (void)sendMessage:(NIMMessage *)message didCompleteWithError:(NSError *)error { [self handleMessage:message]; } - (void)handleMessage:(NIMMessage *)message { if (message.session.sessionType == NIMSessionTypeChatroom && message.messageType == NIMMessageTypeCustom) { NIMCustomObject *obj = (NIMCustomObject *)message.messageObject; if (obj.attachment != nil && [obj.attachment isKindOfClass:[AttachmentModel class]]) { AttachmentModel *attachment = (AttachmentModel *)obj.attachment; if (attachment.first == CustomMessageType_Gift) { GiftReceiveInfoModel * receiveInfo = [GiftReceiveInfoModel modelWithJSON:attachment.data]; receiveInfo.isLuckyBagGift = attachment.second == Custom_Message_Sub_Gift_LuckySend; receiveInfo.isBatch = YES; [self receiveGiftHandleSendGiftAnimation:attachment]; [self receiveGift:receiveInfo]; } else if (attachment.first == CustomMessageType_AllMicroSend) { //发送全麦礼物 或者多人礼物 GiftReceiveInfoModel * receiveInfo = [GiftReceiveInfoModel modelWithJSON:attachment.data]; receiveInfo.isLuckyBagGift = (attachment.second == Custom_Message_Sub_AllMicroLuckySend || attachment.second == Custom_Message_Sub_AllBatchMicroLuckySend); receiveInfo.isBatch = (attachment.second == Custom_Message_Sub_AllBatchSend || attachment.second == Custom_Message_Sub_AllBatchMicroLuckySend); [self receiveGiftHandleSendGiftAnimation:attachment]; [self receiveGift:receiveInfo]; } else if (attachment.first == CustomMessageType_LuckyBag) {//福袋消息厅内展示 [self receiveLuckyGiftBigPrize:attachment]; } else if (attachment.first == CustomMessageType_Candy_Tree || attachment.second == Custom_Message_Sub_Candy_Tree_InRoom_NeedAllMicSend ) {//糖果树 [self receiveCandyTreeGiftHighLevle:attachment]; } } } } #pragma mark - 糖果树横幅动画 - (void)receiveCandyTreeGiftHighLevle:(AttachmentModel *)attatchment { if (self.candyTreegiftQueue.count == 0) { [self createCandyTreeBannerAnimation:attatchment.data]; } [self.candyTreegiftQueue addObject:attatchment.data]; } - (void)createCandyTreeBannerAnimation:(NSDictionary *)dic { XPRoomCandyGiftView *candyTreeView = [[XPRoomCandyGiftView alloc] initWithFrame:CGRectMake(KScreenWidth, kNavigationHeight + 15, KScreenWidth, 45)]; candyTreeView.candyInfo = dic; [self.middleLevelView addSubview:candyTreeView]; POPSpringAnimation *springAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPViewCenter]; springAnimation.springSpeed = 12; springAnimation.springBounciness = 10.f; springAnimation.fromValue = [NSValue valueWithCGPoint:candyTreeView.center]; springAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(candyTreeView.frame.size.width / 2, candyTreeView.center.y)]; [springAnimation setCompletionBlock:^(POPAnimation *anim, BOOL finished) { if (finished) { POPBasicAnimation *moveAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPViewCenter]; moveAnimation.fromValue = [NSValue valueWithCGPoint:CGPointMake(candyTreeView.frame.size.width / 2, candyTreeView.center.y)]; moveAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(-KScreenWidth/2, candyTreeView.center.y)]; moveAnimation.beginTime = CACurrentMediaTime() + 3; moveAnimation.duration = 0.5; moveAnimation.repeatCount = 1; moveAnimation.removedOnCompletion = YES; @kWeakify(self); [moveAnimation setCompletionBlock:^(POPAnimation *anim, BOOL finished) { @kStrongify(self); if (finished) { [candyTreeView removeFromSuperview]; [self.candyTreegiftQueue removeObjectAtIndex:0]; if (self.candyTreegiftQueue.count > 0) { [self createCandyTreeBannerAnimation:self.candyTreegiftQueue.firstObject]; } } }]; [candyTreeView pop_addAnimation:moveAnimation forKey:@"moveOutAnimation"]; } }]; [candyTreeView pop_addAnimation:springAnimation forKey:@"candyTreespingOutAnimation"]; } #pragma mark - 收到礼物展示动画 - (void)receiveGiftHandleSendGiftAnimation:(AttachmentModel *)attachment { GiftReceiveInfoModel * receiveInfo = [GiftReceiveInfoModel modelWithJSON:attachment.data]; GiftInfoModel * giftInfo = receiveInfo.gift ? receiveInfo.gift : receiveInfo.giftInfo; if (attachment.second == Custom_Message_Sub_AllMicroLuckySend || attachment.second == Custom_Message_Sub_AllBatchMicroLuckySend || attachment.second == Custom_Message_Sub_Gift_LuckySend) { NSString * giftId = [NSString stringWithFormat:@"%ld", receiveInfo.luckyGiftList.giftList.firstObject.giftId]; giftInfo = [[XPGiftStorage shareStorage] findGiftInfo:giftId]; } if (giftInfo == nil) { giftInfo = [[XPGiftStorage shareStorage] findGiftInfo:receiveInfo.giftId]; } ///在最外面判断是否可以拿到那个礼物 后面使用就不用判断了 receiveInfo.gift = giftInfo; receiveInfo.isLuckyBagGift = (attachment.second == Custom_Message_Sub_AllMicroLuckySend || attachment.second == Custom_Message_Sub_AllBatchMicroLuckySend || attachment.second == Custom_Message_Sub_Gift_LuckySend); [self.giftQueue addObject:receiveInfo]; if (self.giftTimer == nil && self.giftQueue > 0) { [self startGiftQueueTimer]; } } //扫描礼物队列 - (void)startGiftQueueTimer { NSTimeInterval period = 0.5; //设置时间间隔一个礼物动画的时间 dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0)); dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, period * NSEC_PER_SEC, 0 * NSEC_PER_SEC); dispatch_source_set_event_handler(_timer, ^{ dispatch_async(dispatch_get_main_queue(), ^{ if (self.giftQueue.count) { GiftReceiveInfoModel * receiveInfo = self.giftQueue.firstObject; [self handoutGiftDistributeAnimation:receiveInfo]; [self.giftQueue removeObject:receiveInfo]; }else { dispatch_source_cancel(_timer); self.giftTimer = nil; } }); }); dispatch_resume(_timer); self.giftTimer = _timer; } ///处理礼物的分发动画 - (void)handoutGiftDistributeAnimation:(GiftReceiveInfoModel *)receiveInfo { GiftInfoModel * giftInfo = receiveInfo.gift; NSString * sendUid = receiveInfo.uid; CGPoint starPoint = [self getGiftAnimationPoint:sendUid isEnd:NO]; CGPoint endPoint; if (receiveInfo.targetUids.count > 0) { if (receiveInfo.isLuckyBagGift) { NSString * targetUid = [NSString stringWithFormat:@"%ld", receiveInfo.luckyGiftList.user.uid]; endPoint = [self getGiftAnimationPoint:targetUid isEnd:YES]; dispatch_time_t timer = dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC); dispatch_after(timer, dispatch_get_main_queue(), ^{ [self beginGiftAnimation:giftInfo.giftUrl startPoint:starPoint endPoint:endPoint]; }); } else { for (NSString *targetUid in receiveInfo.targetUids) { endPoint = [self getGiftAnimationPoint:targetUid isEnd:YES]; dispatch_time_t timer = dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC); dispatch_after(timer, dispatch_get_main_queue(), ^{ [self beginGiftAnimation:giftInfo.giftUrl startPoint:starPoint endPoint:endPoint]; }); } } } else { NSString * targetUid = receiveInfo.targetUid; endPoint = [self getGiftAnimationPoint:targetUid isEnd:YES]; dispatch_time_t timer = dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC); dispatch_after(timer, dispatch_get_main_queue(), ^{ [self beginGiftAnimation:giftInfo.giftUrl startPoint:starPoint endPoint:endPoint]; }); } } ///起点 - (CGPoint)getGiftAnimationPoint:(NSString *)uid isEnd:(BOOL)isEnd{ CGPoint point = [self.delegate centerForUserAtUid:uid]; //没有找到那个点 那个用户不再坑位上 if (point.x <= 0 || point.y <= 0) { if (isEnd) { point = CGPointMake([UIScreen mainScreen].bounds.size.width /2 + 30, 44); } else { point = CGPointMake([UIScreen mainScreen].bounds.size.width / 2, 44); } } return point; } - (void)beginGiftAnimation:(NSString *)giftUrl startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint { NetImageView * giftImageView = [self.giftReuseArray anyObject]; if (giftImageView == nil) { giftImageView = [[NetImageView alloc]initWithFrame:CGRectMake(0, 0 , 55, 55)]; giftImageView.center = startPoint; giftImageView.alpha = 1; giftImageView.layer.anchorPoint = CGPointMake(0.5, 0.5); giftImageView.imageUrl = giftUrl; [self.giftVisibleArray addObject:giftImageView]; }else { [self.giftReuseArray removeObject:giftImageView]; } giftImageView.hidden = NO; giftImageView.imageUrl = giftUrl; [self.lowLevelView addSubview:giftImageView]; CAAnimationGroup *group = [self createGiftAnimationStartPoint:startPoint endPoint:endPoint]; [giftImageView.layer addAnimation:group forKey:@"giftDisplayViewAnimation"]; [self performSelector:@selector(aniationDidFinish:) withObject:giftImageView afterDelay:(3.2+0.25)]; } /// 图片运动的动画组 /// @param startPoint 开始的点 /// @param endPoint 结束的点 - (CAAnimationGroup *)createGiftAnimationStartPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint{ CGPoint centerPoint = CGPointMake(KScreenWidth / 2, KScreenHeight / 2); CAKeyframeAnimation *animation0 = [CAKeyframeAnimation animation]; animation0.duration = 0.8; animation0.keyPath = @"transform.scale"; animation0.values = @[@1.0,@1.5,@2.0,@1.5]; animation0.repeatCount = 1; animation0.calculationMode = kCAAnimationCubic; animation0.removedOnCompletion = NO; animation0.fillMode = kCAFillModeForwards; CAKeyframeAnimation *animation1 = [CAKeyframeAnimation animation]; animation1.duration = 0.8; animation1.beginTime = 0.8; animation1.keyPath = @"transform.scale"; animation1.values = @[@1.5,@2.0,@2.5,@3.0]; animation1.repeatCount = 1; animation1.calculationMode = kCAAnimationCubic; animation1.removedOnCompletion = NO; animation1.fillMode = kCAFillModeForwards; CAKeyframeAnimation *animation2 = [CAKeyframeAnimation animation]; animation2.duration = 0.8; animation2.beginTime = 0.8; animation2.keyPath = @"position"; animation2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];; animation2.values = @[[NSValue valueWithCGPoint:startPoint],[NSValue valueWithCGPoint:CGPointMake(centerPoint.x ,centerPoint.y)]]; animation2.repeatCount = 1; animation2.removedOnCompletion = NO; animation2.fillMode = kCAFillModeForwards; CAKeyframeAnimation *animation3 = [CAKeyframeAnimation animation]; animation3.duration = 0.8; animation3.beginTime = 2.6;//0.8+0.8+1 animation3.keyPath = @"transform.scale"; animation3.values = @[@3,@2.5,@2,@1.5,@1]; animation3.repeatCount = 1; CAKeyframeAnimation *animation4 = [CAKeyframeAnimation animation]; animation4.duration = 0.8; animation4.beginTime = 2.6; animation4.keyPath = @"position"; animation4.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; animation4.values = @[[NSValue valueWithCGPoint:CGPointMake(centerPoint.x ,centerPoint.y)],[NSValue valueWithCGPoint:endPoint]]; animation4.repeatCount = 1; CAAnimationGroup *group = [CAAnimationGroup animation]; group.duration = 3.2; group.animations = @[animation0,animation1,animation2, animation3,animation4]; group.repeatCount = 1; group.removedOnCompletion = NO; group.fillMode = kCAFillModeForwards; return group; } - (void)aniationDidFinish:(NetImageView *)giftImageView{ [giftImageView removeFromSuperview]; giftImageView.image = nil; giftImageView.hidden = YES; [self.giftVisibleArray removeObject:giftImageView]; [self.giftReuseArray addObject:giftImageView]; } #pragma mark - 全服礼物 飘屏 - (void)receiveBroadcastGift:(AttachmentModel *)attachment { XPRoomGiftBroadCastModel *giftNotifyInfo = [XPRoomGiftBroadCastModel modelWithJSON:attachment.data]; if (self.giftBroadcastQueue.count == 0) { [self createGiftBroadcastViewAnimation:giftNotifyInfo]; } [self.giftBroadcastQueue addObject:giftNotifyInfo]; } - (void)createGiftBroadcastViewAnimation:(XPRoomGiftBroadCastModel *)giftModel { XPRoomGiftBroadcastView * view = [[XPRoomGiftBroadcastView alloc] init]; view.delegate = self; view.frame = CGRectMake(KScreenWidth, 35 + statusbarHeight, KScreenWidth, 150); view.broadcastModel = giftModel; [self.highLevleView addSubview:view]; [self beginGiftBraodcastViewAnimation:view notifyStaySecond:giftModel.notifyStaySecond]; } - (void)beginGiftBraodcastViewAnimation:(XPRoomGiftBroadcastView *)view notifyStaySecond:(int)notifyStaySecond{ CGFloat startTime = notifyStaySecond > 0 ? notifyStaySecond : 3.0; POPSpringAnimation *springAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPViewCenter]; springAnimation.springSpeed = 12; springAnimation.springBounciness = 10.f; springAnimation.fromValue = [NSValue valueWithCGPoint:view.center]; springAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(0 + view.frame.size.width / 2, view.center.y)]; [springAnimation setCompletionBlock:^(POPAnimation *anim, BOOL finished) { if (finished) { [self removeGiftBroadcastOutAnimation:view stayTime:startTime]; } }]; [view pop_addAnimation:springAnimation forKey:@"spingOutAnimation"]; } - (void)removeGiftBroadcastOutAnimation:(XPRoomGiftBroadcastView *)view stayTime:(CGFloat)stayTime{ POPBasicAnimation *moveAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPViewCenter]; moveAnimation.fromValue = [NSValue valueWithCGPoint:view.center]; moveAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(-KScreenWidth/2, view.center.y)]; moveAnimation.beginTime = CACurrentMediaTime() + stayTime; moveAnimation.duration = 0.5; moveAnimation.repeatCount = 1; moveAnimation.removedOnCompletion = YES; @kWeakify(self); [moveAnimation setCompletionBlock:^(POPAnimation *anim, BOOL finished) { @kStrongify(self); if (finished) { [view removeFromSuperview]; [self.giftBroadcastQueue removeObjectAtIndex:0]; if (self.giftBroadcastQueue.count > 0) { [self createGiftBroadcastViewAnimation:self.giftBroadcastQueue.firstObject]; } } }]; [view pop_addAnimation:moveAnimation forKey:@"moveOutAnimation"]; } #pragma mark - XPRoomGiftBroadcastViewDelegate - (void)xPRoomGiftBroadcastView:(XPRoomGiftBroadcastView *)view enterRoom:(NSString *)roomUid { //TODO: 退出当前房间 换个房间 } - (void)xPRoomGiftBroadcastView:(XPRoomGiftBroadcastView *)view didClickClose:(XPRoomGiftBroadCastModel *)broadcastModel { [view removeFromSuperview]; [view pop_removeAllAnimations]; if ([self.giftBroadcastQueue containsObject:broadcastModel]) { [self.giftBroadcastQueue removeObject:broadcastModel]; } if (self.giftBroadcastQueue.count > 0) { [self createGiftBroadcastViewAnimation:self.giftBroadcastQueue.firstObject]; } } #pragma mark - 福袋中 四级奖品 - (void)receiveLuckyGiftBigPrize:(AttachmentModel *)attachment { if (attachment.second == Custom_Message_Sub_Room_Gift_LuckBag || attachment.second == Custom_Message_Sub_Room_Gift_LuckBag_Server) { XPGiftBigPrizeModel * prizeModel = [[XPGiftBigPrizeModel alloc] init]; prizeModel.giftName = attachment.data[@"giftName"]; prizeModel.luckyBagName = attachment.data[@"luckyBagName"]; prizeModel.nick = attachment.data[@"nick"]; [self.bigPrizeGiftQueue addObject:prizeModel]; if (self.luckyprizeTimer == nil) { [self startHandleBigPrizeGiftTimer]; } } } - (void)startHandleBigPrizeGiftTimer { NSTimeInterval period = 1.0; //设置时间间隔 dispatch_queue_t queue = dispatch_get_global_queue(0, 0); dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), period * NSEC_PER_SEC, 0); //每秒执行 @kWeakify(self); dispatch_source_set_event_handler(_timer, ^{ @kStrongify(self) if (self.bigPrizeGiftQueue.count > 0) { dispatch_sync(dispatch_get_main_queue(), ^{ XPGiftBigPrizeModel * prizeModel = [self.bigPrizeGiftQueue firstObject]; [self createBigPrizeAnimation:prizeModel]; [self.bigPrizeGiftQueue removeObject:prizeModel]; }); }else { dispatch_source_cancel(_timer); self.luckyprizeTimer = nil; } }); dispatch_resume(_timer); self.luckyprizeTimer = _timer; } - (void)createBigPrizeAnimation:(XPGiftBigPrizeModel *)prizeModel { NSAttributedString *text = [self createBigPrizeAttribute:prizeModel]; UIImageView *luckyBgView = [[UIImageView alloc] initWithFrame:CGRectMake(KScreenWidth, kNavigationHeight + 15, KScreenWidth, 45)]; luckyBgView.contentMode = UIViewContentModeScaleAspectFill; luckyBgView.image= [UIImage imageNamed:@"room_lucky_gift_big_prize_bg"]; UILabel *contentLabel = [[UILabel alloc] initWithFrame:CGRectMake(90, 0, KScreenWidth - 160, 45)]; contentLabel.attributedText = text; contentLabel.textAlignment = NSTextAlignmentCenter; contentLabel.numberOfLines = 2; [luckyBgView addSubview:contentLabel]; [self.lowLevelView addSubview:luckyBgView]; [UIView animateWithDuration:0.5 animations:^{ luckyBgView.frame = CGRectMake(0, kNavigationHeight + 15, KScreenWidth, 45); } completion:^(BOOL finished) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [UIView animateWithDuration:0.5 animations:^{ luckyBgView.hidden = YES; } completion:^(BOOL finished) { [luckyBgView removeFromSuperview]; }]; }); }]; } - (NSAttributedString *)createBigPrizeAttribute:(XPGiftBigPrizeModel *)prizModel { NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] init]; [attributedString appendAttributedString: [self createTextAttribute:@"哇偶 ! " color:[ThemeColor messageViewTipColor] font:12]]; [attributedString appendAttributedString: [self createTextAttribute:prizModel.nick color:[ThemeColor messageNickColor] font:12]]; [attributedString appendAttributedString: [self createTextAttribute:@"通过" color:[ThemeColor messageViewTipColor] font:12]]; [attributedString appendAttributedString: [self createTextAttribute:prizModel.luckyBagName color:[ThemeColor messageNickColor] font:12]]; [attributedString appendAttributedString: [self createTextAttribute:@"开出了" color:[ThemeColor messageNickColor] font:12]]; [attributedString appendAttributedString: [self createTextAttribute:prizModel.giftName color:[ThemeColor messageNickColor] font:12]]; return attributedString; } /// 生成一个富文本 /// @param text 富文本的文字 /// @param color 文字的颜色 /// @param font 文字的大小 - (NSMutableAttributedString *)createTextAttribute:(NSString *)text color:(UIColor *)color font:(CGFloat)font { if (text == nil || text.length <= 0) { text = @""; } NSDictionary * dic = @{NSFontAttributeName:[UIFont systemFontOfSize:12], NSForegroundColorAttributeName:color}; NSMutableAttributedString *attribute = [[NSMutableAttributedString alloc] initWithString:text attributes:dic]; return attribute; } #pragma mark - 普通礼物 ///收到了礼物 - (void)receiveGift:(GiftReceiveInfoModel *)receiveInfo { ///CPU警告的时候 需要停止接收动画 RoomInfoModel * roomInfor = [self.delegate getRoomInfo]; if (receiveInfo.isLuckyBagGift) { //如果是福袋礼物的话 就不用看他的价值了 只要有SVGA的话 就播放 if (receiveInfo.displayGift.count > 0) { [receiveInfo.displayGift enumerateObjectsUsingBlock:^(GiftInfoModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { if (obj.hasVggPic && roomInfor.hasAnimationEffect) { GiftReceiveInfoModel * model = [[GiftReceiveInfoModel alloc] init]; model.vggUrl = obj.vggUrl; [self.giftEffectQueue addObject:model]; } }]; } } else { GiftInfoModel *giftInfo = receiveInfo.gift == nil ? receiveInfo.gift : receiveInfo.giftInfo; if (!giftInfo) { giftInfo = [[XPGiftStorage shareStorage] findGiftInfo: receiveInfo.giftId]; } ///重新赋值一下 用的时候不用在查找了 真的没有的话 那就没有那个礼物了 if (giftInfo) { receiveInfo.gift = giftInfo; } if (receiveInfo.targetUsers.count > 0 && receiveInfo.targetUids.count <= 0) { receiveInfo.targetUids = [receiveInfo.targetUsers valueForKeyPath:@"uid"]; } NSInteger giftTotal = 0; if (receiveInfo.targetUids.count > 0) { giftTotal = receiveInfo.giftNum * giftInfo.goldPrice * receiveInfo.targetUids.count; } else { giftTotal = receiveInfo.giftNum * giftInfo.goldPrice; } ///房间横幅 if (giftTotal >= 520) { ///房间横幅和 房间特效的关闭没有关系 [self.giftEffectQueue addObject:receiveInfo]; } else { ///SVGA动画 if (giftInfo.hasVggPic && roomInfor.hasAnimationEffect) { receiveInfo.vggUrl = giftInfo.vggUrl; [self.giftEffectQueue addObject:receiveInfo]; } } } if (self.giftEffectTimer == nil && self.giftEffectQueue.count > 0) { [self startHandleGiftEffectTimer]; } } ///开启普通礼物特效定时器 - (void)startHandleGiftEffectTimer { NSTimeInterval period = 6.0; //设置时间间隔 6秒是 1、单纯的svga 2.有横幅有svga 3、只有横幅 dispatch_queue_t queue = dispatch_get_global_queue(0, 0); dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), period * NSEC_PER_SEC, 0); //每秒执行 @kWeakify(self); dispatch_source_set_event_handler(_timer, ^{ @kStrongify(self) if (self.giftEffectQueue.count > 0) { dispatch_sync(dispatch_get_main_queue(), ^{ GiftReceiveInfoModel * receiveModel = [self.giftEffectQueue firstObject]; [self createGiftBannerViewAnimation:receiveModel]; [self.giftEffectQueue removeObject:receiveModel]; }); }else { dispatch_source_cancel(_timer); self.giftEffectTimer = nil; } }); dispatch_resume(_timer); self.giftEffectTimer = _timer; } - (void)createGiftBannerViewAnimation:(GiftReceiveInfoModel *)receiveInfo { GiftInfoModel * giftInfo = receiveInfo.gift; NSInteger giftTotal = 0; if (receiveInfo.targetUids.count > 0) { giftTotal = receiveInfo.giftNum * giftInfo.goldPrice * receiveInfo.targetUids.count; } else { giftTotal = receiveInfo.giftNum * giftInfo.goldPrice; } if (giftTotal >= 520) { __block XPRoomGiftBannerView *view = [self.bannerDequePool anyObject]; if (view == nil) { view = [[XPRoomGiftBannerView alloc] init]; [self.bannerVisiablePool addObject:view]; }else{ [self.bannerDequePool removeObject:view]; } [self removeGiftBannerView:view]; [view configGiftBanner:receiveInfo users:[self makeBannerUsers]]; view.frame = CGRectMake(KScreenWidth, 100, KScreenWidth, 153); [self.lowLevelView addSubview:view]; POPSpringAnimation *springAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPViewCenter]; springAnimation.springSpeed = 12; springAnimation.springBounciness = 10.f; springAnimation.fromValue = [NSValue valueWithCGPoint:view.center]; springAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(view.frame.size.width / 2, view.center.y)]; @kWeakify(self); [springAnimation setCompletionBlock:^(POPAnimation *anim, BOOL finished) { @kStrongify(self); if (finished) { [self removeAnimation:view]; } }]; [springAnimation setAnimationDidStartBlock:^(POPAnimation *anim) { @kStrongify(self); if (giftInfo.hasVggPic && [self.delegate getRoomInfo].hasAnimationEffect) { [self playGiftEffect:giftInfo.vggUrl]; } }]; [view pop_addAnimation:springAnimation forKey:@"spingOutAnimation"]; } else { if (receiveInfo.vggUrl.length > 0) {///如果有特效的话就播放特效 [self playGiftEffect:receiveInfo.vggUrl]; } } } //移除特效动画 - (void)removeAnimation:(XPRoomGiftBannerView *)view{ POPBasicAnimation *moveAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPViewCenter]; moveAnimation.fromValue = [NSValue valueWithCGPoint:view.center]; moveAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(view.center.x - KScreenWidth, view.center.y)]; moveAnimation.beginTime = CACurrentMediaTime() +5; moveAnimation.duration = 0.5; moveAnimation.repeatCount = 1; moveAnimation.removedOnCompletion = YES; [view pop_addAnimation:moveAnimation forKey:@"moveOutAnimation"]; } - (NSArray *)makeBannerUsers { NSArray * microArray = [self.delegate getMicroQueue].allValues; NSMutableArray * array = [NSMutableArray array]; [microArray enumerateObjectsUsingBlock:^(MicroQueueModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { XPGiftBannerUserInfoModel * bannserUser = [[XPGiftBannerUserInfoModel alloc] init]; bannserUser.position = obj.microState.position; bannserUser.uid = obj.userInfo.uid; [array addObject:bannserUser]; }]; return array; } ///播放礼物特效 SVGA的 - (void)playGiftEffect:(NSString *)vggUrl { self.giftEffectView.hidden = NO; if (self.giftEffectView.superview == nil) { [self.middleLevelView addSubview:self.giftEffectView]; [self.giftEffectView mas_makeConstraints:^(MASConstraintMaker *make) { make.center.mas_equalTo(self.lowLevelView); make.width.mas_equalTo(KScreenWidth); make.height.mas_equalTo(KScreenHeight); }]; } [self.parser parseWithURL:[NSURL URLWithString:vggUrl] completionBlock:^(SVGAVideoEntity * _Nullable videoItem) { if (videoItem != nil) { CGFloat width = videoItem.videoSize.width; CGFloat height = videoItem.videoSize.height; if (width > height) { self.giftEffectView.contentMode = UIViewContentModeScaleAspectFit; } else {//高大于宽 CGFloat resizeH = KScreenWidth * height / width;//按照屏幕的宽度去缩放,获得高度 if (resizeH > KScreenHeight) {//如果大于屏幕高度,填充 self.giftEffectView.contentMode = UIViewContentModeScaleAspectFill; } else {//小于屏幕高度, self.giftEffectView.contentMode = UIViewContentModeScaleAspectFit; } } self.giftEffectView.alpha = 1; self.giftEffectView.loops = 1; self.giftEffectView.clearsAfterStop = YES; self.giftEffectView.videoItem = videoItem; [self.giftEffectView startAnimation]; } } failureBlock:^(NSError * _Nullable error) { }]; } ///移除横幅的veiw 添加到复用池中 - (void)removeGiftBannerView:(XPRoomGiftBannerView *)view { [view removeFromSuperview]; [view resetData]; [self.bannerVisiablePool removeObject:view]; [self.bannerDequePool addObject:view]; ///svga的处理 [self.giftEffectView stopAnimation]; self.giftEffectView.hidden = YES; } #pragma mark - - (void)svgaPlayerDidFinishedAnimation:(SVGAPlayer *)player { if (player == self.giftEffectView) { self.giftEffectView.hidden = YES; } } #pragma mark - Getters And Setters - (SVGAImageView *)giftEffectView { if (!_giftEffectView) { _giftEffectView = [[SVGAImageView alloc]init]; _giftEffectView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5]; _giftEffectView.userInteractionEnabled = NO; _giftEffectView.delegate = self; _giftEffectView.hidden = YES; } return _giftEffectView; } - (SVGAParser *)parser { if (!_parser) { _parser = [[SVGAParser alloc]init]; } return _parser; } - (NSMutableArray *)giftEffectQueue { if (!_giftEffectQueue) { _giftEffectQueue = [NSMutableArray array]; } return _giftEffectQueue; } - (NSMutableArray *)bigPrizeGiftQueue { if (!_bigPrizeGiftQueue) { _bigPrizeGiftQueue = [NSMutableArray array]; } return _bigPrizeGiftQueue; } - (NSMutableArray *)giftBroadcastQueue { if (!_giftBroadcastQueue) { _giftBroadcastQueue = [NSMutableArray array]; } return _giftBroadcastQueue; } - (NSMutableSet *)bannerDequePool { if (!_bannerDequePool) { _bannerDequePool = [NSMutableSet set]; } return _bannerDequePool; } - (NSMutableSet *)bannerVisiablePool { if (!_bannerVisiablePool) { _bannerVisiablePool = [NSMutableSet set]; } return _bannerVisiablePool; } - (XPRoomAnimationHitView *)lowLevelView { if (!_lowLevelView) { _lowLevelView = [[XPRoomAnimationHitView alloc] init]; _lowLevelView.backgroundColor = [UIColor clearColor]; _lowLevelView.userInteractionEnabled = YES; } return _lowLevelView; } - (XPRoomAnimationHitView *)middleLevelView { if (!_middleLevelView) { _middleLevelView = [[XPRoomAnimationHitView alloc] init]; _middleLevelView.backgroundColor = [UIColor clearColor]; _middleLevelView.userInteractionEnabled = YES; } return _middleLevelView; } - (XPRoomAnimationHitView *)highLevleView { if (!_highLevleView) { _highLevleView = [[XPRoomAnimationHitView alloc] init]; _highLevleView.backgroundColor = [UIColor clearColor]; _highLevleView.userInteractionEnabled = YES; } return _highLevleView; } - (NSMutableArray *)giftQueue { if (!_giftQueue) { _giftQueue = [NSMutableArray array]; } return _giftQueue; } - (NSMutableSet *)giftReuseArray { if (!_giftReuseArray) { _giftReuseArray = [NSMutableSet set]; } return _giftReuseArray; } - (NSMutableSet *)giftVisibleArray { if (!_giftVisibleArray) { _giftVisibleArray = [NSMutableSet set]; } return _giftVisibleArray; } - (NSMutableArray *)candyTreegiftQueue { if (!_candyTreegiftQueue) { _candyTreegiftQueue = [NSMutableArray array]; } return _candyTreegiftQueue; } @end