我们如何才能在旋转后的精确点停止这个 HTML5 Canvas 轮子? [英] How can we stop this HTML5 Canvas wheel at exact points after spin?

查看:18
本文介绍了我们如何才能在旋转后的精确点停止这个 HTML5 Canvas 轮子?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在下面的代码链接中,HTML5 画布旋转轮游戏.我想在用户定义的位置停止这个画布,就好像用户想要总是在 200 个文本或 100 个这样的文本处停止.

目前,它在随机点停止,我想控制停止的位置,就像我想随时在 100 或 200 或 0 处停止圆圈一样.

我们怎样才能做到这一点???谁能帮忙!!!!!!

还附上了 Codepen 链接.

HTML 文件

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

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;无功度 = 270;无功速度= 5;var slowDownRand = 0;var ctx = canvas.getContext('2d');var width = canvas.width;//尺寸var 中心 = 宽度/2;//中央var isStopped = false;无功锁=假;函数兰特(最小,最大){返回 Math.random() * (max - min) + min;}函数 deg2rad(deg){ 返回 deg * Math.PI/180;}函数 drawSlice(度,颜色){ctx.beginPath();ctx.fillStyle = 颜色;ctx.moveTo(中心,中心);ctx.arc(center, center, width/2, deg2rad(deg), deg2rad(deg+sliceDeg));控制台日志(中心,中心,宽度/2,deg2rad(deg),deg2rad(deg+sliceDeg))ctx.lineTo(center, center);ctx.fill();}函数 drawText(deg, text) {ctx.save();ctx.translate(中心,中心);ctx.rotate(deg2rad(deg));ctx.textAlign = "right";ctx.fillStyle = "#fff";ctx.font = '粗体 30 像素无衬线';ctx.fillText(text, 130, 10);ctx.restore();}函数 drawImg() {ctx.clearRect(0, 0, width, width);for(var i=0; i

旋转轮编码笔

解决方案

缓和曲线

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

如果将 x 的平方值绘制在 0 到 1 的范围内,则可以得到与下一个片段中相同的曲线,红线显示了 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 纯黑色";情节(获取数据());绘图(unitCurve(x => x * x),#F00");ctx.canvas.addEventListener("click",()=>plot(unitCurve(x => 1 - (1 - x) ** 2), "#FF0"), {once: true});函数获取数据(图表 = []){var pos = 0,速度 = 9,减速度 = 0.1;而(速度> 0){图表推(位置);位置 += 速度;速度 -= 减速;}返回图表;}函数 unitCurve(f,chart = []) {常量步 = 1/100;无功x = 0;而(x <= 1){chart.push(f(x));x += 步}返回图表;}功能图(图表,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();}

在动画中,这条曲线很容易出现.

我们可以创建使用ease函数的函数,花费时间并返回轮子的位置.我们可以提供一些额外的值来控制车轮停止所需的时间、起始位置和所有重要的停止位置.

function wheelPos(currentTime, startTime, endTime, startPos, endPos) {//首先将当前时间缩放为一个从 0 到 1 的值const x = (currentTime - startTime)/(endTime - startTime);//而不是平方,我们将使用平方根(这会翻转曲线)const xx = x ** (1/2);//将值转换为车轮位置返回 xx * (endPos - startPos) + startPos;}

演示

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

requestAnimationFrame(mainLoop);Math.TAU = Math.PI * 2;常量大小 = 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 纯黑色";var gTime;//全球时间const 颜色 = ["#F00","#F80","#FF0","#0C0","#08F","#00F","#F0F"];常量轮步 = 12;const minSpins = 3 * Math.TAU;//停止前的最小旋转次数常量自旋时间 = 6000;//毫秒const slowDownRate = 1/1.8;//该值越小,缓入越大.//必须 >0var startSpin = false;变量就绪时间 = 0;ctx.canvas.addEventListener("click",() => { startSpin = !wheel.spinning });stopAt.textContent = "点击滚轮旋转";const wheel = {//保存轮子相关变量img: createWheel(wheelSteps),结束时间:performance.now() - 2000,起始位置:0,结束位置:0,速度:0,位置:0,纺纱:假,设置 currentPos(val) {this.speed = (val - this.pos)/2;//停止时的摆动this.pos = val;},设置结束(位置){this.endPos = (Math.TAU - (pos/wheelSteps) * Math.TAU) + minSpins;this.endTime = gTime + spinTime;this.startTime = gTime;stopAt.textContent = "旋转到:"+(pos + 1);}};功能wheelPos(当前时间,开始时间,结束时间,开始位置,结束位置){const x = ((currentTime - startTime)/(endTime - startTime)) ** slowDownRate;返回 x * (endPos - startPos) + startPos;}功能主循环(时间){gTime = 时间;ctx.setTransform(1,0,0,1,0,0);ctx.clearRect(0, 0, size, size);if (startSpin && !wheel.spinning) {startSpin = 假;轮子.旋转=真;wheel.startPos = (wheel.pos % Math.TAU + Math.TAU) % Math.TAU;wheel.endAt = Math.random() * wheelSteps |0;} else if (gTime <= wheel.endTime) {//轮子正在旋转 get poswheel.currentPos = wheelPos(gTime,wheel.startTime,wheel.endTime,wheel.startPos,wheel.endPos);准备时间 = gTime + 1500;} else {//停止时摆动轮速 += (wheel.endPos - wheel.pos) * 0.0125;车轮速度 *= 0.95;轮子位置 += 轮子速度;如果(wheel.spinning && gTime > readyTime){轮子.旋转=假;stopAt.textContent = "点击滚轮旋转";}}//绘制轮子ctx.setTransform(1,0,0,1,size/2, size/2);ctx.rotate(wheel.pos);ctx.drawImage(wheel.img, -size/2 , - size/2);//绘制标记阴影ctx.setTransform(1,0,0,1,1,4);ctx.fillStyle = "#0004";ctx.beginPath();ctx.lineTo(size - 13, size/2);ctx.lineTo(size, size/2 - 7);ctx.lineTo(size, size/2 + 7);ctx.fill();//绘制标记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);}功能创建轮(步骤){const ctx = Object.assign(document.createElement("canvas"),{width: size, height: size}).getContext("2d");const s = 大小,s2 = s/2,r = s2 - 4;var colIdx = 0;for (让 a = 0; a < Math.TAU; a += Math.TAU/步数) {const aa = a - Math.PI/步骤;ctx.fillStyle = 颜色[colIdx++ % 颜色.长度];ctx.beginPath();ctx.moveTo(s2, s2);ctx.arc(s2, s2, r, aa, aa + Math.TAU/步数);ctx.fill();}ctx.fillStyle = "#FFF";ctx.beginPath();ctx.arc(s2, s2, 12, 0, Math.TAU);ctx.fill();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 (让 a = 0; a < Math.TAU; a += Math.TAU/步数) {const aa = a - Math.PI/步骤;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 black";ctx.textAlign = "居中";ctx.textBaseline = "中";const tr = r - 8;无功 idx = 1;for (让 a = 0; a < Math.TAU; a += Math.TAU/步数) {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);}返回 ctx.canvas;}

body { font-family: arial }

In the Below code link HTML5 canvas spin wheel game. I want to stop this canvas at a user-defined position as if the user wants to stop always at 200 texts or 100 texts like that.

Currently, it is stopping at random points I want to control where to stop as in if I want to stop circle at 100 or 200 or 0 whenever I want.

How can we achieve that??? Can anyone Help!!!!!

Attached Codepen link also.

Html file

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

JS file

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:
"+ 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

解决方案

Ease curves

If you where to plot the wheel position over time as it slows to a stop you would see a curve, a curve that looks like half a parabola.

You can get the very same curve if you plot the value of x squared in the range 0 to 1 as in the next snippet, the red line shows the plot of f(x) => x * x where 0 <= x <= 1

Unfortunately the plot is the wrong way round and needs to be mirrored in x and y. That is simple by changing the function to f(x) => 1 - (1 - x) ** 2 (Click the canvas to get the yellow line)

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();
}

