我想在每次点击 requestFrameAnimation 时开始一个新的动画

I want to start a new animation every time I click with requestFrameAnimation

我有多个问题。

  1. 每次我点击时动画会变快。 已解决 @Jorge Fuentes González
  2. 每次我点击 最后一个动画停止移动 已解决@Kaiido

我已经改变了我能想到的一切,但仍然是同样的问题。任何帮助,将不胜感激。谢谢!

    function drawFrame(frameX, frameY, canvasX, canvasY) {
      ctx.drawImage(img,
                    frameX * width, frameY * height,
                    width, height,
                    x_click, y_click,
                    scaledWidth, scaledHeight);
    }

    // Number of frames in animation
    var cycleLoop = [3, 2, 1, 0, 7, 6, 5];
    // Position of sprite in sheet
    var currentLoopIndex = 0;
    var frameCount = 0;

    function step() {

      frameCount++;
      if (frameCount < 30) {
        window.requestAnimationFrame(step);
        return;
      }
      frameCount = 0;
      // ctx.clearRect(0, 0, canvas.width, canvas.height);
      drawFrame(cycleLoop[currentLoopIndex++], 0, 0, 0);
      // Starts animation over
      if (currentLoopIndex >= cycleLoop.length) {
        // If you want to loop back in oposite direction after full animation
        cycleLoop.reverse();
        // Reseting position of which sprite to use
        currentLoopIndex = 0;
      }
      window.requestAnimationFrame(step);
    }

    canvas.addEventListener("mousedown", getPosition, false);
    function getPosition(event) {
       x_click = event.x;
       y_click = event.y;

       x_click -= canvas.offsetLeft * 10;
       y_click -= canvas.offsetTop * 10;
       step();
    }

============================== JS Fiddle:

https://jsfiddle.net/HYUTS/q4fazt6L/9/

=======================================

当您再次单击时,

window.requestAnimationFrame 仍然是 运行ning,并且当您单击时,您会为动画添加每帧的另一个刻度,使速度加倍,因为每次调用 step() 两次现在画框。再次点击时应该取消之前的动画帧,使用 window.cancelAnimationFrame()

https://developer.mozilla.org/en-US/docs/Web/API/Window/cancelAnimationFrame

像这样:

...
var animationID;

//in step() save the id in every call
function step() {
    ...
    animationID = window.requestAnimationFrame(step);
    ...
}

//In getPosition cancel the current animation
function.getPosition(event) {
    ...
    window.cancelAnimationFrame(animationId);
    ...
}

如果你想要多个动画运行ning,为每个动画创建一个对象并使函数step()成为它们的属性,然后在运行 window.requestAnimationFrame(this.step)里面步()。您还必须保存动画所需的每个变量,例如 currentLoopIndex 作为对象的一部分。

每次点击,都会调用step();,会调用window.requestAnimationFrame(step);,会调用step()下一个动画帧。我没有看到任何停止点,所以循环将永远被调用。

所以,当你第一次调用step()时,step()会一直调用下去,如果你再次点击,又会调用step()"line"第二次调用将再次调用 window.requestAnimationFrame(step);,所以现在您将有两个 "lines" 调用 step()。这就是动画速度更快的原因,因为在每个动画帧上 step() 将被调用两次,使计算加倍。

你要做的是检查动画是否已经 运行ning(带有标志)并且不要再次 运行 ,或者在开始之前 window.cancelAnimationFrame(ID) step() 再次循环。请注意,在每次单击时,您必须重新启动控制动画的变量,例如 frameCountcurrentLoopIndex

function drawFrame(frameX, frameY, canvasX, canvasY) {
  ctx.drawImage(img,
                frameX * width, frameY * height,
                width, height,
                x_click, y_click,
                scaledWidth, scaledHeight);
}

// Number of frames in animation
var cycleLoop = [3, 2, 1, 0, 7, 6, 5];
// Position of sprite in sheet
var currentLoopIndex = 0;
var frameCount = 0;

