2

我基于其他人的 Chrome 实验创建了一个 jQuery 插件,该插件将一个画布元素插入到您的目标元素中,并在画布中绘制一个交互式星空。

每次调整窗口大小时,canvas 元素都会被移除然后恢复,以使其大小与其父元素匹配,并且所有内容都可以正常动画;它反应灵敏。

但是,每当它恢复时,动画的速度就会增加。为什么这样做?我认为使用 this.start() 方法将所有变量(包括速度)重置为默认值。

您可以在CodePen上查看代码(和演示);你也可以在Github上 fork 它,尽管我认为 Github 版本是我自己的几个提交。

(另外,这是我的第一个真正的 jQuery 插件,所以如果你发现任何问题,一定要告诉我。)

有什么线索吗?

4

2 回答 2

3

cancelAnimationFrame 单独使用不需要停止动画循环(事实证明)。

绝对确定您还需要使用条件检查- 一个通用示例:

var isPlaying; /// our condition

function loop() {

    /* funky stuff here */

    If (isPlaying === true)
        requestId = requestAnimationFrame(loop);
}

然后启动它:

functiom start() {
    isPlaying = true;
    loop();
}

现在,当您想要停止动画时,您需要执行以下操作:

function stop() {

    isPlaying = false;

    /// kill any request in progress
    if (requestId) cancelAnimationFrame(requestId);
}

如果你不这样做,你就有可能将调用堆叠到循环中——例如:

如果您调整大小并且 cAF 没有阻止 rAF 重新触发循环,则旧循环仍将在后台运行,您将在此基础上启动一个新循环。

这就是为什么您会看到速度增加的原因,因为旧循环和新循环都会在星星被绘制到屏幕之前增加位置。

在第三次重新调整大小时,又开始了另一个循环,最终整个事情将阻塞浏览器。

然而

我建议您使用以下方法,而不是利用循环的开始和停止:

  • 一次创建画布
  • 只启动一次循环
  • 在这种情况下,整个调整大小机制的重构可能是有益的(例如,将所需的初始化(元素的宽度和高度)与以后可以重用的第一次初始化分开)。
  • 无需为每个重新调整大小重新初始化星星,因为您将使用宽度和高度来检查它们的边界(画布将进行剪裁)。

调整大小时,您可以考虑使用条件标志来防止在调整大小时渲染

尽管通常情况下,由于 JavaScript 的单线程特性,实际上不需要在画布更改大小时阻止渲染的条件,在这种情况下,您需要对当前元素大小进行边界检查。Canvas 本身会为您处理剪裁。

话虽如此:不需要每次都重新创建画布元素。

这会产生不必要的开销。如果画布已经创建,则简单地在其属性上设置新的宽度和高度:

if (typeof canvas === 'undefined')
    canvas = /* only create if it doesn't exist */

canvas.width = width;
canvas.height = height;

PS:我用上面的一些蛮力实现“阻碍”了一个版本。它远非完整或高质量,但为了举例说明给你一些建议,它消除了一些痛苦。

请采用适合您的需要。

更新

要包含来自附加评论的更多信息:

当在画布元素上设置新大小时,其上下文将重置为默认值(fillStyle变为透明、strokeStyle黑色、重置变换等)。

这意味着在设置每个新尺寸后,必须重新设置所有非默认设置。

设置新的大小可能(并且通常会)清除画布的内容,以便所有像素变为透明黑色。

于 2013-08-28T18:44:09.980 回答
0

对于任何努力手动更新canvas元素尺寸的人:

调整canvas元素的大小会导致它丢弃在调整大小之前绘制到它的任何东西。

这个脚本的动画应该在调整大小后继续绘制到画布上,但唯一会更新的是fillRect背景;星星消失了。

更改元素尺寸后恢复星星的唯一方法canvas:额外调用context.strokeStyle. 我不知道为什么; 如果有人能对此事有所了解,我将不胜感激。

编辑:根据下面的评论,画布中的所有内容都会重置 - 包括笔触和填充样式(显然都默认为黑色)。所以当调整大小触发时,我不得不重新定义笔触和填充样式。

于 2013-09-01T11:24:48.483 回答