Created
February 1, 2010 14:56
-
-
Save kmdsbng/291727 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>HTML5 Canvas</title> | |
<script src="3d.js" type="text/javascript"></script> | |
<style type="text/css"> | |
html, body, canvas { overflow: hidden; width: 100%; height: 100%; margin: 0; padding: 0; border: 0 } | |
textarea { display: none } | |
</style> | |
</head> | |
<body> | |
<canvas></canvas> | |
<textarea class="10 Mona"> | |
∧_∧ | |
_( ´・ω・)_ | |
/\`'⊃(;;゚;;) \ ___ | |
/ ※ \_____|\__ヽ | |
\※ ※ | |_三三|_< はじまるよ! | |
\ / ※ ※ ※!、_ |||__|、 | |
`─────||ヽ───i§ | |
ヽi |`?? | |
</textarea> | |
<textarea class="10"> | |
JavaScript でも | |
3D くらい | |
できるさ! | |
</textarea> | |
<textarea class="10 Mona"> | |
amachang 先生の | |
次回作にご期待ください | |
∧∧ | |
( ・ω・) | |
_| ⊃/(___ | |
/ └-(____/ | |
 ̄ ̄ ̄ ̄ ̄ ̄ ̄ | |
</textarea> | |
<textarea class="10"> | |
- 完 - | |
</textarea> | |
<textarea class="10"> | |
long one line by alphabet. Is this showed valid? if not showed, bug. | |
</textarea> | |
<textarea class="10"> | |
日本語による長い行。表示できてる?表示できてなかったらバグです。どうかな? | |
</textarea> | |
<textarea class="10"> | |
ごめんなさい | |
ごめんなさい>< | |
</textarea> | |
<textarea class="15"> | |
DOM パフォーマンス | |
チューニング | |
</textarea> | |
<textarea class="10"> | |
JavaScript の | |
誤解 | |
</textarea> | |
<textarea class="10"> | |
JavaScript は | |
遅い | |
</textarea> | |
<textarea class="12"> | |
JavaScript は | |
そこそこ速い | |
</textarea> | |
<textarea class="10"> | |
論より証拠。 | |
ベンチマーク | |
</textarea> | |
<textarea class="10"> | |
マージソート | |
100 numbers | |
× 3000 times | |
</textarea> | |
<textarea class="10"> | |
JavaScript ActionScript code: | |
http://svn.coderepos.org/share/docs/amachang/ | |
Ruby, PHP, Python, Perl code: | |
http://izumi.plan99.net/blog/index.php/ | |
2008/01/17/ruby-vs-php-performance/ | |
</textarea> | |
<textarea class="10"> | |
PHP 5.2.6 (cli) (built: Jul 15 2008 12:18:21) [9.595s] | |
Ruby 1.8.6 (2007-09-24 patchlevel 111) [5.239s] | |
Perl v5.8.8 built for darwin-2level [5.000s] | |
JavaScript (V8 version 0.2.5) [4.931s] | |
Python 2.5.2 (r252:60911, Sep 3 2008, 10:08:55) [3.975s] | |
ActionScript 3.0 | |
(Flex SDK 3.2 & Flash Player 10.0.12.36) [1.329s] | |
</textarea> | |
<textarea class="10"> | |
ASはえーw | |
</textarea> | |
<textarea class="10"> | |
JavaScript も | |
悪くないでしょ? | |
</textarea> | |
<textarea class="10"> | |
JavaScript は | |
そこそこ速い | |
</textarea> | |
<textarea class="10"> | |
では、何故 | |
遅くなって | |
しまうのか | |
</textarea> | |
<textarea class="10"> | |
よく言われるのが | |
「DOM が遅い」 | |
</textarea> | |
<textarea class="10"> | |
よく言われるのが | |
「ドットを減らせばいい」 | |
</textarea> | |
<textarea class="10"> | |
でも、 | |
それだけじゃない | |
</textarea> | |
<textarea class="10"> | |
1. 再描画のための計算が行われる | |
タイミングを知ろう | |
2. HTML の属性変更の | |
影響範囲を考えよう | |
3. スタイル変更の | |
影響範囲を考えよう | |
</textarea> | |
<textarea class="10"> | |
1. 再描画のための | |
計算が行われる | |
タイミングを知ろう | |
</textarea> | |
<textarea class="10"> | |
基本的には、 | |
一つのイベントが | |
終わったあと | |
</textarea> | |
<textarea class="10"> | |
elm.onclick = function() { | |
elm.style.fontSize = '10px'; | |
elm.style.width = '2em'; | |
// JavaScript 実行後 | |
// ここで計算が走る | |
} | |
</textarea> | |
<textarea class="10"> | |
教訓 1 | |
JavaScript 実行後の | |
再描画の計算は | |
ベンチマークには出ない | |
</textarea> | |
<textarea class="10"> | |
elm.onclick = function() { | |
elm.style.fontSize = '10px'; | |
var w = elm.offsetWidth; // ここで計算が走る | |
elm.style.width = '2em'; | |
// さらに、ここで計算が走る | |
} | |
</textarea> | |
<textarea class="10"> | |
elm.onclick = function() { | |
elm.style.fontSize = '10px'; | |
elm.style.width = '2em'; | |
var w = elm.offsetWidth; // ここで計算が走る | |
// 実行後は、計算が走らない | |
} | |
</textarea> | |
<textarea class="10"> | |
教訓 2 | |
値の取得、変更は | |
それぞれ | |
まとめて行う | |
</textarea> | |
<textarea class="10"> | |
2. HTML 属性変更 | |
の影響範囲を | |
考えよう | |
</textarea> | |
<textarea class="10"> | |
elm.onclick = function() { | |
elm.className = 'hoge'; | |
// 計算が走る要素は | |
// いくつあるか? | |
} | |
</textarea> | |
<textarea class="10"> | |
<div> | |
<div>...</div> | |
<div>...</div> | |
<div class="hoge"> - この影響範囲は? | |
<div>...</div> | |
<div>...</div> | |
</div> | |
<div>...</div> | |
<div>...</div> | |
</div> | |
</textarea> | |
<textarea class="10"> | |
<div> | |
<div>...</div> | |
<div>...</div> | |
<div class="hoge"> * | |
<div>...</div> * ┬ 子孫セレクタの影響範囲 | |
<div>...</div> * ┘ | |
</div> | |
<div>...</div> * ┬ 兄弟セレクタの影響範囲 | |
<div>...</div> * ┘ | |
</div> | |
</textarea> | |
<textarea class="10"> | |
<div> | |
<div>...</div> | |
<div>...</div> | |
<div> ─ ここに要素を追加 | |
<div class="hoge"> * | |
<div>...</div> * | |
<div>...</div> * | |
</div> | |
</div> ─ ここで閉じる | |
<div>...</div> | |
<div>...</div> | |
</div> | |
</textarea> | |
<textarea class="10"> | |
教訓 3 | |
HTML の属性を | |
変更する要素は | |
きちんと隔離しよう | |
</textarea> | |
<textarea class="10"> | |
3. スタイル変更の | |
影響範囲を | |
考えよう | |
</textarea> | |
<textarea class="10"> | |
width プロパティ | |
- 子要素の width | |
- 子要素の margin | |
- 子要素の padding | |
- 子要素の border | |
</textarea> | |
<textarea class="10"> | |
height プロパティ | |
- 親要素の height | |
- 子要素の height | |
</textarea> | |
<textarea class="10"> | |
font-size プロパティ | |
- 自要素の em 指定の値 | |
- 子要素の font-size の値 | |
</textarea> | |
<textarea class="10"> | |
などなど | |
</textarea> | |
<textarea class="10"> | |
様々な | |
変更が | |
連鎖する | |
</textarea> | |
<textarea class="10"> | |
教訓 4 | |
すべての | |
包含ボックスと | |
inherit 値と | |
相対値 (%, em, auto) を | |
意識する | |
</textarea> | |
<textarea class="10"> | |
CSS の勉強 | |
超重要? | |
</textarea> | |
<textarea class="10"> | |
まとめ | |
</textarea> | |
<textarea class="10"> | |
Flash は | |
軽くて | |
いいですね>< | |
</textarea> | |
<textarea class="10"> | |
でも JavaScript も | |
頑張れば | |
Flash 並みに | |
出来るよ! | |
</textarea> | |
<textarea class="10"> | |
最後に | |
</textarea> | |
<textarea class="10"> | |
I ? Web | |
Standard! | |
</textarea> | |
<script type="text/javascript"> | |
function Matrix3D(data) { | |
this.data = data || [ [1, 0, 0, 0], | |
[0, 1, 0, 0], | |
[0, 0, 1, 0], | |
[0, 0, 0, 1] ]; | |
} | |
Matrix3D.prototype = { | |
multiplyMatrix: function(a, b, n, m) { | |
var r = []; | |
for (var i = 0; i < n; i ++) { | |
r[i] = []; | |
for (var j = 0; j < m; j ++) { | |
r[i][j] = 0; | |
for (var k = 0; k < n; k ++) { | |
r[i][j] += a[i][k] * b[k][j]; | |
} | |
} | |
} | |
return r; | |
}, | |
clone: function() { | |
var data = this.data; | |
var result = []; | |
for (var i = 0; i < data.length; i ++) { | |
var d = data[i]; | |
var r = result[i] = []; | |
for (var j = 0; j < d.length; j ++) { | |
r[j] = d[j]; | |
} | |
} | |
return new Matrix3D(result); | |
}, | |
transform: function(p) { | |
var data = this.multiplyMatrix(this.data, p.data, 3, 1); | |
var result = []; | |
for (var i = 0; i < data.length; i ++) { | |
result[i] = [data[i][0] + this.data[i][3]]; | |
} | |
return new Point3D(result); | |
}, | |
rotateX: function(rad) { | |
return this.concat(new Matrix3D([ [1, 0, 0, 0], | |
[0, Math.cos(rad), -Math.sin(rad), 0], | |
[0, Math.sin(rad), Math.cos(rad), 0], | |
[0, 0, 0, 1] ])); | |
}, | |
rotateY: function(rad) { | |
return this.concat(new Matrix3D([ [ Math.cos(rad), 0, Math.sin(rad), 0], | |
[ 0, 1, 0, 0], | |
[-Math.sin(rad), 0, Math.cos(rad), 0], | |
[ 0, 0, 0, 1] ])); | |
}, | |
rotateZ: function(rad) { | |
return this.concat(new Matrix3D([ [Math.cos(rad), -Math.sin(rad), 0, 0], | |
[Math.sin(rad), Math.cos(rad), 0, 0], | |
[0, 0, 1, 0], | |
[0, 0, 0, 1] ])); | |
}, | |
scale: function(x, y, z) { | |
return this.concat(new Matrix3D([ [x, 0, 0, 0], | |
[0, y, 0, 0], | |
[0, 0, z, 0] | |
[0, 0, 0, 1] ])); | |
}, | |
translate: function(x, y, z) { | |
var m = this.clone(); | |
m.data[0][3] += x; | |
m.data[1][3] += y; | |
m.data[2][3] += z; | |
return m; | |
}, | |
concat: function(m) { | |
return new Matrix3D(this.multiplyMatrix(m.data, this.data, 4, 4)); | |
}, | |
toString: function() { | |
return this.data.map(function(d) { | |
return d.join(', '); | |
}).join('\n'); | |
} | |
}; | |
function Point3D(data) { | |
this.data = data || [[0], [0], [0]]; | |
} | |
Point3D.prototype = { | |
get x() { | |
return this.data[0][0]; | |
}, | |
get y() { | |
return this.data[1][0]; | |
}, | |
get z() { | |
return this.data[2][0]; | |
}, | |
getProjectionPoint: function(dist) { | |
var prs = dist / (this.z + dist); | |
return new Point3D([[this.x * prs], [this.y * prs], [0]]); | |
}, | |
dot: function(v) { | |
return x * v.x + y * v.y + z * v.z; | |
} | |
}; | |
function Scene3D(elm, backgroundStyle, dist) { | |
this.shapes = []; | |
this.elm = elm; | |
var ctx = elm.getContext('2d'); | |
var width = elm.width; | |
var height = elm.height | |
ctx.translate(width / 2, height / 2); | |
this.ctx = new Context3D(ctx, dist || 3000, width, height); | |
this.backgroundStyle = backgroundStyle || 'black'; | |
} | |
Scene3D.prototype = { | |
set viewDistance(dist) { | |
this.ctx.viewDistance = dist; | |
this.redraw(); | |
return dist; | |
}, | |
get viewDistance(dist) { | |
return this.ctx.viewDistance; | |
}, | |
redraw: function() { | |
var shapes = this.shapes; | |
for (var i = 0; i < shapes.length; i ++) { | |
shapes[i].redraw(); | |
} | |
return this; | |
}, | |
append: function(shape) { | |
this.shapes.push(shape); | |
return this; | |
}, | |
remove: function(shape) { | |
var shapes = this.shapes; | |
for (var i = 0; i < shapes.length; i ++) { | |
if (shapes[i] === shape) { | |
shapes.splice(i, 1); | |
} | |
} | |
return this; | |
}, | |
render: function() { | |
this.shapes.sort(function(a, b) { return b.getCenterZ() - a.getCenterZ(); }); | |
var shapes = this.shapes; | |
var ctx = this.ctx; | |
ctx.ctx.save(); | |
try { | |
ctx.ctx.fillStyle = this.backgroundStyle; | |
ctx.ctx.fillRect(-ctx.width / 2, -ctx.height / 2, ctx.width, ctx.height); | |
} | |
finally { | |
ctx.ctx.restore(); | |
} | |
for (var i = 0; i < shapes.length; i ++) { | |
shapes[i].render(ctx); | |
} | |
return this; | |
} | |
}; | |
function Context3D(ctx, dist, width, height) { | |
this.ctx = ctx; | |
// TODO: 莉翫?豎コ繧∵遠縺。 | |
this.ctx.textBaseline = 'top'; | |
this.ctx.textAlign = 'left'; | |
this.dist = dist; | |
this.m = new Matrix3D(); | |
this.originX = 0; | |
this.originY = 0; | |
this.stateStack = []; | |
this.width = width; | |
this.height = height; | |
} | |
Context3D.prototype = { | |
set viewDistance(dist) { | |
return this.dist = dist; | |
}, | |
get viewDistance() { | |
return this.dist; | |
}, | |
set matrix(m) { | |
return this.m = m; | |
}, | |
get matrix() { | |
return this.m; | |
}, | |
set fillStyle(fillStyle) { | |
return this.ctx.fillStyle = fillStyle; | |
}, | |
get fillStyle() { | |
return this.ctx.fillStyle; | |
}, | |
createProjectionPoint: function(x, y) { | |
return this.m.translate(this.originX, this.originY, 0).transform(new Point3D([[x], [y], [0]])).getProjectionPoint(this.dist); | |
}, | |
// back-reference to the canvas | |
get canvas() { | |
return this.ctx.canvas; | |
}, | |
// state | |
save: function() { | |
this.stateStack.push({ | |
matrix: this.m | |
}); | |
this.ctx.save(); | |
}, | |
restore: function() { | |
var state = this.stateStack.pop(); | |
this.m = state.matrix; | |
this.ctx.restore(); | |
}, | |
// rects | |
rect: function(x, y, w, h) { | |
this.moveTo(x, y); | |
this.lineTo(x + w, y); | |
this.lineTo(x + w, y + h); | |
this.lineTo(x, y + h); | |
this.lineTo(x, y); | |
}, | |
fillRect: function(x, y, w, h) { | |
this.beginPath(); | |
this.rect(x, y, w, h); | |
this.fill(); | |
this.closePath(); | |
}, | |
strokeRect: function(x, y, w, h) { | |
this.beginPath(); | |
this.rect(x, y, w, h); | |
this.stroke(); | |
this.closePath(); | |
}, | |
// path API | |
beginPath: function() { | |
this.ctx.beginPath(); | |
}, | |
closePath: function() { | |
this.ctx.closePath(); | |
}, | |
moveTo: function(x, y) { | |
var p = this.createProjectionPoint(x, y); | |
this.ctx.moveTo(p.x, p.y); | |
}, | |
lineTo: function(x, y) { | |
var p = this.createProjectionPoint(x, y); | |
this.ctx.lineTo(p.x, p.y); | |
}, | |
quadraticCurveTo: function(cpx, cpy, x, y) { | |
var cp = this.createProjectionPoint(cpx, cpy); | |
var p = this.createProjectionPoint(x, y); | |
this.ctx.quadraticCurveTo(cp.x, cp.y, p.x, p.y); | |
}, | |
bezierCurveTo: function(cp0x, cp0y, cp1x, cp1y, x, y) { | |
var cp0 = this.createProjectionPoint(cp0x, cp0y); | |
var cp1 = this.createProjectionPoint(cp1x, cp1y); | |
var p = this.createProjectionPoint(x, y); | |
this.ctx.quadraticCurveTo(cp0.x, cp0.y, cp1.x, cp1.y, p.x, p.y); | |
}, | |
arc: function(x, y, radius, startAngle, endAngle, anticlockwise) { | |
if (startAngle == endAngle) { | |
return; | |
} | |
if (anticlockwise) { | |
while (startAngle <= endAngle) { | |
endAngle -= Math.PI * 2; | |
} | |
} | |
else { | |
while (startAngle >= endAngle) { | |
endAngle += Math.PI * 2; | |
} | |
} | |
var angleSpan = endAngle - startAngle; | |
var stepNum = Math.ceil(Math.abs(angleSpan / (Math.PI / 4))); | |
var angleStep = angleSpan / stepNum; | |
this.moveTo( | |
x + radius * Math.cos(startAngle), | |
y + radius * Math.sin(startAngle) | |
); | |
for (var i = 1; i <= stepNum; i++) { | |
angle = startAngle + angleStep * i; | |
var ax = radius * Math.cos(angle); | |
var ay = radius * Math.sin(angle); | |
var cx = ax + radius * Math.tan(angleStep / 2) * Math.cos(angle - Math.PI/2); | |
var cy = ay + radius * Math.tan(angleStep / 2) * Math.sin(angle - Math.PI/2); | |
this.quadraticCurveTo(cx + x, cy + y, ax + x, ay + y); | |
} | |
}, | |
drawImage: function(img, dx, dy, width, height) { | |
var num = 10; | |
var imgWidth = img.width; | |
var imgHeight = img.height; | |
var stepWidth = width / num; | |
var stepHeight = height / num; | |
var stepImgWidth = imgWidth / num; | |
var stepImgHeight = imgHeight / num; | |
if (!imgWidth || !imgHeight) { | |
return | |
} | |
var points = []; | |
for (var i = 0, x = dx; i <= num; i++, x += stepWidth) { | |
var subpoints = []; | |
points.push(subpoints); | |
for (var j = 0, y = dy; j <= num; j++, y += stepHeight) { | |
subpoints.push([x, y]); | |
} | |
} | |
var ctx = this.ctx; | |
for (var i = 0; i < num; i ++) { | |
for (var j = 0; j < num; j++) { | |
var p0 = this.createProjectionPoint.apply(this, points[i][j]); | |
var p1 = this.createProjectionPoint.apply(this, points[i + 1][j]); | |
var p2 = this.createProjectionPoint.apply(this, points[i][j + 1]); | |
var p3 = this.createProjectionPoint.apply(this, points[i + 1][j + 1]); | |
if ((p2.y - p0.y) < (p3.y - p1.y)) { | |
var fixHeight = (p3.y - p1.y) / (p2.y - p0.y); | |
} | |
else { | |
fixHeight = 1; | |
} | |
ctx.save(); | |
try { | |
ctx.transform(Math.ceil(p1.x - p0.x), Math.ceil(p1.y - p0.y), Math.ceil(p2.x - p0.x), Math.ceil(p2.y - p0.y), Math.floor(p0.x), Math.floor(p0.y)); | |
ctx.scale(1 / stepWidth, 1 / stepHeight); | |
ctx.drawImage( | |
img, | |
i * stepImgWidth, j * stepImgHeight, stepImgWidth, stepImgHeight, | |
0, 0, stepWidth + 1, stepHeight * fixHeight + 2 | |
); | |
} | |
finally { | |
ctx.restore(); | |
} | |
} | |
} | |
}, | |
set font(font) { | |
var m; | |
if (!(m = font.match(/\b([\d\.]+)\s*?px\b/))) { | |
console.log(font); | |
throw Error(); | |
} | |
this.fontSize = parseInt(m[1], 10); | |
return this.ctx.font = font; | |
}, | |
fontSize: 10, | |
fillText: function(text, x, y) { | |
var ctx = this.ctx; | |
var height = this.fontSize; | |
for (var i = 0; i < text.length; i ++) { | |
var t = text[i]; | |
if (ctx.mozMeasureText) { | |
var width = ctx.mozMeasureText(t); | |
} | |
else { | |
var width = ctx.measureText(t).width; | |
} | |
var p0 = this.createProjectionPoint(x, y); | |
var p1 = this.createProjectionPoint(x + width, y); | |
var p2 = this.createProjectionPoint(x, y + height); | |
ctx.save(); | |
try { | |
ctx.transform( | |
p1.x - p0.x, (p1.y - p0.y || 0.0000000000000000000000000000001), // for Google Chrome's Bug | |
p2.x - p0.x, p2.y - p0.y, | |
p0.x, p0.y); | |
ctx.scale(1 / width, 1 / height); | |
if (ctx.mozDrawText) { | |
ctx.mozDrawText(t); | |
} | |
else { | |
ctx.fillText(t, 0, 0); | |
} | |
} | |
finally { | |
ctx.restore(); | |
} | |
x = x + width; | |
} | |
}, | |
fill: function() { | |
this.ctx.fill(); | |
}, | |
stroke: function() { | |
this.ctx.stroke(); | |
} | |
}; | |
function Shape3D(properties) { | |
for (var name in properties) { | |
this[name] = properties[name]; | |
} | |
} | |
Shape3D.prototype = { | |
center: [0, 0], | |
normalPoint: new Point3D([[0], [0], [-1]]), | |
matrix: new Matrix3D(), | |
redraw: function() { | |
return this; | |
}, | |
getCenterZ: function() { | |
return this.matrix.transform(new Point3D([[this.center[0]], [this.center[1]], [0]])).z; | |
}, | |
getBright: function() { | |
return (this.matrix.transform(this.normalPoint).z - this.getCenterZ()) * -1; | |
}, | |
isBack: function() { | |
return (this.matrix.transform(this.normalPoint).z - this.getCenterZ()) >= 0; | |
}, | |
render: function(ctx) { | |
if (!this.isBack()) { | |
ctx.save(); | |
try { | |
ctx.matrix = this.matrix; | |
var shadowRect = this.renderImpl(ctx, ctx.width, ctx.height); | |
if (!shadowRect) { | |
shadowRect = [-ctx.width / 2, -ctx.height / 2, ctx.width, ctx.height]; | |
} | |
ctx.beginPath(); | |
ctx.rect.apply(ctx, shadowRect); | |
var dark = 1 - this.getBright(); | |
if (dark > 0.001) { | |
ctx.save(); | |
try { | |
ctx.fillStyle = 'rgba(0, 0, 0, ' + dark + ')' | |
ctx.fill(); | |
} | |
finally { | |
ctx.restore(); | |
} | |
} | |
ctx.closePath(); | |
} | |
finally { | |
ctx.restore(); | |
} | |
return this; | |
4} | |
} | |
}; | |
function go(i) { | |
window.localStorage.index = i; | |
history.go(0); | |
} | |
// Version Check | |
if (!navigator.userAgent.match(/AppleWebKit\//) || | |
!window.CanvasRenderingContext2D || | |
!window.CanvasRenderingContext2D.prototype || | |
!window.CanvasRenderingContext2D.prototype.measureText || | |
!window.CanvasRenderingContext2D.prototype.fillText || | |
!window.CanvasRenderingContext2D.prototype.transform | |
) { | |
if (!confirm('このプレゼンテーションを実行するには以下のブラウザが必要です。\n\n- 最新の Google Chrome Dev Branch\n- WebKit Nightly Build\n\nこのブラウザでは、正常に動きませんが、実行してみますか?')) { | |
throw Error(); | |
} | |
} | |
if (!window.localStorage) { | |
if (window.globalStorage) { | |
window.localStorage = window.globalStorage[location.hostname] | |
} | |
else { | |
window.localStorage = {}; | |
window.localStorage.__defineGetter__('index', function() { | |
var m; | |
if (m = document.cookie.match(/\bindex=(\d+)/)) { | |
return +m[1] || 0; | |
} | |
else { | |
return 0; | |
} | |
}); | |
window.localStorage.__defineSetter__('index', function(index) { | |
document.cookie = 'index=' + index; | |
}); | |
} | |
} | |
var randoms = []; | |
for (var i = 0; i < 1000; i++) | |
randoms.push(Math.random()); | |
var randomIndex = 0; | |
var randomStart = function(i) { | |
randomIndex = i; | |
}; | |
var random = function() { | |
return randoms[randomIndex++]; | |
}; | |
var textareas = document.getElementsByTagName('textarea'); | |
var data = []; | |
for (var i = 0, l = textareas.length; i < l; i ++) { | |
var textarea = textareas[i]; | |
var classes = textarea.className.split(' '); | |
data[i] = [classes[0], classes[1] || 'sans-serif', textarea.value]; | |
} | |
function PageShape(data, font) { | |
if (font == 'javascript') { | |
this.js = new Function('ctx', 'width', 'height', data); | |
} | |
else if (data.substring(0, 4) == 'img:') { | |
this.img = new Image(); | |
this.img.src = data.substring(4); | |
} | |
else { | |
this.datas = data.split('\n'); | |
this.datas.pop(); | |
this.font = font; | |
} | |
} | |
PageShape.prototype = new Shape3D({ | |
renderImpl: function(ctx, width, height) { | |
if (this.js) { | |
this.js(ctx, width, height); | |
} | |
else if (this.img) { | |
ctx.fillStyle = 'white'; | |
ctx.fillRect(-width / 2, -height / 2, width, height); | |
ctx.drawImage(this.img, -width * .8 / 2, -height * .8 / 2, width * .8, height * .8); | |
} | |
else if (this.datas) { | |
ctx.fillStyle = 'white'; | |
ctx.fillRect(-width / 2, -height / 2, width, height); | |
ctx.fillStyle = 'black'; | |
var lineHeight = this.calcLineHeight(width, height); | |
fontSize = lineHeight * 0.85; | |
ctx.font = fontSize + 'px ' + this.font; | |
for (var i = 0; i < this.datas.length; i ++) { | |
var data = this.datas[i]; | |
ctx.fillText(data, -width / 2, -height / 2 + i * lineHeight + ((lineHeight - fontSize) / 2)); | |
} | |
} | |
}, | |
calcLineHeight: function(width, height) { | |
return Math.min(height / this.datas.length, | |
width / this.getMaxRowSize() * 2); | |
}, | |
// 最長の行の文字数を返す。 | |
getMaxRowSize: function(height, width) { | |
var maxRowSize = 1; | |
for (var i=0; i < this.datas.length; i++) { | |
maxRowSize = Math.max(maxRowSize, this.getCharacterLength(this.datas[i])); | |
} | |
return maxRowSize; | |
}, | |
// 半角英数字以外は2文字換算で文字数を返す | |
getCharacterLength: function(str) { | |
return str.length * 2 - this.getHankakuCount(str); | |
}, | |
getHankakuCount: function(str) { | |
var pattern = /[a-z0-9 ]/ig; | |
return str.length - str.replace(pattern, '').length; | |
} | |
}); | |
var shapes = []; | |
for (var i = 0; i < data.length; i ++) { | |
shapes[i] = [data[i][0], new PageShape(data[i][2], data[i][1])]; | |
} | |
function sinoidal(x) { return (-Math.cos(x*Math.PI)/2) + 0.5; | |
} | |
function sinoidalN(x, n) { | |
x /= n; | |
return (-Math.cos(x*Math.PI)/2) + 0.5; | |
} | |
window.addEventListener('DOMContentLoaded', function(evt) { | |
var elm = document.getElementsByTagName('canvas')[0]; | |
var width = elm.offsetWidth; | |
var height = elm.offsetHeight; | |
elm.width = width; | |
elm.height = height; | |
var ctx = elm.getContext('2d'); | |
var scene = new Scene3D(elm); | |
var index = + localStorage.index || 0; | |
if (index >= shapes.length) { | |
index = shapes.length - 1; | |
} | |
var shape = shapes[index][1]; | |
scene.append(shape); | |
scene.render(); | |
var pos = 0; | |
var busy = false; | |
document.oncontextmenu = function() { | |
next(-1); | |
return false; | |
}; | |
document.onclick = function() { | |
next(1); | |
return false; | |
}; | |
function next(dir) { | |
if (busy) return; | |
if (!shapes[index + dir]) { | |
return; | |
} | |
busy = true; | |
var nextShape = shapes[index + dir][1] | |
if (dir == 1) { | |
var mode = shapes[index + dir][0]; | |
} | |
else { | |
var mode = shapes[index][0]; | |
} | |
var frameCount = 10; | |
var submode = Math.floor(mode / 10) % 10; | |
if (submode == 1) { | |
var d = 1000; | |
var finishZ = 5; | |
} | |
else if (submode == 2) { | |
var d = 10000; | |
var finishZ = 20; | |
} | |
else { | |
var d = 0; | |
var finishZ = 0; | |
} | |
switch(mode % 10) { | |
case 0: | |
var finish = frameCount; | |
nextShape.matrix = new Matrix3D().translate(dir * width, 0, 0); | |
var matrix = new Matrix3D().translate(- dir * width / finish, 0, 0); | |
break; | |
case 1: | |
finish = frameCount; | |
nextShape.matrix = new Matrix3D().translate(- dir * width, 0, 0); | |
var matrix = new Matrix3D().translate(dir * width / finish, 0, 0); | |
break; | |
case 2: | |
finish = frameCount; | |
nextShape.matrix = new Matrix3D().translate(dir * width, 0, 0); | |
var matrix = new Matrix3D().translate(- dir * width / finish, 0, 0); | |
break; | |
case 3: | |
finish = frameCount; | |
nextShape.matrix = new Matrix3D().translate(- dir * width, 0, 0); | |
var matrix = new Matrix3D().translate(dir * width / finish, 0, 0); | |
break; | |
case 4: | |
finish = frameCount; | |
nextShape.matrix = new Matrix3D().translate(0, - dir * height, 0); | |
var matrix = new Matrix3D().translate(0, dir * height / finish, 0); | |
break; | |
case 5: | |
finish = frameCount; | |
nextShape.matrix = new Matrix3D().translate(0, dir * height, 0); | |
var matrix = new Matrix3D().translate(0, - dir * height / finish, 0); | |
break; | |
case 6: | |
finish = frameCount; | |
nextShape.matrix = new Matrix3D().translate(0, dir * height, 0); | |
var matrix = new Matrix3D().translate(0, - dir * height / finish, 0); | |
break; | |
case 7: | |
finish = frameCount; | |
nextShape.matrix = new Matrix3D().translate(0, dir * height, 0); | |
var matrix = new Matrix3D().translate(0, - dir * height / finish, 0); | |
break; | |
case 8: | |
finish = frameCount; | |
nextShape.matrix = new Matrix3D().translate(0, dir * height, 0); | |
var matrix = new Matrix3D().translate(0, - dir * height / finish, 0); | |
break; | |
case 9: | |
finish = frameCount; | |
nextShape.matrix = new Matrix3D().translate(0, dir * height, 0); | |
var matrix = new Matrix3D().translate(0, - dir * height / finish, 0); | |
break; | |
} | |
scene.append(nextShape); | |
var m = shape.matrix; | |
var nm = nextShape.matrix; | |
var interval1 = setInterval(function() { | |
shape.matrix = shape.matrix.concat(matrix); | |
nextShape.matrix = nextShape.matrix.concat(matrix); | |
if (++pos == finish) { | |
clearInterval(interval1); | |
pos = 0; | |
index += dir; | |
localStorage.index = index; | |
scene.remove(shape); | |
shape = nextShape; | |
shape.matrix = new Matrix3D(); | |
nextShape = null; | |
busy = false; | |
} | |
scene.render(); | |
}, 40); | |
} | |
}, false); | |
</script> | |
</body> | |
</html> | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment