在画布(js)中超时变化的颜色/使用setInterval遇到麻烦

时间:2018-11-19 18:00:43

标签: javascript animation canvas colors setinterval

我正在尝试在画布中制作动画,其中只有一个从白色到红色再到绿色再到蓝色再到白色的实心矩形。我认为颜色可以正常工作,但是它发生得太快,甚至看不到任何东西,因此我搜索了一个等待或延迟选项,并遇到了setInterval方法。我尽了一切努力使它正常运行,但是没有运气:

这是我编写的简洁代码。

HTML:

<!DOCTYPE html>
<html>
<head>
      <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
  <style type="text/css">
      canvas { border: 3px solid black; }
  </style>
</head>
<body>
  <div>
    <button onclick="draw()">Start the gradient</button><br><br>
    <canvas id="can" width="200" heigth="200"></canvas>
  </div>
</body>
</html>

Javascript:

function draw() {
  var can = document.getElementById('can');
  if (can.getContext) {
    var ctx = can.getContext('2d');
    var r = 0;
    var g = 0;
    var b = 0;
    var i;
    for(i=0;i<255;i++){
          ctx.fillStyle = 'rgb('+r+', '+g+', '+b+')';
          ctx.fillRect(0, 0, 200, 200);
          r = r + 1;
    }
    for(i=0;i<255;i++){
          ctx.fillStyle = 'rgb('+r+', '+g+', '+b+')';
          ctx.fillRect(0, 0, 200, 200);
          r = r - 1;
          g = g + 1;
    }
    for(i=0;i<255;i++){
          ctx.fillStyle = 'rgb('+r+', '+g+', '+b+')';
          ctx.fillRect(0, 0, 200, 200);
          g = g - 1;
          b = b + 1;
    }
    for(i=0;i<255;i++){
          ctx.fillStyle = 'rgb('+r+', '+g+', '+b+')';
          ctx.fillRect(0, 0, 200, 200);
          r = r + 1;
          g = g + 1;
    }
  }
//'can' is the canvas//
//Red->Green->Blue//
}

2 个答案:

答案 0 :(得分:1)

这是一种快速而肮脏的方法。您在开始时设置了一堆超时:

function draw() {
    var can = document.getElementById('can');
    if (can.getContext) {
        var ctx = can.getContext('2d');
        var r = 0;
        var g = 0;
        var b = 0;
        var i;
        var numColors = 255;
        var delay = 0; // ms
        var delta = 5; // ms

        for (i = 0; i < numColors; i++) {
            delay += delta;
            setTimeout(function () {
                ctx.fillStyle = 'rgb(' + r + ', ' + g + ', ' + b + ')';
                ctx.fillRect(0, 0, 200, 200);
                r = r + 1;
            }, delay);
        }

        for (i = 0; i < numColors; i++) {
            delay += delta;
            setTimeout(function () {
                ctx.fillStyle = 'rgb(' + r + ', ' + g + ', ' + b + ')';
                ctx.fillRect(0, 0, 200, 200);
                r = r - 1;
                g = g + 1;
            }, delay);
        }

        for (i = 0; i < numColors; i++) {
            delay += delta;
            setTimeout(function () {
                ctx.fillStyle = 'rgb(' + r + ', ' + g + ', ' + b + ')';
                ctx.fillRect(0, 0, 200, 200);
                g = g - 1;
                b = b + 1;
            }, delay);
        }

        for (i = 0; i < numColors; i++) {
            delay += delta;
            setTimeout(function () {
                ctx.fillStyle = 'rgb(' + r + ', ' + g + ', ' + b + ')';
                ctx.fillRect(0, 0, 200, 200);
                r = r + 1;
                g = g + 1;
            }, delay);
        }
    }
}

答案 1 :(得分:1)

您要处理的是动画工作原理的基础。

要使动画正常工作,您需要作为无序列表,

  • 一个场景,其中一些元素可以让观众识别,例如角色,或者在您的情况下为纯色。
  • 渲染器,它将很好地将场景渲染为某种图形形式。如果是低端翻书,可能是在笔记本角落的手绘图,如果是计算机图形学,可能是绘图功能。
  • 运动引擎,它将更新我们的运动元素在场景中的位置,并显示在特定时间间隔内打勾的下一个渲染图形。在活动簿中滑动手指,这是计算机图形学中基于时间的方法。 setInterval是一种这样的方法,但是最好直接从正确的工具开始,所以让我向您介绍requestAnimationFrame,这是事实上在浏览器中增强图形动画的最佳方法。基本上,它将在下一次屏幕刷新之前安排回调函数,从而始终以最佳节能方式同步动画。

所以我们来构建它;)

在您的情况下,我们场景的元素可能只是我们要制作动画的纯色的三个组成部分:

const color = [255, 255, 255]; // [red, green, blue]

渲染器将对其进行动画处理。 由于您的设置中有一些关键帧,因此要做的是计算中间帧。在这里,我将展示一个基本的帧增量引擎,但是最好使用基于时间的引擎,但是我认为现在可能太多了。

因此,我们将保留一个位置变量,此后称为currentFrame,以便我们可以知道动画在哪里,从哪个关键帧到另一个关键帧,并为我们的动画计算正确的值中间框架。在每次迭代(每帧)时,我们将递增此变量,直到达到将要制作动画的帧总数。在这一点上,我们将能够通过使用模%运算符使它重新开始。

const color = [255, 255, 255]; // [red, green, blue]
const keyFrames = [
  [255, 255, 255], // white
  [255, 0, 0], // to red
  [0, 255, 0], // to green
  [0, 0, 255] // to blue
];
const duration = 5; // in seconds
const totalFrames = 60 * duration; // 60 FPS
let currentFrame = 0;

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

function update() {
  // update currentFrame, relative to the whole animation duration
  currentFrame = (currentFrame + 1) % totalFrames;
  // make it relative to our key-frames
  const keyFrameIndex = currentFrame / (totalFrames / keyFrames.length);
  // Now we know our previous and next key-frames
  const prev = keyFrames[Math.floor(keyFrameIndex) % keyFrames.length];
  const next = keyFrames[Math.ceil(keyFrameIndex) % keyFrames.length];
  // We need to get the in-between ratio (that's the decimal of our floating index)
  const inBetweenRatio = keyFrameIndex - Math.floor(keyFrameIndex);
  //now we can update our color
  calculateInBetween(color, prev, next, inBetweenRatio);
}

function calculateInBetween(color, prev, next, ratio) {
  // simply update each channel of our current color
  color[0] = Math.floor((next[0] - prev[0]) * ratio) + prev[0];
  color[1] = Math.floor((next[1] - prev[1]) * ratio) + prev[1];
  color[2] = Math.floor((next[2] - prev[2]) * ratio) + prev[2];
}

function draw() {
  // clear all
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  // render
  ctx.fillStyle = "rgb(" + color + ")";
  ctx.fillRect(0, 0, 200, 200);
}

// our animation loop
function anim() {
  // update the scene
  update();
  // then render it
  draw();
  // start again at next screen refresh
  requestAnimationFrame(anim);
}


// let's begin
anim();
<canvas id="canvas" width="200" height="200"></canvas>