dvc890 commited on
Commit
9e4e7e8
·
verified ·
1 Parent(s): a023416

Update pages/GameLucky.tsx

Browse files
Files changed (1) hide show
  1. pages/GameLucky.tsx +57 -30
pages/GameLucky.tsx CHANGED
@@ -70,7 +70,7 @@ const LuckyWheel = ({ config, isSpinning, result, onSpin }: {
70
  const angle = (seg.weight / totalWeight) * 360;
71
  const start = currentAngle;
72
  currentAngle += angle;
73
- // Center angle is where the pointer should land for this segment
74
  const center = start + (angle / 2);
75
  return { ...seg, startAngle: start, endAngle: currentAngle, angle, centerAngle: center };
76
  });
@@ -80,23 +80,30 @@ const LuckyWheel = ({ config, isSpinning, result, onSpin }: {
80
  // Find target slice index
81
  const targetSlice = slices.find(s => s.name === result.prize) || slices[slices.length - 1];
82
 
83
- // Logic: Rotate the POINTER to align with the slice center
84
- // Base rounds + Target Angle
85
- // We need to ensure we always spin forward (clockwise)
 
86
  const minSpins = 5;
87
- const baseRotation = Math.floor(rotation / 360) * 360 + (minSpins * 360);
 
 
 
 
 
88
 
89
- // Current effective angle
90
  const currentMod = rotation % 360;
91
- const targetMod = targetSlice.centerAngle;
92
 
93
- let diff = targetMod - currentMod;
94
- if (diff <= 0) diff += 360; // Ensure positive delta for clockwise spin
 
95
 
96
- // Add a little randomness within the slice (-40% to +40% width) to simulate real physics
 
97
  const jitter = (Math.random() - 0.5) * (targetSlice.angle * 0.8);
98
 
99
- const finalRotation = rotation + (minSpins * 360) + diff + jitter;
100
 
101
  setRotation(finalRotation);
102
  }
@@ -110,10 +117,16 @@ const LuckyWheel = ({ config, isSpinning, result, onSpin }: {
110
  };
111
 
112
  return (
113
- <div className="flex flex-col items-center justify-center h-full w-full max-w-2xl mx-auto p-4">
114
  <div className="relative w-full aspect-square max-w-[500px] md:max-w-[600px]">
115
- {/* Wheel (Static Background) */}
116
- <div className="w-full h-full rounded-full border-8 border-yellow-400 shadow-2xl overflow-hidden relative bg-white">
 
 
 
 
 
 
117
  <svg viewBox="-1 -1 2 2" style={{ transform: 'rotate(-90deg)' }} className="w-full h-full">
118
  {slices.map((slice, i) => {
119
  // Calculate SVG path
@@ -148,29 +161,43 @@ const LuckyWheel = ({ config, isSpinning, result, onSpin }: {
148
  </svg>
149
  </div>
150
 
151
- {/* Spinning Pointer Container (Centered) */}
152
- <div
153
- className="absolute inset-0 pointer-events-none flex items-center justify-center z-20"
154
- style={{
155
- transform: `rotate(${rotation}deg)`,
156
- transition: isSpinning ? 'transform 4s cubic-bezier(0.2, 0.8, 0.2, 1)' : 'none'
157
- }}
158
- >
159
- {/* The Needle Graphic (Extends UP from center) */}
160
- <div className="relative w-0 h-0 -mt-[35%]">
161
- {/* Needle Body */}
162
- <div className="absolute bottom-0 left-1/2 -translate-x-1/2 w-4 md:w-6 h-32 md:h-48 bg-red-600 rounded-t-full shadow-lg border-2 border-white origin-bottom"></div>
163
- {/* Needle Base Decoration */}
164
- <div className="absolute bottom-[-10px] left-1/2 -translate-x-1/2 w-8 h-8 md:w-10 md:h-10 bg-red-700 rounded-full border-2 border-white shadow-md"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  </div>
166
  </div>
167
 
168
- {/* Center Button (Static Pivot) */}
169
  <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-30 pointer-events-auto">
170
  <button
171
  onClick={onSpin}
172
  disabled={isSpinning}
173
- className="w-16 h-16 md:w-20 md:h-20 bg-gradient-to-b from-yellow-300 to-yellow-500 rounded-full border-4 border-white shadow-[0_4px_10px_rgba(0,0,0,0.3)] flex items-center justify-center font-black text-red-600 text-lg md:text-xl hover:scale-105 active:scale-95 disabled:grayscale transition-all active:shadow-inner"
174
  >
175
  {isSpinning ? '...' : '抽奖'}
176
  </button>
 
70
  const angle = (seg.weight / totalWeight) * 360;
71
  const start = currentAngle;
72
  currentAngle += angle;
73
+ // Center angle relative to start of wheel (0 deg)
74
  const center = start + (angle / 2);
75
  return { ...seg, startAngle: start, endAngle: currentAngle, angle, centerAngle: center };
76
  });
 
80
  // Find target slice index
81
  const targetSlice = slices.find(s => s.name === result.prize) || slices[slices.length - 1];
82
 
83
+ // LOGIC: The POINTER is fixed at TOP (0 deg or 360 deg).
84
+ // We need to rotate the WHEEL so that the Target Slice center hits 0 deg.
85
+ // Target Rotation = (360 - TargetSliceCenter) + FullSpins.
86
+
87
  const minSpins = 5;
88
+ const spinAngle = minSpins * 360;
89
+
90
+ // Calculate where we need to land relative to 0
91
+ // Since SVG 0 starts at Top (after -90deg rotation),
92
+ // if we rotate by (360 - centerAngle), that slice will be at Top.
93
+ const targetLandingAngle = 360 - targetSlice.centerAngle;
94
 
95
+ // Current Rotation Modulo
96
  const currentMod = rotation % 360;
 
97
 
98
+ // Calculate forward distance to target
99
+ let distance = targetLandingAngle - currentMod;
100
+ if (distance < 0) distance += 360; // Ensure positive to spin clockwise
101
 
102
+ // Add slight randomness within the slice (-40% to +40% width)
103
+ // Note: If we add angle, we shift the slice CW, meaning pointer hits earlier (CCW shift relative to slice)
104
  const jitter = (Math.random() - 0.5) * (targetSlice.angle * 0.8);
105
 
106
+ const finalRotation = rotation + spinAngle + distance + jitter;
107
 
108
  setRotation(finalRotation);
109
  }
 
117
  };
118
 
119
  return (
120
+ <div className="flex flex-col items-center justify-center h-full w-full max-w-3xl mx-auto p-4">
121
  <div className="relative w-full aspect-square max-w-[500px] md:max-w-[600px]">
122
+ {/* 1. Wheel Container (Rotates) */}
123
+ <div
124
+ className="w-full h-full rounded-full border-8 border-yellow-400 shadow-2xl overflow-hidden relative bg-white"
125
+ style={{
126
+ transform: `rotate(${rotation}deg)`,
127
+ transition: isSpinning ? 'transform 4s cubic-bezier(0.15, 0.85, 0.35, 1)' : 'none'
128
+ }}
129
+ >
130
  <svg viewBox="-1 -1 2 2" style={{ transform: 'rotate(-90deg)' }} className="w-full h-full">
131
  {slices.map((slice, i) => {
132
  // Calculate SVG path
 
161
  </svg>
162
  </div>
163
 
164
+ {/* 2. Static Pointer (Fixed Overlay) */}
165
+ <div className="absolute inset-0 pointer-events-none z-20 flex justify-center">
166
+ {/* Container spanning from top to center (height 50%) */}
167
+ <div className="relative w-10 md:w-14 h-[50%]">
168
+ {/*
169
+ The Pointer Shape:
170
+ A long triangle stretching from the bottom (center of wheel) to the top (edge).
171
+ Using SVG to ensure perfect sharpness and responsiveness.
172
+ */}
173
+ <svg
174
+ viewBox="0 0 40 300"
175
+ preserveAspectRatio="none"
176
+ className="w-full h-full drop-shadow-xl"
177
+ style={{ filter: 'drop-shadow(0px 4px 4px rgba(0,0,0,0.4))' }}
178
+ >
179
+ {/* Gradient Def */}
180
+ <defs>
181
+ <linearGradient id="pointerGrad" x1="0%" y1="0%" x2="100%" y2="0%">
182
+ <stop offset="0%" stopColor="#dc2626" />
183
+ <stop offset="50%" stopColor="#ef4444" />
184
+ <stop offset="100%" stopColor="#b91c1c" />
185
+ </linearGradient>
186
+ </defs>
187
+ {/* Triangle Path: Top-Center (20,0) -> Bottom-Right (40,300) -> Bottom-Left (0,300) */}
188
+ <path d="M 20 0 L 40 300 L 0 300 Z" fill="url(#pointerGrad)" stroke="white" strokeWidth="2" />
189
+ {/* Inner Highlight line for 3D effect */}
190
+ <path d="M 20 0 L 20 300" stroke="rgba(255,255,255,0.3)" strokeWidth="1" />
191
+ </svg>
192
  </div>
193
  </div>
194
 
195
+ {/* 3. Center Button (Static Pivot) */}
196
  <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-30 pointer-events-auto">
197
  <button
198
  onClick={onSpin}
199
  disabled={isSpinning}
200
+ className="w-20 h-20 md:w-24 md:h-24 bg-gradient-to-b from-yellow-300 to-yellow-500 rounded-full border-4 border-white shadow-[0_4px_15px_rgba(0,0,0,0.4)] flex items-center justify-center font-black text-red-600 text-xl md:text-2xl hover:scale-105 active:scale-95 disabled:grayscale transition-all active:shadow-inner"
201
  >
202
  {isSpinning ? '...' : '抽奖'}
203
  </button>