JavaScriptハイパースペース

ハブロビテス! 幸せな宇宙航行日!







あるプロジェクトでは、今日の休日に合わせて、デザイナーはハイパースペースの模倣を作成する必要がありました。 少し考えてから、Canvas要素を使用する方が適切であると判断しました-SVGには非常に多くの要素があり、ブラウザー間のサポートはあまりよくありません。ビデオには背景が大きすぎます。つまり、ファイルが大きすぎてダウンロードに時間がかかりすぎます。 ちなみに、Canvasも理想的なオプションではありません-プロセッサの負荷が高く、RAMを大量に消費します。 しかし、それでも...



マージン入力
コードは、異なるブラウザーでのrequestAnimationFrameの操作を正規化するreqestAnimFrame関数と、同じ整数関数を使用してランダムな整数を生成します。 ソースコードは次のとおりです。

window.requestAnimFrame = (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(/* function */ callback, /* DOMElement */ element){ window.setTimeout(callback, 1000 / 60); }; })(); function getRandomInt(min, max){ return Math.floor( Math.random() * (max - min + 1) ) + min; }
      
      







アプローチ1.スケーリングされた画像をオーバーレイします。


デザインには星空の写真があり、それを使用することにしました。 取って、ロードして、Canvasに置きます。 次に、ピクセルを増やして追加しました。 それからまた。 写真は美しいですが、どのような方法でもカスタマイズすることはできません。



jsfiddleの完全なコードとデモ。



コードにコメントするものはないので、コードだけです。



このアプローチには、いくつかの理由で寿命がありません。



アプローチ2.戦闘。


それぞれの星とループを別々に描くことにしました。



jsfiddleの完全なコードとデモ。 mousemoveイベントで移動がハングしました。



そこで、星の配列を作成し、それらの初期値を生成します。 ここで、xとy、もちろん、星の座標、currentLifeは星からのループの現在の長さの指標であり、nx、ny、lifeは停止後に星を再初期化するために使用されます。 color-カラー配列のオプションの1つ。 原則として、一般的に任意の色を作成できますが、使用可能な色の数を制限する機能は後で役立ちます。 2つの配列があるため、減衰の瞬間に、動く星と​​動かない星を同時に表示する必要があります。 もちろん、これは星の個別のプロパティを持つ1つの配列を介して行うことができます(おそらく必要な場合もあります)が、それ以上のロジックはそれに依存するため、すべてを書き換えるのは面倒です



  var colors = ["white","rgb(200,200,255)"]; function newStar(){ var life = getRandomInt(50,150); var dx = getRandomInt(0,canvas.width); var dy = getRandomInt(0,canvas.height); return { x : dx, y : dy, nx : dx, ny : dy, life : life, currentLife : life, color : colors[getRandomInt(0,1)] }; } var stars = []; var finStars = []; var maxStars = 350; for(var i = 0; i < maxStars; i++){ finStars.push(newStar()); }
      
      







次に、星の表示について説明します。 ここに簡単な数学を含めます:







axがayを指すのと同じように、dxがdyを指すことを説明する必要はないと思います。 currentLifeの値に等しいdxを使用する場合、dy = currentLife *(y-cy)/(x-cx)。 さらに、各スターには2つの状態があります。列車が成長するときと減少するときです。 これを実装するには、2つの定数と2つの変数の4つの値を使用するのが非常に簡単です。 (var1> const1?Var1:const1)から(var2 <const2?Var2:const2)に描画します。 最初に成長し、次に星が消えます。