var animationid = null;
function step() {

  frameCount++;
  if (frameCount < 30) {
    animationid = window.requestAnimationFrame(step);
    return;
  }
  frameCount = 0;
  // ctx.clearRect(0, 0, canvas.width, canvas.height);
  drawFrame(cycleLoop[currentLoopIndex++], 0, 0, 0);
  // Starts animation over
  if (currentLoopIndex >= cycleLoop.length) {
    // If you want to loop back in oposite direction after full animation
    cycleLoop.reverse();
    // Reseting position of which sprite to use
    currentLoopIndex = 0;
  }
  animationid = window.requestAnimationFrame(step);
}

canvas.addEventListener("mousedown", getPosition, false);
function getPosition(event) {
   x_click = event.x;
   y_click = event.y;

   x_click -= canvas.offsetLeft * 10;
   y_click -= canvas.offsetTop * 10;
   frameCount = currentLoopIndex = 0;
   window.cancelAnimationFrame(animationid);
   step();
}

根据您的情况,第一步是为每个 animatables 创建不同的对象,这样它们就可以独立绘制和更新。

之后,您必须将逻辑分成几个部分。

一个基本设置是有一个在后台不断运行的主循环,它将调用所有更高级别的对象更新函数,然后是所有绘图函数。

正是在这些更高级别的方法中,您将检查是否应该实际丢弃它们。主循环不必处理它。

在下面的示例中,我为您的 animatable 对象创建了一个 class。这些对象现在将拥有自己的状态,并且能够根据自己的意愿独立于其他对象进行更新。

使用此设置,在场景中添加一个新对象只是将其推入数组的问题。

// Our Animatable class (ES5 style...)
// Each object as its own frameCount and its own loopIndex
function Animatable(x, y) {
  this.x = x;
  this.y = y;
  this.frameCount = 0;
  this.loopIndex = 0;
  this.cycleLoop = [3, 2, 1, 0, 7, 6, 5];
}
Animatable.prototype = {
  update: function() {
    this.frameCount++;
    if (this.frameCount < 30) {
      return;
    }
    this.frameCount = 0;
    this.loopIndex++
      if (this.loopIndex >= this.cycleLoop.length) {
        // If you want to loop back in oposite direction after full animation
        this.cycleLoop.reverse();
        // Reseting position of which sprite to use
        this.loopIndex = 0;
      }
  },
  draw: function() {
    // check the image is loaded
    if (!img.naturalWidth) return;
    var frameX = this.cycleLoop[this.loopIndex];
    ctx.drawImage(img,
      frameX * width, 0,
      width, height,
      this.x - scaledWidth/2, this.y - scaledHeight/2,
      scaledWidth, scaledHeight);
  }
};

// the main anim loop, independent
function startAnimLoop() {

  animloop();
  
  function animloop() {
    requestAnimationFrame(animloop);
    // updates
    animatables.forEach(update);
    // drawings
    ctx.clearRect(0,0,canvas.width, canvas.height);
    animatables.forEach(draw);
  }

  function update(animatable) {
    animatable.update();
  }

  function draw(animatable) {
    animatable.draw();
  }
}



// one image for all
var img = new Image();
img.src = 'https://imgur.com/u2hjhwq.png';
img.onload = startAnimLoop;

// here we will hold all our objects
var animatables = [new Animatable(50, 50)]; // start with a single one

var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');

// some constant from OP's fiddle
var scale = 1.5;
var width = 100; // Bigger numbers push left <-, smaller right ->
var height = 100;
var scaledWidth = scale * width;
var scaledHeight = scale * height;


canvas.onclick = function(evt) {
  var rect = canvas.getBoundingClientRect();
  var x = evt.clientX - rect.left;
  var y = evt.clientY - rect.top;
  // we simply create a new object ;-)
  animatables.push(new Animatable(x, y));
};
canvas{border:1px solid}
<canvas id="canvas" width="500" height="500"></canvas>