HTML5 Canvasでフラクタル雪片を実行する

サイトを雪で装飾するための、HTML5 Canvasでの大Year日のエンターテインメント(Canvasの動作を確認するための興味深い例です)。



私の話では Giorgio Sardo コードに基づいて構築します 。これは、David Flanaganコードに基づいています







以下で説明するすべてのことは、JavaScriptコンソールを起動するだけで、開発ツールを備えた最新のブラウザーでHabréで直接試すことができます。 IE9では、F12キーを押すだけで、このページで直接テストする場合は、ブラウザーをInternet Explorer 9 Standard(Alt + 9)モードにすることを忘れないでください。 デフォルトでは、HabrにはIE8モードが必要です。



Canvasサポートの確認





まず、ブラウザーがCanvasをサポートしていることを確認することから始める必要があります。そのためには、Canvasエレメントを作成し、作業のコンテキストを取得する必要があります。



if ( document .createElement( 'canvas' ).getContext) {

...

}

else {

...

}








最初のケースでは、先に進んで雪片を開始できます。



キャンバスを作成する





雪片を描くために、フルスクリーンでキャンバス(キャンバス)を作成します:



var canvas = document .createElement( 'canvas' );

canvas.style.position = 'fixed' ;

canvas.style.top = '0px' ;

canvas.style.left = '0px' ;

canvas.style.zIndex = '-10' ;

canvas.width = document .body.offsetWidth;

canvas.height = window.innerHeight;



document .body.insertBefore(canvas, document .body.firstChild);








この場合、新しいCanvas要素を作成し、固定された場所を指定して、残りの要素と干渉しないように配置しようとします。



次に、レンダリングのコンテキストを取得します。



var sky = canvas.getContext( '2d' );







コッホスノーフレーク





habrayuzersにとって、 コッホ・スノーフレークが何であるかはよく知られているべきだと思うので、私は写真に自分自身を制限します:







この作品はフラクタルで、便利に再帰的に描かれています。 三角形を描くには、同じ描画パターンを各エッジに順番に適用する必要があります。







1本の線を引くことから始めましょう。 便利なレンダリングの場合、各ローカルレンダリングが水平な直線の描画のように見える変換 (スケーリングと回転)を適用できます。 つまり、線の描画を回転させるのではなく、コンテキストを拡大縮小して回転させます(変換行列を変更します)。



変換マトリックスの状態を保存および復元するには、それぞれsave()およびrestore()関数を使用します。



作業中に、度をラジアンに変換する必要があります(ただし、必要に応じて、すぐにラジアンで記述できます)。



var deg = Math.PI / 180;







1つのエッジを描く再帰関数は次のようになります。



function leg(n, len) {

sky.save(); //

if (n == 0) { // -

sky.lineTo(len, 0); }

else {

sky.scale(1 / 3, 1 / 3); // 3

leg(n – 1, len); sky.rotate(60 * deg);

leg(n – 1, len); sky.rotate(-120 * deg);

leg(n – 1, len); sky.rotate(60 * deg); leg(n – 1, len); }

sky.restore(); //

sky.translate(len, 0); //

}








レンダリングを開始するには、次の関数を使用できます。



function drawFlake(x, y, len, n, stroke, fill) {

sky.save(); sky.strokeStyle = stroke;

sky.fillStyle = fill;

sky.beginPath();

sky.translate(x, y);

sky.moveTo(0, 0); leg(n, len); sky.closePath();

sky.fill();

sky.stroke();

sky.restore();

}








描画するには、パスの作成を開始する必要があることに注意してください。必要に応じて、パスを閉じてから、領域を塗りつぶして線を描画する必要があることを伝えます。 結果:







対応する回転でさらにいくつかのエッジを追加すると、雪片が得られます。



function drawFlake(x, y, len, n, stroke, fill) {

sky.save(); sky.strokeStyle = stroke;

sky.fillStyle = fill;

sky.beginPath();

sky.translate(x, y);

sky.moveTo(0, 0); leg(n, len); sky.rotate(-120 * deg);

leg(n, len); sky.rotate(-120 * deg);

leg(n, len);
sky.closePath();

sky.fill();

sky.stroke();

sky.restore();

}








結果:







雪片の作成と移動





さらに、アイデアは非常に透明です。1)タイマーに雪片を追加して雪片のプールを作成し、2)タイマーで雪片の位置を変更して描画します。



雪片の追加




ランダムな値、雪片の配列、最大数の追加機能。 タイマーを設定します。



var rand = function (n) { return Math.floor(n * Math.random()); }

var flakes = []; var maxflakes = 20;

var snowspeed = 500;

var snowingTimer = setInterval(createSnowflake, snowspeed);








そして実際には、雪片の作成(適切なタイミングで、タイマーをクリーニングすることで新しい雪片の作成を停止します):



function createSnowflake() {

var order = 3;

var size = 10 + rand(50);

var x = rand( document .body.offsetWidth);

var y = window.pageYOffset;



flakes.push({ x: x, y: y, vx: 0, vy: 3 + rand(3), size: size, order: order, stroke: "#99f" , fill: "transparent" });

if (flakes.length > maxflakes) clearInterval(snowingTimer);

}








