如何在Class对象中使用requestAnimationFrame

时间:2018-02-15 21:18:24

标签: javascript oop animation scope requestanimationframe

我有一个类,它需要一些坐标和持续时间数据。我想用它来动画svg。更明确地说,我希望使用该数据在一个时间范围内更改svg属性。

我在课堂外使用step函数和requestAnimationFrame

function step(timestamp) {
  if (!start) start = timestamp
  var progress =  timestamp - start;
  var currentX = parseInt(document.querySelector('#start').getAttribute('cx'));
  var moveX =  distancePerFrame(circleMove.totalFrames(), circleMove.xLine);

  document.querySelector('#start').setAttribute('cx', currentX + moveX);
  if (progress < circleMove.duration) {
    window.requestAnimationFrame(step);
  }
}

var circleMove = new SingleLineAnimation(3000, startXY, endXY)

var start = null

function runProgram() {
  window.requestAnimationFrame(step);
}

我可以将其作为一种方法,将circleLine替换为this。这在第一次运行时工作正常,但是当它第二次调用this.step回调时,我们处于回调黑洞中并且对this的引用被破坏。一旦我们跳入回调self = this未定义(我不知道为什么),执行旧this也将无效。这是一种方法:

step(timestamp) {
  var self = this;

  if (!start) start = timestamp
  var progress =  timestamp - start;
  var currentX = parseInt(document.querySelector('#start').getAttribute('cx'));
  var moveX =  distancePerFrame(self.totalFrames(), self.xLine);

  document.querySelector('#start').setAttribute('cx', currentX + moveX);
  if (progress < self.duration) {
    window.requestAnimationFrame(self.step);
  }
}

关于如何在对象内保持“布线”的任何想法?

这里的代码或多或少适用于在类外定义的step函数。

class SingleLineAnimation { 
  constructor(duration, startXY, endXY) {
    this.duration = duration;
    this.xLine = [ startXY[0], endXY[0] ];
    this.yLine = [ startXY[1], endXY[1] ];
  }

  totalFrames(framerate = 60) { // Default to 60htz ie, 60 frames per second
    return Math.floor(this.duration * framerate / 1000);
  } 

  frame(progress) {
    return this.totalFrames() - Math.floor((this.duration - progress) / 17 );
  } 
}

这也将被插入到Class中,现在它只是一个辅助函数:

function distancePerFrame(totalFrames, startEndPoints) {
  return totalFrames > 0 ? Math.floor(Math.abs(startEndPoints[0] - startEndPoints[1]) / totalFrames) : 0;
}

然后点击按钮...

function runProgram() {
  window.requestAnimationFrame(step);
}

1 个答案:

答案 0 :(得分:2)

您需要将requestAnimationFrame回调函数绑定到上下文。这样做的规范方式是这样的:

window.requestAnimationFrame(this.step.bind(this))

但它并不理想,因为你反复调用.bind并反复创建一个新的函数引用,每帧一次。

如果您将本地范围的变量设置为this.step.bind(this),则可以通过该变量并避免不断重新绑定。

另一种选择是:

function animate() {

    var start = performance.now();
    el = document.querySelector('#start');

    // use var self = this if you need to refer to `this` inside `frame()`

    function frame(timestamp) {

        var progress =  timestamp - start;
        var currentX = parseInt(el.getAttribute('cx'));
        var moveX =  distancePerFrame(circleMove.totalFrames(), circleMove.xLine);
        el.setAttribute('cx', currentX + moveX);

        if (progress < circleMove.duration) {
            window.requestAnimationFrame(frame);
        }
    }

    window.requestAnimationFrame(frame);
}

即。你正在设置初始状态,然后在一个纯粹的本地范围的函数中进行动画,该函数由requestAnimationFrame伪递归地调用。

注意:如果您无意中调用另一个同时启动动画的函数,则任一版本的代码都会发生严重的交互。