【问题标题】:Specify which index wheel of canves should stop on指定 canves 的索引轮应该停止
【发布时间】:2023-02-17 00:20:38
【问题描述】:

我正在创建一个命运轮游戏,我需要轮子停在 sectors 数组中的特定项目上。

只要用户点击“旋转”,后端就会返回一个索引,指示旋转器应该落在哪个项目上。

Jsfiddle

const sectors = [
  {color:"#b0f", label:"100"} ,
  {color:"#f0b", label:"5", },
  {color:"#bf0", label:"500"},
];

// Generate random float in range min-max:
const rand = (m, M) => Math.random() * (M - m) + m;

const tot = sectors.length;
const elSpin = document.querySelector("#spin");
const ctx = document.querySelector("#wheel").getContext`2d`;

   ctx.canvas.width = 600;
    ctx.canvas.height = 600;
    
    const dia = ctx.canvas.width;

    
const rad = dia / 2;
const PI = Math.PI;
const TAU = 2 * PI;
const arc = TAU / sectors.length;
const friction = 0.991;  // 0.995=soft, 0.99=mid, 0.98=hard
const angVelMin = 0.002; // Below that number will be treated as a stop
let angVelMax = 0; // Random ang.vel. to acceletare to 
let angVel = 0;    // Current angular velocity
let ang = 0;       // Angle rotation in radians
let isSpinning = false;
let isAccelerating = false;

//* Get index of current sector */
const getIndex = () => Math.floor(tot - ang / TAU * tot) % tot;

   const loadImage = (ctx, sector, ang) => {
      const img = new Image();
      img.onload = function () {
        ctx.drawImage(img, rad - 50, 10); // Or at whatever offset you like
      };
      img.src = sector.image;
    };
    
//* Draw sectors and prizes texts to canvas */
const drawSector = (sector, i) => {
  const ang = arc * i;
  ctx.save();
  // COLOR
  ctx.beginPath();
  ctx.fillStyle = sector.color;
  ctx.moveTo(rad, rad);
  ctx.arc(rad, rad, rad, ang, ang + arc);
  ctx.lineTo(rad, rad);
  ctx.fill();
  // TEXT
  ctx.translate(rad, rad);
  ctx.rotate(ang + arc / 2);
  ctx.textAlign = "right";
  ctx.fillStyle = "#fff";
  ctx.font = "bold 30px sans-serif";
  loadImage(ctx, sector, ang);
  ctx.fillText(sector.label, rad - 10, 10);
  //
  ctx.restore();
};

//* CSS rotate CANVAS Element */
const rotate = () => {
  const sector = sectors[getIndex()];
  ctx.canvas.style.transform = `rotate(${ang - PI / 2}rad)`;
  elSpin.textContent = !angVel ? "SPIN" : sector.label;
  elSpin.style.background = sector.color;
};

const frame = () => {

  if (!isSpinning) return;

  if (angVel >= angVelMax) isAccelerating = false;

  // Accelerate
  if (isAccelerating) {
    angVel ||= angVelMin; // Initial velocity kick
    angVel *= 1.06; // Accelerate
  }
  
  // Decelerate
  else {
    isAccelerating = false;
    angVel *= friction; // Decelerate by friction  

    // SPIN END:
    if (angVel < angVelMin) {
      isSpinning = false;
      angVel = 0; 
    }
  }

  ang += angVel; // Update angle
  ang %= TAU;    // Normalize angle
  rotate();      // CSS rotate!
};

const engine = () => {
  frame();
  requestAnimationFrame(engine)
};

elSpin.addEventListener("click", () => {
  if (isSpinning) return;
  isSpinning = true;
  isAccelerating = true;
  angVelMax = rand(0.25, 0.40);
});

// INIT!
sectors.forEach(drawSector);
rotate(); // Initial rotation
engine(); // Start engine!
#wheelOfFortune {
  display: inline-flex;
  position: relative;
  /* height: 720px;
  width: 720px; */
  overflow: hidden;
}