これをすべて計算することは残っています:



  var x = stars[j].x, // (x,y) -  const1 y = stars[j].y, dx = cx - stars[j].x, dy = cy - stars[j].y; if ( Math.abs(dx) > Math.abs(dy) ){ var xLife = dx > 0 ? stars[j].life : - stars[j].life, // (xLife, yLife) - const2.  star.life    ""  yLife = xLife * dy / dx, xCur = dx > 0 ? - stars[j].currentLife : stars[j].currentLife, // (xCur,yCur) -var1 yCur = xCur * dy / dx, xLast = dx > 0 ? xCur + stars[j].life : xCur - stars[j].life, // (xLast,yLast) - var2 yLast = xLast * dy / dx, mod = "x"; } else { var yLife = dy > 0 ? stars[j].life : - stars[j].life, xLife = yLife * dx / dy, yCur = dy > 0 ? - stars[j].currentLife : stars[j].currentLife, xCur = yCur * dx / dy, yLast = dy > 0 ? yCur + stars[j].life : yCur - stars[j].life, xLast = yLast * dx / dy, mod = "y"; } if(dx > 0 && dy > 0) { var qx = x - ( xLife < xLast ? xLife : xLast); var qy = y - ( yLife < yLast ? yLife : yLast); ctx.moveTo( qx < cx ? qx : cx, qy < cy ? qy : cy); var sx = x - ( xCur > 0 ? xCur : 0); var sy = y - ( yCur > 0 ? yCur : 0); ctx.lineTo( sx < cx ? sx : cx, sy < cy ? sy : cy); if ( mod == "x"){ ctx.lineTo( qx < cx ? qx : cx, (qy < cy ? qy : cy) + 2); } else { ctx.lineTo( (qx < cx ? qx : cx) + 2, qy < cy ? qy : cy); } ctx.lineTo( qx < cx ? qx : cx, qy < cy ? qy : cy); ctx.closePath(); stars[j].nx = sx < cx ? sx : cx; stars[j].ny = sy < cy ? sy : cy; } if(dx < 0 && dy < 0) { var qx = x - ( xLife > xLast ? xLife : xLast); var qy = y - ( yLife > yLast ? yLife : yLast); ctx.moveTo( qx > cx ? qx : cx, qy > cy ? qy : cy); var sx = x - ( xCur < 0 ? xCur : 0); var sy = y - ( yCur < 0 ? yCur : 0); ctx.lineTo( sx > cx ? sx : cx, sy > cy ? sy : cy); if ( mod == "x" ){ ctx.lineTo( qx > cx ? qx : cx, (qy > cy ? qy : cy) + 2); } else { ctx.lineTo( (qx > cx ? qx : cx) + 2, qy > cy ? qy : cy); } ctx.lineTo( qx > cx ? qx : cx, qy > cy ? qy : cy); ctx.closePath(); stars[j].nx = sx > cx ? sx : cx; stars[j].ny = sy > cy ? sy : cy; } if(dx < 0 && dy > 0) { var qx = x - ( xLife > xLast ? xLife : xLast); var qy = y - ( yLife < yLast ? yLife : yLast); ctx.moveTo( qx > cx ? qx : cx, qy < cy ? qy : cy); var sx = x - ( xCur < 0 ? xCur : 0); var sy = y - ( yCur > 0 ? yCur : 0); ctx.lineTo( sx > cx ? sx : cx, sy < cy ? sy : cy); if ( mod == "x" ){ ctx.lineTo( qx > cx ? qx : cx, (qy < cy ? qy : cy) + 2); } else { ctx.lineTo( (qx > cx ? qx : cx) + 2, qy < cy ? qy : cy); } ctx.lineTo( qx > cx ? qx : cx, qy < cy ? qy : cy); ctx.closePath(); stars[j].nx = sx > cx ? sx : cx; stars[j].ny = sy < cy ? sy : cy; } if(dx > 0 && dy < 0) { var qx = x - ( xLife < xLast ? xLife : xLast); var qy = y - ( yLife > yLast ? yLife : yLast); ctx.moveTo( qx < cx ? qx : cx, qy > cy ? qy : cy); var sx = x - ( xCur > 0 ? xCur : 0); var sy = y - ( yCur < 0 ? yCur : 0); ctx.lineTo( sx < cx ? sx : cx, sy > cy ? sy : cy); if ( mod == "x" ){ ctx.lineTo( qx < cx ? qx : cx, (qy > cy ? qy : cy) + 2); } else { ctx.lineTo( (qx < cx ? qx : cx) + 2, qy > cy ? qy : cy); } ctx.lineTo( qx < cx ? qx : cx, qy > cy ? qy : cy); ctx.closePath(); stars[j].nx = sx < cx ? sx : cx; stars[j].ny = sy > cy ? sy : cy; }
      
      







星がどの四半期に属するかによって、値と比較の前の符号が異なるため、コードはほぼ4回複製されます。 さらに、mod変数は、どの座標が先行座標と見なされるかを記憶します(つまり、dxをcurrentLifeまたはdyと同等にします)。 MODを使用しないと、広角のため、縦座標軸の近くにある星が「飛ぶ」のが速すぎます。



最後の発言-オリジナルでは、2色のみが使用されるため、1回のパスでキャンバスは2回だけ描画されます(2色が示されているため)。 同じ色の星はすべて1つの方法で形成され、Canvasに表示されます。



これらすべてをループでラップして実行することが残っています。



アプローチ3.正しい。


適切なアプローチで、私は一般的な既製のソリューションとライブラリの使用を理解しています。 大まかな検査中に既製のソリューションはありませんでした。 ライブラリとして、私はlibcanvasを試してみることにしました。 幸いなことに、Habrahabrでは非常に強く表現されています。



