fix(splash): 배경 아이콘 겹침 최소화
This commit is contained in:
@@ -19,6 +19,8 @@ class _SplashScreenState extends State<SplashScreen>
|
|||||||
late List<AnimationController> _foodControllers;
|
late List<AnimationController> _foodControllers;
|
||||||
late AnimationController _questionMarkController;
|
late AnimationController _questionMarkController;
|
||||||
late AnimationController _centerIconController;
|
late AnimationController _centerIconController;
|
||||||
|
List<Offset>? _iconPositions;
|
||||||
|
Size? _lastScreenSize;
|
||||||
|
|
||||||
final List<IconData> foodIcons = [
|
final List<IconData> foodIcons = [
|
||||||
Icons.rice_bowl,
|
Icons.rice_bowl,
|
||||||
@@ -62,6 +64,64 @@ class _SplashScreenState extends State<SplashScreen>
|
|||||||
)..repeat(reverse: true);
|
)..repeat(reverse: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Offset> _generateIconPositions(Size screenSize) {
|
||||||
|
final random = math.Random();
|
||||||
|
const iconSize = 40.0;
|
||||||
|
const maxScale = 1.5;
|
||||||
|
const margin = iconSize * maxScale;
|
||||||
|
const overlapThreshold = 0.3;
|
||||||
|
final effectiveSize = iconSize * maxScale;
|
||||||
|
final center = Offset(screenSize.width / 2, screenSize.height / 2);
|
||||||
|
final centerSafeRadius =
|
||||||
|
math.min(screenSize.width, screenSize.height) * 0.18;
|
||||||
|
|
||||||
|
Offset randomPosition() {
|
||||||
|
final x =
|
||||||
|
margin +
|
||||||
|
random.nextDouble() * (screenSize.width - margin * 2).clamp(1, 9999);
|
||||||
|
final y =
|
||||||
|
margin +
|
||||||
|
random.nextDouble() * (screenSize.height - margin * 2).clamp(1, 9999);
|
||||||
|
return Offset(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
final positions = <Offset>[];
|
||||||
|
var attempts = 0;
|
||||||
|
const maxAttempts = 500;
|
||||||
|
|
||||||
|
while (positions.length < foodIcons.length && attempts < maxAttempts) {
|
||||||
|
attempts++;
|
||||||
|
final candidate = randomPosition();
|
||||||
|
if ((candidate - center).distance < centerSafeRadius) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final hasHeavyOverlap = positions.any(
|
||||||
|
(p) => _isOverlapTooHigh(p, candidate, effectiveSize, overlapThreshold),
|
||||||
|
);
|
||||||
|
if (hasHeavyOverlap) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
positions.add(candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (positions.length < foodIcons.length) {
|
||||||
|
positions.add(randomPosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
return positions;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isOverlapTooHigh(Offset a, Offset b, double size, double maxRatio) {
|
||||||
|
final dx = (a.dx - b.dx).abs();
|
||||||
|
final dy = (a.dy - b.dy).abs();
|
||||||
|
final overlapX = math.max(0.0, size - dx);
|
||||||
|
final overlapY = math.max(0.0, size - dy);
|
||||||
|
final overlapArea = overlapX * overlapY;
|
||||||
|
final maxArea = size * size;
|
||||||
|
if (maxArea == 0) return false;
|
||||||
|
return overlapArea / maxArea > maxRatio;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
@@ -145,15 +205,22 @@ class _SplashScreenState extends State<SplashScreen>
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _buildFoodIcons() {
|
List<Widget> _buildFoodIcons() {
|
||||||
final random = math.Random();
|
final screenSize = MediaQuery.of(context).size;
|
||||||
|
final sameSize =
|
||||||
|
_lastScreenSize != null &&
|
||||||
|
(_lastScreenSize!.width - screenSize.width).abs() < 1 &&
|
||||||
|
(_lastScreenSize!.height - screenSize.height).abs() < 1;
|
||||||
|
if (_iconPositions == null || !sameSize) {
|
||||||
|
_iconPositions = _generateIconPositions(screenSize);
|
||||||
|
_lastScreenSize = screenSize;
|
||||||
|
}
|
||||||
|
|
||||||
return List.generate(foodIcons.length, (index) {
|
return List.generate(foodIcons.length, (index) {
|
||||||
final left = random.nextDouble() * 0.8 + 0.1;
|
final position = _iconPositions![index];
|
||||||
final top = random.nextDouble() * 0.7 + 0.1;
|
|
||||||
|
|
||||||
return Positioned(
|
return Positioned(
|
||||||
left: MediaQuery.of(context).size.width * left,
|
left: position.dx,
|
||||||
top: MediaQuery.of(context).size.height * top,
|
top: position.dy,
|
||||||
child: FadeTransition(
|
child: FadeTransition(
|
||||||
opacity: Tween(begin: 0.2, end: 0.8).animate(
|
opacity: Tween(begin: 0.2, end: 0.8).animate(
|
||||||
CurvedAnimation(
|
CurvedAnimation(
|
||||||
|
|||||||
Reference in New Issue
Block a user