如何在旋转后将HTML5帆布轮停在精确的位置?

问题描述 投票:0回答:1

在下面的代码中链接HTML5画布自转轮游戏。我想将此画布停在用户定义的位置,就像用户希望始终停在200个文本或类似的100个文本一样。

当前,它在随机点处停止,我想控制停在哪里,就像我想在任何时候将圆停在100或200或0处一样。

我们如何实现这一目标???谁能帮忙!!!!!

也附有Codepen链接。

HTML文件

<div>
  <canvas class="spin-wheel" id="canvas" width="300" height="300"></canvas>
</div>

JS文件

var color    = ['#ca7','#7ac','#77c','#aac','#a7c','#ac7', "#caa"];
var label    = ['10', '200','50','100','5','500',"0"];
var slices = color.length;
var sliceDeg = 360/slices;
var deg = 270;
var speed = 5;
var slowDownRand = 0;
var ctx = canvas.getContext('2d');
var width = canvas.width; // size
var center = width/2;      // center
var isStopped = false;
var lock = false;

function rand(min, max) {
  return Math.random() * (max - min) + min;
}


function deg2rad(deg){ return deg * Math.PI/180; }

function drawSlice(deg, color){
  ctx.beginPath();
  ctx.fillStyle = color;
  ctx.moveTo(center, center);
  ctx.arc(center, center, width/2, deg2rad(deg), deg2rad(deg+sliceDeg));
  console.log(center, center, width/2, deg2rad(deg), deg2rad(deg+sliceDeg))
  ctx.lineTo(center, center);
  ctx.fill();
}

function drawText(deg, text) {
  ctx.save();
  ctx.translate(center, center);
  ctx.rotate(deg2rad(deg));
  ctx.textAlign = "right";
  ctx.fillStyle = "#fff";
  ctx.font = 'bold 30px sans-serif';
  ctx.fillText(text, 130, 10);
  ctx.restore();
}

function drawImg() {
  ctx.clearRect(0, 0, width, width);
  for(var i=0; i<slices; i++){
    drawSlice(deg, color[i]);
    drawText(deg+sliceDeg/2, label[i]);
    deg += sliceDeg;
  }
}

  // ctx.rotate(360);

function anim() {
   isStopped = true;
  deg += speed;
  deg %= 360;

  // Increment speed
  if(!isStopped && speed<3){
    speed = speed+1 * 0.1;
  }
  // Decrement Speed
  if(isStopped){
    if(!lock){
      lock = true;
      slowDownRand = rand(0.994, 0.998);
    } 
    speed = speed>0.2 ? speed*=slowDownRand : 0;
  }
  // Stopped!
  if(lock && !speed){
    var ai = Math.floor(((360 - deg - 90) % 360) / sliceDeg); // deg 2 Array Index
    console.log(slices)
    ai = (slices+ai)%slices; // Fix negative index
    return alert("You got:\n"+ label[ai] ); // Get Array Item from end Degree
    // ctx.arc(150,150,150,8.302780584487312,9.200378485512967);
    //   ctx.fill();
  }

  drawImg();
  window.requestAnimationFrame(anim);
}

function start() {
  anim()
}

drawImg();

Spin wheel codepen

javascript html animation html5-canvas 2d-games
1个回答
0
投票

缓和曲线

如果您在慢速到停止的位置绘制车轮位置随时间变化的位置,您会看到一条曲线,看起来像是一条抛物线的一半。

如果在下一个代码段中绘制0到1范围内的x平方值,则可以获得相同的曲线,红线显示f(x) => x * x的图,其中0 <= x <= 1