雪片の移動




画面サイズを変更するとtrueに設定される追加の変数invalidateMeasureが表示されます。 タイマーを設定して、位置と実際の移動機能を更新します(画面をクリアし、位置を更新->雪片を描画)。



var scrollspeed = 64;

setInterval(moveSnowflakes, scrollspeed);



function moveSnowflakes() {

sky.clearRect(0, 0, canvas.width, canvas.height);



var maxy = canvas.height;



for ( var i = 0; i < flakes.length; i++) {

var flake = flakes[i];

flake.y += flake.vy;

flake.x += flake.vx;



if (flake.y > maxy) flake.y = 0;

if (invalidateMeasure) {

flake.x = rand(canvas.width);

}



drawFlake(flake.x, flake.y, flake.size, flake.order, flake.stroke, flake.fill);



//

if (rand(4) == 1) flake.vx += (rand(11) - 5) / 10;

if (flake.vx > 2) flake.vx = 2;

if (flake.vx < -2) flake.vx = -2;

}

if (invalidateMeasure) invalidateMeasure = false ;

}








最終コード





さらに、雪片のランダムな回転と雪片のランダムな色+サイズに応じた詳細など、いくつかの詳細を追加できます。



( function () {

if ( document .createElement( 'canvas' ).getContext) {

if ( document .readyState === 'complete' )

snow();

else

window.addEventListener( 'DOMContentLoaded' , snow, false );

}

else {

return ;

}



var deg = Math.PI / 180;

var maxflakes = 20; var flakes = []; var scrollspeed = 64; var snowspeed = 500;

var canvas, sky;

var snowingTimer;

var invalidateMeasure = false ;



var strokes = [ "#6cf" , "#9cf" , "#99f" , "#ccf" , "#66f" , "#3cf" ];



function rand (n) {

return Math.floor(n * Math.random());

}



//

function snow() {

canvas = document .createElement( 'canvas' );

canvas.style.position = 'fixed' ;

canvas.style.top = '0px' ;

canvas.style.left = '0px' ;

canvas.style.zIndex = '-10' ;



document .body.insertBefore(canvas, document .body.firstChild);

sky = canvas.getContext( '2d' );



ResetCanvas();



snowingTimer = setInterval(createSnowflake, snowspeed);

setInterval(moveSnowflakes, scrollspeed);

window.addEventListener( 'resize' , ResetCanvas, false );

}



// Canvas

function ResetCanvas() {

invalidateMeasure = true ;

canvas.width = document .body.offsetWidth;

canvas.height = window.innerHeight;

}



//

function leg(n, len) {

sky.save(); //

if (n == 0) { // -

sky.lineTo(len, 0); }

else {

sky.scale(1 / 3, 1 / 3); // 3

leg(n - 1, len); sky.rotate(60 * deg);

leg(n - 1, len); sky.rotate(-120 * deg);

leg(n - 1, len); sky.rotate(60 * deg); leg(n - 1, len); }

sky.restore(); //

sky.translate(len, 0); //

}



//

function drawFlake(x, y, angle, len, n, stroke, fill) {

sky.save(); sky.strokeStyle = stroke;

sky.fillStyle = fill;

sky.beginPath();

sky.translate(x, y);

sky.moveTo(0, 0); sky.rotate(angle);

leg(n, len);

sky.rotate(-120 * deg);

leg(n, len); sky.rotate(-120 * deg);

leg(n, len); sky.closePath();

sky.fill();

sky.stroke();

sky.restore();

}



//

function createSnowflake() {

var order = 2+rand(2);

var size = 10*order+rand(10);

var x = rand( document .body.offsetWidth);

var y = window.pageYOffset;

var stroke = strokes[rand(strokes.length)];



flakes.push({ x: x, y: y, vx: 0, vy: 3 + rand(3), angle:0, size: size, order: order, stroke: stroke, fill: 'transparent' });



if (flakes.length > maxflakes) clearInterval(snowingTimer);

}



//

function moveSnowflakes() {

sky.clearRect(0, 0, canvas.width, canvas.height);



var maxy = canvas.height;



for ( var i = 0; i < flakes.length; i++) {

var flake = flakes[i];



flake.y += flake.vy;

flake.x += flake.vx;



if (flake.y > maxy) flake.y = 0;

if (invalidateMeasure) {

flake.x = rand(canvas.width);

}



drawFlake(flake.x, flake.y, flake.angle, flake.size, flake.order, flake.stroke, flake.fill);



//

if (rand(4) == 1) flake.vx += (rand(11) - 5) / 10;

if (flake.vx > 2) flake.vx = 2;

if (flake.vx < -2) flake.vx = -2;

if (rand(3) == 1) flake.angle = (rand(13) - 6) / 271;

}

if (invalidateMeasure) invalidateMeasure = false ;

}

} ());





* This source code was highlighted with Source Code Highlighter .








コードをコピーし、コンソールから実行して、サイトで降雪を取得します。



All Articles