jsfiddleの完全なコードとデモ。 (JSFiddleはgithubでatomおよびlibcanvasをロードしない可能性があるため、ページを数回リロードする必要がある場合があります)



結果は次のとおりです。



  new function () { var center, i, helper, stars; LibCanvas.extract(); helper = new App.Light(new Size( document.width, document.height)); center = helper.app.rectangle.center; stars = []; for(i = 0; i < 350; i++){ new function() { var point = new Point(getRandomInt(document.width/2,document.width),document.height/2), length = getRandomInt(50,150), angle = getRandomInt(0,360), coords = [ new Point(0,0), new Point(0,0), new Point(0,0) ], path = helper.createVector( new Path() .moveTo( coords[0] ) .lineTo( coords[1] ) .lineTo( coords[2] ) .lineTo( coords[0] )).setStyle({fill:"rgb(150,150,150)",stroke:"rgb(150,150,150)"}); point.rotate( - angle.degree(), center); var star = { point : point, length : length, angle : angle, coords : coords, live : 0, setLength : function(){ if (arguments.length > 0){ this.live = arguments[0]; } this.coords[0].x = this.point.x; this.coords[0].y = this.point.y; this.coords[1].x = this.coords[0].x + this.live * Math.cos( this.angle.degree() ); this.coords[1].y = this.coords[0].y - this.live * Math.sin( this.angle.degree() ); this.coords[2].x = this.coords[1].x + 2 * Math.sin( this.angle.degree() ); this.coords[2].y = this.coords[1].y + 2 * Math.cos( this.angle.degree() ); }, path : path }; star.setLength(); stars.push(star); }; } setInterval(function(){ for(var i = 0; i < 350; i++){ stars[i].setLength( stars[i].live + 1 ); stars[i].path.redraw(); } },10); };
      
      







ここでは、戦闘バージョンとは対照的に、三角法を使用した適切な数学が使用されていると言わなければなりませんが、同じ機能を追加し始めていません。 libCanvasでのコード実行の速度は、ネイティブメソッドと大差ありませんが、コードは数倍遅く、開発速度はずっと高くなります。 最初から、いくつかの理由でlibCanvasを使用しませんでした。以前は使用していなかったため、純粋なJavaScriptに慣れていたため、アドインバージョンが著しく遅くなるのではないかと心配しました。 結局のところ、私は無駄に恐れていました。





UPD TheShockのLibCanvasで正しい実装


Jsfiddleの例fullScreenでは特に見栄えがよくなります



スターの作成とチューニングは、別のクラスに移動されます。 星ごとに、速度、色、寿命が決定されます。



 atom.declare( 'Star', App.Element, { progress: 0, opacity: 1, configure: function () { var screenRectangle = this.layer.ctx.rectangle; this.animate = new atom.Animatable(this).animate; this.from = screenRectangle.getRandomPoint(); this.shape = new Polygon(this.from.clone(), this.from.clone(), this.from.clone()); this.angle = -this.from.angleTo(screenRectangle.center); this.sin = this.angle.sin(); this.cos = this.angle.cos(); this.progressSpeed = Math.random() + 0.5; this.color = new atom.Color(128, 128, Number.random(128, 192)).toString(); setTimeout(this.fadeOut.bind(this), Number.random(1000, 8000)); }, fadeOut: function () { this.animate({ time: 2000, props: { opacity: 0 }, onComplete: this.destroy }); }, onUpdate: function () { var sin = this.sin, cos = this.cos, p = this.shape.points; this.progress += this.progressSpeed; p[1].x = p[0].x + this.progress * cos; p[1].y = p[0].y - this.progress * sin; p[2].x = p[1].x + 2 * sin; p[2].y = p[1].y + 2 * cos; this.redraw(); }, renderTo: function (ctx) { ctx.save(); if (this.opacity < 1) ctx.set({ globalAlpha: this.opacity }); ctx.fill( this.shape, this.color ); ctx.restore(); } });
      
      







最初は、作成される星の数が少なくなり、各フレームに追加されます。



 new function () { var helper = new App.Light( new Size(document.width || 800, document.height || 800), { intersection: 'full', invoke: true } ); for(i = 0; i < 200; i++) new Star(helper.layer); atom.frame.add(function () { new Star(helper.layer); }); };
      
      





// UPD終了



それは、宇宙飛行士の日でもあります!



参照:

jsfiddleの写真の例。

jsfiddleでの 「戦闘」の例。

3番目の例のAtomJSlibCanvas

jsfiddleでのlibCanvasの 3番目の例。 (jsfiddleとgithubの機能により、すぐに動作しない場合があります)

エフェクトが作成されたプロモーションサイト



All Articles