280 lines
7.8 KiB
Dart
280 lines
7.8 KiB
Dart
import 'dart:async';
|
|
import 'dart:collection';
|
|
|
|
import 'package:flutter/cupertino.dart';
|
|
|
|
import 'package:yumi/ui_kit/widgets/room/anim/l_gift_animal_view.dart';
|
|
|
|
class GiftAnimationManager extends ChangeNotifier {
|
|
static const int _maxPendingAnimations = 24;
|
|
static const Duration _activeComboIdleDismissDelay = Duration(
|
|
milliseconds: 3200,
|
|
);
|
|
|
|
Queue<LGiftModel> pendingAnimationsQueue = Queue<LGiftModel>();
|
|
List<LGiftScrollingScreenAnimsBean> animationControllerList = [];
|
|
|
|
//每个控件正在播放的动画
|
|
Map<int, LGiftModel?> giftMap = {0: null, 1: null, 2: null, 3: null};
|
|
|
|
bool get _controllersReady =>
|
|
animationControllerList.length >= giftMap.length;
|
|
|
|
GiftAnimationSlotSnapshot? slotSnapshotAt(int index) {
|
|
final gift = giftMap[index];
|
|
if (gift == null || animationControllerList.length <= index) {
|
|
return null;
|
|
}
|
|
final bean = animationControllerList[index];
|
|
return GiftAnimationSlotSnapshot(
|
|
index: index,
|
|
bean: bean,
|
|
sendUserName: gift.sendUserName,
|
|
sendToUserName: gift.sendToUserName,
|
|
sendUserPic: gift.sendUserPic,
|
|
giftPic: gift.giftPic,
|
|
giftName: gift.giftName,
|
|
giftCount: gift.giftCount,
|
|
rewardAmountText: gift.rewardAmountText,
|
|
rewardAmount: gift.rewardAmount,
|
|
showLuckyRewardFrame: gift.showLuckyRewardFrame,
|
|
labelId: gift.labelId,
|
|
giftCountStepUnit: gift.giftCountStepUnit,
|
|
);
|
|
}
|
|
|
|
void enqueueGiftAnimation(LGiftModel giftModel) {
|
|
if (_mergeIntoActiveAnimation(giftModel)) {
|
|
return;
|
|
}
|
|
if (_mergeIntoPendingAnimation(giftModel)) {
|
|
return;
|
|
}
|
|
_trimPendingAnimations();
|
|
pendingAnimationsQueue.add(giftModel);
|
|
proceedToNextAnimation();
|
|
}
|
|
|
|
void _trimPendingAnimations() {
|
|
while (pendingAnimationsQueue.length >= _maxPendingAnimations) {
|
|
pendingAnimationsQueue.removeFirst();
|
|
}
|
|
}
|
|
|
|
bool _mergeIntoActiveAnimation(LGiftModel incoming) {
|
|
for (final entry in giftMap.entries) {
|
|
final current = entry.value;
|
|
if (current == null || current.labelId != incoming.labelId) {
|
|
continue;
|
|
}
|
|
_mergeGiftModel(target: current, incoming: incoming);
|
|
notifyListeners();
|
|
_refreshSlotAnimation(entry.key, restartEntry: false);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool _mergeIntoPendingAnimation(LGiftModel incoming) {
|
|
for (final pending in pendingAnimationsQueue) {
|
|
if (pending.labelId != incoming.labelId) {
|
|
continue;
|
|
}
|
|
_mergeGiftModel(target: pending, incoming: incoming);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void _mergeGiftModel({
|
|
required LGiftModel target,
|
|
required LGiftModel incoming,
|
|
}) {
|
|
target.giftCount = (target.giftCount) + (incoming.giftCount);
|
|
if (target.sendUserName.isEmpty) {
|
|
target.sendUserName = incoming.sendUserName;
|
|
}
|
|
if (target.sendToUserName.isEmpty) {
|
|
target.sendToUserName = incoming.sendToUserName;
|
|
}
|
|
if (target.sendUserPic.isEmpty) {
|
|
target.sendUserPic = incoming.sendUserPic;
|
|
}
|
|
if (target.giftPic.isEmpty) {
|
|
target.giftPic = incoming.giftPic;
|
|
}
|
|
if (target.giftName.isEmpty) {
|
|
target.giftName = incoming.giftName;
|
|
}
|
|
target.rewardAmount = target.rewardAmount + incoming.rewardAmount;
|
|
if (incoming.rewardAmountText.isNotEmpty) {
|
|
target.rewardAmountText = incoming.rewardAmountText;
|
|
}
|
|
target.showLuckyRewardFrame =
|
|
target.showLuckyRewardFrame || incoming.showLuckyRewardFrame;
|
|
if (incoming.giftCountStepUnit > 0) {
|
|
target.giftCountStepUnit = incoming.giftCountStepUnit;
|
|
}
|
|
}
|
|
|
|
///开始播放
|
|
proceedToNextAnimation() {
|
|
if (pendingAnimationsQueue.isEmpty || !_controllersReady) {
|
|
return;
|
|
}
|
|
var playGift = pendingAnimationsQueue.first;
|
|
for (var key in giftMap.keys) {
|
|
var value = giftMap[key];
|
|
if (value == null) {
|
|
giftMap[key] = playGift;
|
|
pendingAnimationsQueue.removeFirst();
|
|
notifyListeners();
|
|
_refreshSlotAnimation(key, restartEntry: true);
|
|
break;
|
|
} else {
|
|
if (value.labelId == playGift.labelId) {
|
|
_mergeGiftModel(target: value, incoming: playGift);
|
|
pendingAnimationsQueue.removeFirst();
|
|
notifyListeners();
|
|
_refreshSlotAnimation(key, restartEntry: false);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void attachAnimationControllers(List<LGiftScrollingScreenAnimsBean> anins) {
|
|
animationControllerList = anins;
|
|
proceedToNextAnimation();
|
|
}
|
|
|
|
void cleanupAnimationResources() {
|
|
pendingAnimationsQueue.clear();
|
|
giftMap[0] = null;
|
|
giftMap[1] = null;
|
|
giftMap[2] = null;
|
|
giftMap[3] = null;
|
|
for (var element in animationControllerList) {
|
|
element.dismissTimer?.cancel();
|
|
element.controller.dispose();
|
|
element.countPulseController.dispose();
|
|
}
|
|
animationControllerList.clear();
|
|
}
|
|
|
|
void markAnimationAsFinished(int index) {
|
|
_cancelSlotDismissTimer(index);
|
|
if (giftMap[index] == null) {
|
|
return;
|
|
}
|
|
giftMap[index] = null;
|
|
notifyListeners();
|
|
proceedToNextAnimation();
|
|
}
|
|
|
|
void _refreshSlotAnimation(int index, {required bool restartEntry}) {
|
|
if (animationControllerList.length <= index) {
|
|
return;
|
|
}
|
|
final bean = animationControllerList[index];
|
|
_restartSlotDismissTimer(index);
|
|
bean.countPulseController.forward(from: 0);
|
|
if (restartEntry) {
|
|
bean.controller.forward(from: 0);
|
|
}
|
|
}
|
|
|
|
void _restartSlotDismissTimer(int index) {
|
|
if (animationControllerList.length <= index) {
|
|
return;
|
|
}
|
|
final bean = animationControllerList[index];
|
|
bean.dismissTimer?.cancel();
|
|
bean.dismissTimer = Timer(_activeComboIdleDismissDelay, () {
|
|
if (giftMap[index] == null) {
|
|
return;
|
|
}
|
|
markAnimationAsFinished(index);
|
|
});
|
|
}
|
|
|
|
void _cancelSlotDismissTimer(int index) {
|
|
if (animationControllerList.length <= index) {
|
|
return;
|
|
}
|
|
animationControllerList[index].dismissTimer?.cancel();
|
|
animationControllerList[index].dismissTimer = null;
|
|
}
|
|
}
|
|
|
|
@immutable
|
|
class GiftAnimationSlotSnapshot {
|
|
const GiftAnimationSlotSnapshot({
|
|
required this.index,
|
|
required this.bean,
|
|
required this.sendUserName,
|
|
required this.sendToUserName,
|
|
required this.sendUserPic,
|
|
required this.giftPic,
|
|
required this.giftName,
|
|
required this.giftCount,
|
|
required this.rewardAmountText,
|
|
required this.rewardAmount,
|
|
required this.showLuckyRewardFrame,
|
|
required this.labelId,
|
|
required this.giftCountStepUnit,
|
|
});
|
|
|
|
final int index;
|
|
final LGiftScrollingScreenAnimsBean bean;
|
|
final String sendUserName;
|
|
final String sendToUserName;
|
|
final String sendUserPic;
|
|
final String giftPic;
|
|
final String giftName;
|
|
final num giftCount;
|
|
final String rewardAmountText;
|
|
final num rewardAmount;
|
|
final bool showLuckyRewardFrame;
|
|
final String labelId;
|
|
final num giftCountStepUnit;
|
|
|
|
@override
|
|
bool operator ==(Object other) {
|
|
if (identical(this, other)) {
|
|
return true;
|
|
}
|
|
return other is GiftAnimationSlotSnapshot &&
|
|
other.index == index &&
|
|
identical(other.bean, bean) &&
|
|
other.sendUserName == sendUserName &&
|
|
other.sendToUserName == sendToUserName &&
|
|
other.sendUserPic == sendUserPic &&
|
|
other.giftPic == giftPic &&
|
|
other.giftName == giftName &&
|
|
other.giftCount == giftCount &&
|
|
other.rewardAmountText == rewardAmountText &&
|
|
other.rewardAmount == rewardAmount &&
|
|
other.showLuckyRewardFrame == showLuckyRewardFrame &&
|
|
other.labelId == labelId &&
|
|
other.giftCountStepUnit == giftCountStepUnit;
|
|
}
|
|
|
|
@override
|
|
int get hashCode => Object.hash(
|
|
index,
|
|
bean,
|
|
sendUserName,
|
|
sendToUserName,
|
|
sendUserPic,
|
|
giftPic,
|
|
giftName,
|
|
giftCount,
|
|
rewardAmountText,
|
|
rewardAmount,
|
|
showLuckyRewardFrame,
|
|
labelId,
|
|
giftCountStepUnit,
|
|
);
|
|
}
|