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が出ないですね。精進します。