In animation this curve is an ease in.

We can create function that uses the ease function, takes the time and returns the position of the wheel. We can provide some additional values that controls how long the wheel will take to stop, the starting position and the all important stop position.

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;
}

Demo

The demo puts it in action. Rather than using the square root the function in the demo defines the root as the constant slowDownRate = 2.6. The smaller this value the greater start speed and the slower the end speed. A value of 1 means it will move at a constant speed and then stop. The value must be > 0 and < 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 colors = ["#F00","#F80","#FF0","#0C0","#08F","#00F","#F0F"];
const wheelSteps = 12;
const minSpins = 3 * Math.TAU;  // min number of spins before stopping
const spinTime = 6000;          // in ms
const slowDownRate =  1 / 1.8;   // smaller this value the greater the ease in. 
                                 // Must be > 0 
var startSpin = false; 
var readyTime = 0;
ctx.canvas.addEventListener("click",() => { startSpin = !wheel.spinning });
stopAt.textContent = "Click wheel to spin";

const wheel = {  // hold wheel related variables
    img: createWheel(wheelSteps),
    endTime: performance.now() - 2000,
    startPos: 0,
    endPos: 0,
    speed: 0,
    pos: 0,
    spinning: false,
    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.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 (startSpin && !wheel.spinning) {
      startSpin = false;
      wheel.spinning = true;
      wheel.startPos = (wheel.pos % Math.TAU + Math.TAU) % Math.TAU;
      wheel.endAt =  Math.random() * wheelSteps | 0;
  } else if (gTime <= wheel.endTime) { // wheel is spinning get pos
      wheel.currentPos = wheelPos(gTime, wheel.startTime, wheel.endTime, wheel.startPos, wheel.endPos);
      readyTime = gTime + 1500;
  } else { // wobble at stop
      wheel.speed += (wheel.endPos - wheel.pos) * 0.0125;
      wheel.speed *= 0.95;
      wheel.pos += wheel.speed;
      if (wheel.spinning && gTime > readyTime) {
          wheel.spinning = false;
          stopAt.textContent = "Click wheel to spin";
      }
          
  }

  // 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 shadow
  ctx.setTransform(1,0,0,1,1,4);
  ctx.fillStyle = "#0004";
  ctx.beginPath();
  ctx.lineTo(size - 13, size / 2);
  ctx.lineTo(size, size / 2 - 7);
  ctx.lineTo(size, size / 2 + 7);
  ctx.fill();
  // 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;
    var colIdx = 0;
    for (let a = 0; a < Math.TAU; a += Math.TAU / steps) {
        const aa = a - Math.PI / steps;
        ctx.fillStyle = colors[colIdx++ % colors.length];
        ctx.beginPath();
        ctx.moveTo(s2, s2);
        ctx.arc(s2, s2, r, aa, aa + Math.TAU / steps);
        ctx.fill();
    }    
    ctx.fillStyle = "#FFF";
    ctx.beginPath();
    ctx.arc(s2, s2, 12, 0, Math.TAU);
    ctx.fill();
    
    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 black";
    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 }

这篇关于我们如何才能在旋转后的精确点停止这个 HTML5 Canvas 轮子?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