JavaScriptとcanvasで3Dなポリゴンを回転させる。
四角くてシフォン色のポリゴンを回転させるデモです。(お腹ぺこぺこなんで、食べられそうな色にしました)
Firefox, Safari, IE用です。とあるブラウザだと謎のモノリスがぐりぐり回転します。
なぜかOperaではうまく動作させることができませんでした。canvasの指定方法に問題があるのかもしれません。
IEで動作させるためには、excanvas.js が必要になります。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>polygon test</title> <!--[if IE]><script type="text/javascript" src="excanvas.js"></script><![endif]--> </head> <body> <canvas id="canvas0" width="640" height="480"></canvas> <script type="text/javascript"> window.onload = function() { var polySize = { x: 200, y: 200 }; var poly = new polygon(); poly.initialize(document.getElementById("canvas0"), 50, polySize, 800); poly.draw(); } </script> <script type="text/javascript"> function polygon() { } polygon.prototype = { /** 初期化 * * @param element elm <canvas>要素を指定します。 * @param number fps 1秒間に表示するフレーム数の指定です。 * フレーム数を多くすると滑らかになりますが負荷も増えます。 * 25を指定すると約40msで1回描画します。 * @param pos pos ポリゴンの座標を { x, y } で指定します。 * @param number zoom ポリゴンの拡大倍率を数値で指定します。 */ initialize: function(elm, fps, pos, zoom) { this.ctx = elm.getContext("2d"); this.fps = fps; this.pos = pos; this.zoom = zoom; this.poly = [[], [], [], [], [], []]; // polygon data this.theta = 0.5; // vertical this.phi = 0.5; // horizontal // create polygon data var i = 0, v1, v2; for (; i < 5; ++i) { v1 = (!i) ? 0 : Math.SQRT2 * Math.cos((0.5 * i - 0.25) * Math.PI); v2 = (!i) ? 0 : Math.SQRT2 * Math.sin((0.5 * i - 0.25) * Math.PI); this.poly[0].push([ v1, v2, 1]); this.poly[1].push([ 1, v1, v2]); this.poly[2].push([ v2, 1, v1]); this.poly[3].push([-v1, -v2, -1]); this.poly[4].push([ -1, -v1, -v2]); this.poly[5].push([-v2, -1, -v1]); } }, draw: function() { var me = this; var phi = Math.PI / 100; // 0.03141592653589793 var theta = Math.PI / 80; window.setInterval( function() { me.theta += theta; me.phi += phi; me.__drawPolygon(me.theta, me.phi); }, 1000 / this.fps ); }, __drawPolygon: function(theta, phi) { var sinP = Math.sin(phi), cosP = Math.cos(phi); var sinT = Math.sin(theta), cosT = Math.cos(theta); // vector data var vX = [-sinP, cosP, 0]; var vY = [-cosT * cosP, -cosT * sinP, sinT]; var vZ = [-sinT * cosP, -sinT * sinP, -cosT]; var info = []; var x, y, z; var i, j; var surface; // 2D bitmap surface for (i = 0; i < this.poly.length; ++i) { surface = [0, -(vZ[0] * this.poly[i][0][0] + vZ[1] * this.poly[i][0][1] + vZ[2] * this.poly[i][0][2])]; for (j = 1; j < this.poly[i].length; ++j) { z = vZ[0] * this.poly[i][j][0] + vZ[1] * this.poly[i][j][1] + vZ[2] * this.poly[i][j][2]; surface.push([vX[0] * this.poly[i][j][0] + vX[1] * this.poly[i][j][1] + vX[2] * this.poly[i][j][2], vY[0] * this.poly[i][j][0] + vY[1] * this.poly[i][j][1] + vY[2] * this.poly[i][j][2], z]); surface[0] += z; } info.push(surface); } info.sort(function(a, b) { if (a[0] === b[0]) { return 0; } return a[0] < b[0] ? 1: -1; }); // canvas clear this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); var light; for (i = 0; i < info.length; ++i) { info[i].shift(); light = info[i].shift(); if (light >= 0) { for (j = 0; j < info[i].length; ++j) { x = this.zoom * info[i][j][0] / (10 + info[i][j][2]); y = this.zoom * info[i][j][1] / (10 + info[i][j][2]); if (!j) { this.ctx.beginPath(); this.ctx.moveTo(this.pos.x + x, this.pos.y + -y); this.ctx.fillStyle = this.__rgba(parseInt(0x3f * light + 360) * 0x0101, 1.0); } else { this.ctx.lineTo(this.pos.x + x, this.pos.y + -y); } } this.ctx.closePath(); this.ctx.fill(); } } }, __rgba: function(color, alpha) { var rv = [color & 0xff0000, color & 0xff00, color & 0xff, alpha]; return "rgba(" + rv.join(",") + ")"; } }; </script> </body> </html>
独自のメソッドの追加や独自コンテキストの追加も試みたんですが、以下のようにCanvasRenderingContext2Dを拡張したところ、Firefox以外で動かなくなっちゃったので無かったことになりました。
CanvasRenderingContext2D.prototype.rgba = function(color, alpha) { var rv = [color & 0xff0000, color & 0xff00, color & 0xff, alpha]; return "rgba(" + rv.join(",") + ")"; }