Bulletproof - 20150905

Bulletproof 的代码更新,能实现运动了。

同时观察到了疑似的 canvas 清除失败。

更新了一点代码,能支持简单的运动了。这样的运动是直接更改 x 或者 y 属性实现的,而不是正规的用 MotionGroup 实现。以下是测试。

动画:

代码:

// FPS 计算代码来自 3D-ball 示例
var frameCount = 0;
var lastTime = Date.now();
var nextUpdate = Date.now() + 1000;
var f = function () {
    var now = Date.now();
    frameCount++;
    if (now >= nextUpdate) {
        fps = ((frameCount / (now - lastTime) * 1000 * 10) | 0) / 10;
        document.title = "FPS: " + fps.toString();
        lastTime = now;
        frameCount = 0;
        nextUpdate = Date.now() + 1000;
    }
    if (s) {
        s.x += 0.05;
    }
    requestAnimationFrame(f);
};
requestAnimationFrame(f);

绘制部分的代码取自B站的《干杯》神弹幕(AV号忘记了,记得在kanoha的评论里有,去找没找到)的第一部分——绿坝娘。

然而在测试中也发现了一个问题,CanvasDrawingContext2D.clearRect() 有时候会不灵光。下面是不灵光时的效果:

从上往下第一个 nw.exe 是浏览器进程,第二个是服务器进程。

任务管理器中还可以看到,内存占用也是在往上疯长,然后到了一个阈值(大约 270 MiB)就大起大落(应该是不断触发了 V8 的强制垃圾回收)。就在下面这一段:

public clear():void {
    // ...
    // 似乎无效
    //context.clearRect(0, 0, this._canvas.clientWidth, this._canvas.clientHeight);
    // TODO: HACK: works under nw.js v0.12
    // DANGER: will reset styles
    this._canvas.width = this._canvas.width;
    // ...
}

canvas.width = canvas.width 这种方法,100%有效,不过效率会降低EN)。目前我还不是很清楚是什么导致 clearRect() 失效。(在 clearRect() 之前,我重置了变换,所以清空的的确是 <canvas> 的显示区域。)

<canvas> 是由用户负责绘制的,不像 SVG 那样是浏览器负责绘制的,因此做同步是比较难的。如果我用的是 SVG,那么只需要一个:

svgElement.style.left = (++currentX).toString + "px";
svgElement.style.top = (++currentY).toString + "px";

剩下的就是浏览器进行样式计算、绘制、显示。对不起,用 <canvas>,请自己完成所有工作。

所以我做了一个指令队列记录绘制过程。理论上,需要重绘的时候就要恢复到上一次开始绘制前的状态,然后重复执行。我开始以为是这个队列出了什么问题导致多次绘制,但是调试后发现队列没有异常。(WebStorm 对 nw.js 的调试支持基本上算没有,连一个断点都设置不了,所以只好凭原始的黑箱方法来做了。)1539条指令,一直没变,内容也没变。蛋疼地去看自己对 redraw() 的实现逻辑,没问题;重绘队列的更新逻辑,没问题;变量访问加锁不需要,因为在浏览器窗口中的 JavaScript 是单线程运行的(不算 Web Worker 的话)。到底是哪里……

但是值得注意的是,重绘时至少存在一次 this.clear() 成功的操作。

public redraw():void {
    // ...
    this.clear();
    // # possible breakpoint
    switch (this.historyQueue[i].command) {
        // ...
    }
    // ...
}

如果在上面的 this.clear() 之后添加一个 return,那么画布上什么都没有了。考虑到第一次绘制来自神弹幕代码,这里如果清除失败应该还会留有一些图形才对。

另外,内存爆炸(在队列正常的情况下)也大概可以看出一点 <canvas> 的状态机的端倪,多次执行有偏移的相同指令会导致每次增加 20~30 MiB(这个量大约等于后来正常运行时浏览器的内存开销),因此可能有了隐性的状态保存操作。

Q: 透明度怎么办?

A: 简单,样式的 opacity 直接上就行了。

分享到 评论