#wheel {
  display: block;
}

#spin {
  font: 1.5rem/0 sans-serif;
  user-select: none;
  cursor: pointer;
  display: flex;
  justify-content: center;
  align-items: center;
  position: absolute;
  top: 50%;
  left: 50%;
  width: 30%;
  height: 30%;
  margin: -15%;
  background: #fff;
  color: #fff;
  box-shadow: 0 0 0 8px currentColor, 0 0px 15px 5px rgba(0, 0, 0, 0.6);
  border-radius: 50%;
  transition: 0.8s;
}

#spin::after {
  content: '';
  position: absolute;
  top: -17px;
  border: 10px solid transparent;
  border-bottom-color: currentColor;
  border-top: none;
}
<div id="wheelOfFortune">
  <canvas id="wheel" width="300" height="300"></canvas>
  <div id="spin">SPIN asd asd asd as dasd as dasd asd asd as d</div>
</div>

我从哪里开始完成这项工作?旋转器需要逐渐减速,而不是立即停止。

我试图指定一个 winningAngle 变量,当它与当前角度匹配时,它会停止。

但是,它没有用,因为它立即停止了。

【问题讨论】:

  • 代码似乎有效
  • 嗨@MattEllen!它有效,但我需要指定它应该落在哪个项目上,因为后端将处理逻辑。

标签: javascript canvas


【解决方案1】:

Tl;博士:
使用 CSS transition 和 rotate 或 JS 对元素进行缓动动画动画接口.

代码(最初在这里回答:How to Draw a Wheel of Fortune)正在使用 requestAnimationFrame(处理加速、减速等)。这个不一定需要.

反而:

获取车轮电流absolute angle

// Generate random float in range min-max:
const rand = (m, M) => Math.random() * (M - m) + m;
// Fix negative modulo stackoverflow.com/a/71167019/383904
const mod = (n, m) => (n % m + m) % m;

let ang = 0;

// function spin (index) { .....
const angAbs = mod(ang, TAU)

获得服务器发送的“奖品指数" 并计算该索引的绝对角度

let angNew = arc * index

因为你不想恰好在扇区边缘结束旋转,所以减去相同的随机数0到arc学位:

angNew -= rand(0, arc)

使用辅助模函数修复负度数

angNew = mod(angNew, TAU)

现在我们有两个角度(angAbs 和angNew)得到角度差:

const angDiff = mod(angNew - angAbs, TAU)

现在您可以轻松地将 angDiff 添加到 ang 并制作动画,但让我们先添加一些随机转数(整圈):

const rev = rand(4, 6) // extra 4 or 5 full turns

// Finally update-increment ang
ang += angDiff + rev;

和动画!

const spinAnimation = elWheel.animate([{ rotate: `${ang}rad` }], {
  duration: rand(4000, 5000),
  easing: "cubic-bezier(0.2, 0, 0.1, 1)",
  fill: "forwards"
});

spinAnimation.addEventListener("finish", () => {
  // Wheel stopped
});

PS:上面代码中的TAU是(Math.PI * 2),arc是TAU / totalSectors。


提示:如果您想跟踪车轮旋转动画进度,您需要使用 requestAnimationFrame,因为 Animation 实例没有直接“动画滴答”事件回调。

// update() { function called at ~60fps using requestAnimationFrame 

const currentProgress = spinAnimation.effect.getComputedTiming().progress;
// value from 0 to 1  
// Get the angle:
const angDiff = ang - oldAng;
const angCurr = angDiff * currentProgress;
const angAbs = mod(oldAng + angCurr, TAU); 
console.log(angAbs);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-04-01
    • 2012-12-07
    • 2016-09-01
    • 1970-01-01
    • 2015-12-23
    • 1970-01-01
    • 2018-07-17
    • 1970-01-01
    相关资源
    最近更新 更多