ES6で花火を打ち上げる このエントリをはてなブックマークに登録

2015年09月25日

ダニーダニー / ,

はじめに

夏が終わってしまいましたが、毎年恒例の花火を今回はES6で書いてみました。
前回と同じ形の花火だと移植するだけになってしまうので、花火の形も変えました。

以下の環境で動作確認しています。

  • Firefox 40.0.3
  • Chrome 45.0.2454.85

ソース

index.html

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
    <script type="text/javascript" src="./javascripts/request_animation_frame.js"></script>
    <script type="text/javascript" src="./javascripts/fireworks.js"></script>
    <script type="text/javascript" src="./javascripts/main.js"></script>
    <title>hanabi2015</title>
  </head>
  <body>
    <canvas id="fireworks"></canvas>
  </body>
</html>

fireworks.js

'use strict';

class Fireworks {
  constructor(id) {
    this.initCanvas(id);
    this.settings = {
      gravity: 0.2,
      damping: 0.95,
      sparkSize: 3
    };
    this.fire();
  }

  initCanvas(selector) {
    this.canvas = document.getElementById(selector);
    this.canvas.width  = 500;
    this.canvas.height = 500;
    this.context = this.canvas.getContext('2d');
    this.context.fillStyle = '#000000';
    this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
  }

  initSparks() {
    let x = this.canvas.width / 2;
    let y = this.canvas.height / 2;

    for (let i = 0; i <= 700; i++) {
      let vx = Math.random();
      let vy1 = Math.pow(vx, 2 / 3) - Math.sqrt(1 - (vx * vx));
      let vy2 = Math.pow(vx, 2 / 3) + Math.sqrt(1 - (vx * vx)) - 0.4;
      let speed = Math.random() * 6;

      vx *= speed;
      vy1 *= speed;
      vy2 *= speed;

      this.sparks.push({x: x, y: y, vx: vx, vy: - vy1});
      this.sparks.push({x: x, y: y, vx: vx, vy: - vy2});
      this.sparks.push({x: x, y: y, vx: - vx, vy: - vy1});
      this.sparks.push({x: x, y: y, vx: - vx, vy: - vy2});
    }
  }

  update() {
    for (let spark of this.sparks) {
      spark.x += spark.vx;
      spark.y += spark.vy + this.settings.gravity;
      spark.vx *= this.settings.damping;
      spark.vy *= this.settings.damping;
      this.draw(spark);
    }
    this.context.globalCompositeOperation = 'source-over';
    this.context.fillStyle = 'rgba(0, 0, 0, 0.3)';
    this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
    this.sparkSize *= 0.97;
    if (this.sparkSize < 0.03) {
      this.fire();
      return;
    }
    requestAnimationFrame(this.update.bind(this));
  }

  fire() {
    this.sparkSize = this.settings.sparkSize;
    this.sparks = [];
    this.initSparks();
    requestAnimationFrame(this.update.bind(this));
  }

  draw(spark) {
    this.context.fillStyle = '#723057';
    this.context.globalCompositeOperation = 'lighter';
    this.context.beginPath();
    this.context.arc(spark.x, spark.y, this.sparkSize, 0, Math.PI * 2, true);
    this.context.fill();
  }
}

request_animation_frame.js

'use strict';

for (let vendor of ['moz', 'o', 'ms']) {
  window.requestAnimationFrame = window[\`${vendor}RequestAnimationFrame\`];
  if (window.requestAnimationFrame) {
    break;
  }
}

if (!window.requestAnimationFrame) {
  window.requestAnimationFrame = (callback) => {
    setTimeout(callback, 1000 / 60);
  }
}

main.js

'use strict';

window.onload = () => {
  new Fireworks('fireworks');
}

実際にES6で書いてみて良かった点

Class

CoffeeScriptを使ってた理由の一つとしてクラスが使えるというのがあったのですが、ES6でも使えるようになって良かったですね。

例として、以下のような形でクラス名やコンストラクタやメソッドを定義して使えます。

class Fireworks {
  constructor() {

  }

  fire() {

  }
}

let fireworks = new Fireworks();
fireworks.fire();

Arrow Function

今まで、functionって書いてたのが=>で済むので簡潔に書けるようになった点がいいですね。

ただ違う点は単純にfunctionの省略記法ではなくて、thisのスコープが、定義されたスコープになります。

今までこう書いていたのが

var test = {
  run: function() {
    this.i = 20;
    var self = this;
    setTimeout(function() {
      console.log(self.i); // => 20
    });
  }
};

test.run();

selfに代入しないで済むようになります。

var test = {
  run: function() {
    this.i = 20;
    setTimeout(() => {
      console.log(this.i); // => 20
    });
  }
};

test.run();

要するにArrow Functionを使うとその関数のスコープのthisがバインドされます。

var test = {
  run: function() {
    this.i = 20;
    setTimeout(function() {
      console.log(self.i); // => 20
    }.bind(this));
  }
};

test.run();

Let

今までローカル変数を定義した場合、ブロック内で定義しても、ブロック外の変数が上書きされるようになってました。

var i = 30;

console.log(i); // => 30

{
  var i = 20;

  console.log(i); // => 20
}

console.log(i); // => 20

letを使うと、ブロックの中の範囲に有効なスコープの変数が定義できるようになります。

let i = 30;

console.log(i); // => 30

{
  let i = 20;

  console.log(i); // => 20
}

console.log(i); // => 30

Iterator

イテレーターが使えるようになったので、今までこう書いていたのが

var i;
var vendors =  ['moz', 'o', 'ms'];
for (i = 0; i < vendors.length; i++) {
  console.log(vendors[i]);
}

こう書けるようになりました。

for (let vendor of ['moz', 'o', 'ms']) {
  console.log(vendor);
}

Template Literal

文字列をバッククオートにして${}で囲ったものが展開されます。
今回だと、ベンダープレフィックスを付けた、requestAnimationFrameを定義するのに使ってます。

window.requestAnimationFrame = window[\`${vendor}RequestAnimationFrame\`];

リポジトリ

https://github.com/f96q/es6-hanabi

最後に

今までのJavaScriptだと言語的な機能で足りない部分があって、CoffeeScriptが使える環境の場合はCoffeeScriptを使ってました。

ただ、CofeeScriptはブラウザで直接動かすことができないのと、標準的な言語でない点が悩みどころでした。

ES6だとJavaScriptに足りなかった機能や、最近の言語に入ってるような機能が入り、より使いやすくなったんじゃないかと思います。

後は標準サポートされるブラウザが増えるといいですね。

参考

http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts

宣伝

DocBase

情報共有サービス DocBase を本リリースしました。
https://docbase.io

DocBaseとは

DocBaseとは、小さく始める・みんなで育てる・適切に伝える・安心して伝えるをコンセプトにした情報共有サービスです。
メモという形で小さく始められる、エンジニア以外のメンバーでも使いやすい仕組み、情報をまとめて整理できる、柔軟な権限設定で様々なプロジェクトで使えるなど、積極的な情報共有と業務の効率化を実現し、チームの成長を促します。

詳しくはこちらから。
https://kray.jp/news/docbase/

  1. メモからはじめる情報共有 DocBase 無料トライアルを開始
  2. DocBase 資料をダウンロード

「いいね!」で応援よろしくお願いします!

このエントリーに対するコメント

コメントはまだありません。

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


トラックバック

we use!!Ruby on RailsAmazon Web Services

このページの先頭へ