[不幸的是,绘图是错误的,需要在x和y中进行镜像。只需将功能更改为f(x) => 1 - (1 - x) ** 2(单击画布以获取黄线)即可。

const size = 200;
const ctx = Object.assign(document.createElement("canvas"),{width: size, height: size / 2}).getContext("2d");
document.body.appendChild(ctx.canvas);
ctx.canvas.style.border = "2px solid black";

plot(getData());
plot(unitCurve(x => x * x), "#F00");
ctx.canvas.addEventListener("click",()=>plot(unitCurve(x => 1 - (1 - x) ** 2), "#FF0"), {once: true});


function getData(chart = []) {
    var pos = 0, speed = 9, deceleration = 0.1;
    while(speed > 0) {
        chart.push(pos);
        pos += speed;
        speed -= deceleration;    
    }
    return chart;
}
function unitCurve(f,chart = []) {
    const step = 1 / 100;
    var x = 0;
    while(x <= 1) {
        chart.push(f(x));
        x += step
    }
    return chart;
}
function plot(chart, col = "#000") {
    const xScale = size / chart.length, yScale = size / 2 / Math.max(...chart);
    ctx.setTransform(xScale, 0, 0, yScale, 0, 0);
    ctx.strokeStyle = col;
    ctx.beginPath();
    chart.forEach((y,x) => ctx.lineTo(x,y));
    ctx.setTransform(1, 0, 0, 1, 0, 0);
    ctx.stroke();
}

在动画中,这条曲线很容易。

我们可以创建使用缓动功能的功能,花费时间并返回车轮的位置。我们可以提供一些附加值,这些值控制车轮停止需要多长时间,开始位置以及所有重要的停止位置。

function wheelPos(currentTime, startTime, endTime, startPos, endPos) {
    // first scale the current time to a value from 0 to 1
    const x = (currentTime - startTime) / (endTime - startTime);
    // rather than the square, we will use the square root (this flips the curve)
    const xx = x ** (1 / 2);
    // convert the value to a wheel position
    return xx * (endPos - startPos) + startPos;
}

演示

该演示使之付诸实践。演示中的函数没有使用平方根,而是将根定义为常数slowDownRate = 2.6。该值越小,开始速度越大,结束速度越慢。值为1表示它将以恒定速度移动然后停止。该值必须为> 0和<1

requestAnimationFrame(mainLoop);
Math.TAU = Math.PI * 2;
const size = 160;
const ctx = Object.assign(document.createElement("canvas"),{width: size, height: size}).getContext("2d");
document.body.appendChild(ctx.canvas);
const stopAt = document.createElement("div")
document.body.appendChild(stopAt);
ctx.canvas.style.border = "2px solid black";


var gTime;   // global time
const wheelSteps = 12;
const minSpins = 4 * Math.TAU;  // min number of spins before stopping
const spinTime = 6000;          // in ms
const slowDownRate = 1 / 2.6;   // smaller this value the greater the ease in. 
                                // Must be > 0 and < 1

const wheel = {  // hold wheel related variables
    img: createWheel(wheelSteps),
    endTime: performance.now() - 2000,
    set currentPos(val) {
      this.speed = (val - this.pos) / 2;  // for the wobble at stop
      this.pos = val;
    },
    set endAt(pos) {
       this.endPos = (Math.TAU - (pos / wheelSteps) * Math.TAU) + minSpins;
       this.startPos = 0;
       this.endTime = gTime + spinTime;
       this.startTime = gTime;
       stopAt.textContent = "Spin to: "+(pos + 1);
    }
 };
 function wheelPos(currentTime, startTime, endTime, startPos, endPos) {
    const x = ((currentTime - startTime) / (endTime - startTime)) ** slowDownRate;
    return x * (endPos - startPos) + startPos;
 } 

function mainLoop(time) {
  gTime = time;
  ctx.setTransform(1,0,0,1,0,0);
  ctx.clearRect(0, 0, size, size);

  if (time > wheel.endTime + 2000) { // spin again
      wheel.endAt =  Math.random() * wheelSteps | 0;
  } else if (time <= wheel.endTime) { // wheel is spinning get pos
      wheel.currentPos = wheelPos(time, wheel.startTime, wheel.endTime, wheel.startPos, wheel.endPos);
  } else { // wobble at stop
      wheel.speed += (wheel.endPos - wheel.pos) * 0.25;
      wheel.speed *= 0.9;
      wheel.pos += wheel.speed;
  }

  // draw wheel
  ctx.setTransform(1,0,0,1,size / 2, size / 2);
  ctx.rotate(wheel.pos);
  ctx.drawImage(wheel.img, -size / 2 , - size / 2);

  // draw marker
  ctx.setTransform(1,0,0,1,0,0);
  ctx.fillStyle = "#F00";
  ctx.beginPath();
  ctx.lineTo(size - 13, size / 2);
  ctx.lineTo(size, size / 2 - 7);
  ctx.lineTo(size, size / 2 + 7);
  ctx.fill();
  
  requestAnimationFrame(mainLoop);
}

   

function createWheel(steps) {
    const ctx = Object.assign(document.createElement("canvas"),{width: size, height: size}).getContext("2d");
    const s = size, s2 = s / 2, r = s2 - 4;
    ctx.fillStyle = "#EEE";
    ctx.beginPath();
    ctx.lineWidth = 2;
    ctx.arc(s2, s2, r, 0, Math.TAU);
    ctx.moveTo(s2 + 12, s2);
    ctx.arc(s2, s2, 12, 0, Math.TAU);
    for (let a = 0; a < Math.TAU; a += Math.TAU / steps) {
        const aa = a - Math.PI / steps;
        ctx.moveTo(Math.cos(aa) * 12 + s2, Math.sin(aa) * 12 + s2);
        ctx.lineTo(Math.cos(aa) * r + s2, Math.sin(aa) * r + s2);
    }
    ctx.fill("evenodd");
    ctx.stroke();
    ctx.fillStyle = "#000";
    ctx.font = "13px arial";
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    const tr = r - 8;
    var idx = 1;        
    for (let a = 0; a < Math.TAU; a += Math.TAU / steps) {
        const dx = Math.cos(a);
        const dy = Math.sin(a);
        ctx.setTransform(dy, -dx, dx, dy, dx * (tr - 4) + s2, dy * (tr - 4) + s2);
        ctx.fillText(""+ (idx ++), 0, 0);
    }
    return ctx.canvas;
}
body { font-family: arial }
© www.soinside.com 2019 - 2024. All rights reserved.