一个关于canvas中随时间变化的颜色和setInterval出现问题的提问标题。

3

我想在画布(canvas)上制作一个动画,其中有一个实心矩形在从白色到红色再到绿色、蓝色,最后变成白色的过程中移动。我认为颜色正常,但速度太快,甚至看不到任何东西,因此我搜索了等待(wait)或延迟(delay)选项,并发现了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//
}

你能展示一下你在使用 setInterval 中尝试过的内容吗? - Frank Modica
@FrankModica 我尝试了很多 setInterval 的方法,但从我所了解的来看,对我最有意义的形式是这样的:for(i=0;i<255;i++){ctx.fillStyle = 'rgb('+r+', '+g+', '+b+')';ctx.fillRect(0, 0, 200, 200);var x = setInterval(r = r + 1, 15);}我还尝试将 r = r + 1 部分作为函数放置,并在 setInterval 中使用它,但也没有起作用。 - Koba
2个回答

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

非常感谢!但是有没有办法让它通过整个调色板而不是立即切换颜色?很抱歉再次提问,但我不知道最后一部分是如何工作的,所以... - Koba
哦,我误解了,请让我再试一次。 - Frank Modica
非常感谢!!!我之前遇到了很大的困难。我会学习你是如何做到的,以便理解。我不知道我可以使用setTimeout。非常感谢! - Koba
不用谢。我又进行了一次编辑,只是为了清理一下并删除一些硬编码的数字。 - Frank Modica
在“setInterval”方法中,每个间隔结束后,您会设置下一个函数调用。在这种方法中,我们只是预先设置了一堆函数调用,它们将在我们提前计算的时间触发。 - Frank Modica

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>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接