fix(mobile): 简化火柴人位置计算逻辑

使用简单直接的比例计算:
- 起点 = 昵称区域右边 (70px)
- 终点 = 红旗左边 (containerWidth - 62px)
- 火柴人位置 = 起点 + (终点 - 起点) * 进度

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2025-12-24 01:48:08 -08:00
parent 71304dbb69
commit 3e59d56f92
1 changed files with 118 additions and 127 deletions

View File

@ -183,7 +183,12 @@ class _StickmanRaceWidgetState extends State<StickmanRaceWidget>
return SizedBox( return SizedBox(
height: raceTrackHeight, height: raceTrackHeight,
child: Stack( child: LayoutBuilder(
builder: (context, constraints) {
final containerWidth = constraints.maxWidth;
return Stack(
clipBehavior: Clip.none,
children: [ children: [
// 线 // 线
Positioned.fill( Positioned.fill(
@ -212,59 +217,47 @@ class _StickmanRaceWidgetState extends State<StickmanRaceWidget>
...sortedRankings.asMap().entries.map((entry) { ...sortedRankings.asMap().entries.map((entry) {
final index = entry.key; final index = entry.key;
final data = entry.value; final data = entry.value;
return _buildStickman(data, index, sortedRankings.length, raceTrackHeight); return _buildStickman(data, index, sortedRankings.length, raceTrackHeight, containerWidth);
}), }),
], ],
);
},
), ),
); );
} }
/// ///
Widget _buildStickman(StickmanRankingData data, int rank, int total, double raceTrackHeight) { Widget _buildStickman(StickmanRankingData data, int rank, int total, double raceTrackHeight, double containerWidth) {
// () // (0.0 - 1.0)
final horizontalProgress = data.progress; final progress = data.progress;
// () // ()
//
final usableHeight = raceTrackHeight - 40; final usableHeight = raceTrackHeight - 40;
final trackHeight = usableHeight / total; final trackHeight = usableHeight / total;
final verticalPosition = rank * trackHeight + 10; final verticalPosition = rank * trackHeight + 10;
//
// (70)
// (containerWidth - 8(right) - 24() - 30())
// = + ( - ) *
const double startX = 70.0;
final double endX = containerWidth - 62.0;
final double stickmanCenterX = startX + (endX - startX) * progress;
return AnimatedBuilder( return AnimatedBuilder(
animation: _bounceController, animation: _bounceController,
builder: (context, child) { builder: (context, child) {
//
final bounce = _bounceController.value * 3; final bounce = _bounceController.value * 3;
// 100% return Stack(
final screenWidth = MediaQuery.of(context).size.width; clipBehavior: Clip.none,
final containerWidth = screenWidth - 32 - 32; // padding + padding
final stickmanColumnWidth = 60.0; // +
final flagRightMargin = 8.0; // right值
final flagWidth = 24.0; //
final nicknameAreaWidth = 65.0; //
final gap = 4.0; //
// 100%
// = containerWidth - flagRightMargin - flagWidth
// = stickmanLeftPosition + stickmanColumnWidth
// 100% : stickmanLeftPosition = containerWidth - flagRightMargin - flagWidth - stickmanColumnWidth
final endPosition = containerWidth - flagRightMargin - flagWidth - stickmanColumnWidth;
final startPosition = nicknameAreaWidth + gap;
final availableWidth = endPosition - startPosition;
final stickmanLeftPosition = startPosition + availableWidth * horizontalProgress;
return Positioned(
left: 0,
top: verticalPosition - bounce,
child: SizedBox(
width: containerWidth,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// - // -
SizedBox( Positioned(
width: nicknameAreaWidth, left: 0,
top: verticalPosition + 15 - bounce,
child: SizedBox(
width: 65,
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
decoration: BoxDecoration( decoration: BoxDecoration(
@ -281,8 +274,7 @@ class _StickmanRaceWidgetState extends State<StickmanRaceWidget>
style: TextStyle( style: TextStyle(
fontSize: 9, fontSize: 9,
fontFamily: 'Inter', fontFamily: 'Inter',
fontWeight: fontWeight: data.isCurrentUser ? FontWeight.w600 : FontWeight.w400,
data.isCurrentUser ? FontWeight.w600 : FontWeight.w400,
color: data.isCurrentUser color: data.isCurrentUser
? const Color(0xFFD4AF37) ? const Color(0xFFD4AF37)
: const Color(0xFF5D4037), : const Color(0xFF5D4037),
@ -292,12 +284,12 @@ class _StickmanRaceWidgetState extends State<StickmanRaceWidget>
), ),
), ),
), ),
// +
SizedBox(
width: stickmanLeftPosition - nicknameAreaWidth,
), ),
// //
Column( Positioned(
left: stickmanCenterX - 30, // 60
top: verticalPosition - bounce,
child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
// //
@ -333,9 +325,8 @@ class _StickmanRaceWidgetState extends State<StickmanRaceWidget>
), ),
], ],
), ),
),
], ],
),
),
); );
}, },
); );