みかづきブログ その3

3ヶ月つづけてみました。

Canvasではねさせたボールに残像をつける


Canvasでボールをはねさせる - みかづきブログ その3

以前つくったバウンドするボール に残像をつけてみましょう。


DEMO

実装的には毎フレームCanvasの状態を保持しておき、4フレーム前の状態を半透明にしてdrawImageしています。


JavaScript

// forked from kimmy's "Ball" http://jsdo.it/kimmy/66Yi
(function(win, doc, ns) {

    "use strict";

    function _reverseX(rad) {
        return (180 - rad) % 360;
    }

    function _reverseY(rad) {
        return (360 - rad) % 360;
    }

    function Ball(obj) {
        var _this = this;

        _init();

        function _init() {
            _this.x = obj.x;
            _this.y = obj.y;
            _this.r = obj.r;
            _this.v = obj.v || 0;
            _this.rad = obj.rad % 360 || 0;
        }
    }

    Ball.prototype = new ns.EventDispatcher();
    Ball.prototype.constructor = Ball;

    Ball.prototype.draw = function(ctx) {
        var _this = this;

        ctx.save();
            ctx.beginPath();
            ctx.arc(_this.x, _this.y, _this.r, 0, Math.PI * 2, false);
            ctx.fill();
        ctx.restore();
    };

    Ball.prototype.appendTo = function(canvas) {
        this.stage = this.stage || {};

        this.stage.top = 0;
        this.stage.bottom = canvas.height;
        this.stage.left = 0;
        this.stage.right = canvas.width;
    };

    Ball.prototype.set = function(obj) {
        var _this = this;

        _this.x   = obj.x || _this.x;
        _this.y   = obj.y || _this.y;
        _this.r   = obj.r || _this.r;
        _this.v   = obj.v || _this.v;
        _this.rad = obj.rad % 360 || _this.rad;
    };

    Ball.prototype.move = function(delta) {
        var _this = this,
            dx    = _this.v * Math.cos(_this.rad * (Math.PI / 180)),
            dy    = _this.v * Math.sin(_this.rad * (Math.PI / 180)),
            x, y;

        x = _this.x + dx * delta / 1000;
        y = _this.y + dy * delta / 1000;

        if (_this._isOutX(x)) {
            _this.set({rad: _reverseX(_this.rad)});
        } else {
            _this.x = x;
        }

        if (_this._isOutY(y)) {
            _this.set({rad: _reverseY(_this.rad)});
        } else {
            _this.y = y;
        }
    };

    Ball.prototype._isOutX = function(x) {
        var _this = this;

        if (x <= _this.r || x >= _this.stage.right - _this.r) {
            return true;
        } else {
            return false;
        }
    };

    Ball.prototype._isOutY = function(y) {
        var _this = this;

        if (y <= _this.r || y >= _this.stage.bottom - _this.r) {
            return true;
        } else {
            return false;
        }
    };

    ns.Ball = Ball;

})(this, document, App);

(function(win, doc, ns) {

    "use strict";

    var ticker = new ns.Ticker(60),
        ball = new ns.Ball({
            x   : 200,
            y   : 100,
            r   : 20,
            v   : 800,
            rad : 10
        }),
        canvas = doc.getElementById("canvas"),
        ctx = canvas.getContext("2d"),
        main = doc.createElement("canvas"),
        mCtx = main.getContext("2d"),
        sub = doc.createElement("canvas"),
        sCtx,
        shadowList = [],
        index = 0,
        fps = doc.getElementById("fps"),
        SAVE_FRAME_LENGTH = 4,
        ALPHA = 1 / SAVE_FRAME_LENGTH;

    setup();

    function setup() {
        canvas.width = win.innerWidth;
        canvas.height = win.innerHeight;

        ball.appendTo(canvas);

        win.addEventListener("resize", function() {
            ball.appendTo(canvas);
        }, false);

        ticker.addEventListener("tick", function(evt) {
            fps.innerHTML = evt.measuredFPS;
            update(evt.delta);
            draw();
        });
        
        ticker.start();
    }

    function update(delta) {
        ball.move(delta);
    }

    function draw() {
        sub = doc.createElement("canvas");
        sCtx = sub.getContext("2d");
            
        canvas.width = main.width = sub.width = win.innerWidth;
        canvas.height = main.height = sub.height = win.innerHeight;
        
        mCtx.fillStyle = "#83c287";
        ball.draw(mCtx);

        sCtx.globalAlpha = ALPHA;
        
        ctx.drawImage(main, 0, 0);
        sCtx.drawImage(main, 0, 0);
        
        shadowList[index] = sub;
        index = ++index % SAVE_FRAME_LENGTH;
        
        if (shadowList.length === SAVE_FRAME_LENGTH) {
            ctx.drawImage(shadowList[index], 0, 0);
        }
    }

})(this, document, App);

もうちょっと最適化しないとFPSが出ないですね。精進します。