當作交流吧,我變動的點:
/* ---------------- Spin logic ---------------- */
/**
* 調整小數部分往中間集中些(變成 0.1 ~ 0.9)
*/
function adjSector(val) {
return (val | 0) + 0.1 + (val - ((val | 0))) * 0.8;
}
function spin(){
if (isSpinning || wheelItems.length === 0) return;
isSpinning = true;
updateSpinAvailability();
const n = wheelItems.length;
const reduceMotion = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
/**
* 這邊用旋轉幾個扇形區域為單位來計算
* 這樣整數部分就是第幾個選項的索引
* 小數部分對應到扇形內多轉了多少
*/
const rndSector = (reduceMotion ? 2 + Math.random() : 6 + Math.random() * 5) * n;
const nextSector = adjSector(currentSector + rndSector);
/**
* 旋轉經過的角度與時間平方成正比
* 所以時間應該由旋轉經過的角度開根號獲得
* 這樣體感上才會是同一個轉盤
* 後面係數可以自行改變,數字越大代表摩擦力越小
*/
const duration = Math.sqrt((nextSector - currentSector) / n) * 1800 | 0;
const fromDeg = Math.round(currentSector * 360 / n);
const toDeg = Math.round(nextSector * 360 / n);
let startTime = null;
let lastSliceIdx = currentSector | 0;
const ani = wheelWrap.animate([
{ transform: `rotate(${fromDeg}deg)` },
{ transform: `rotate(${toDeg}deg)` }
], {
duration: duration,
// 這是一條拋物線,以符合等角加速度運動
easing: 'cubic-bezier(0.333, 0.667, 0.667, 1)',
fill: 'forwards'
});
function frame() {
// 取得目前的進度值 (0.0 ~ 1.0 之間),這直接就是 Sector 的進度了
let t = ani.effect.getComputedTiming().progress;
if (t !== null) {
// 透過進度,等比例算出當前所處的 sector 索引
const idx = currentSector + (nextSector - currentSector) * t | 0;
if (idx !== lastSliceIdx){
lastSliceIdx = idx;
playTick();
}
}
// 如果動畫還沒結束,就在下一影格繼續監控
if (ani.playState !== 'finished') {
requestAnimationFrame(frame);
} else {
currentSector = (nextSector | 0) % n + (nextSector - (nextSector | 0));
isSpinning = false;
updateSpinAvailability();
onSpinComplete(n - 1 - (currentSector | 0));
}
}
requestAnimationFrame(frame);
}