ブログ

いろんな方法で(ブラウザ上で)フライウィールを回そう

はじめに

フライウィール三部作、完結編です。
こんにちは、エンジニアのハヤサカです。
私はこれまで、「FLYWHEELって、なに?」にてフライウィールとはそもそも何かをご紹介し、「フライウィールを回してフライウィールを回る」にて現実世界でフライウィールを動かしてみました。
今回はとてもシンプルに、「この画面上(ブラウザ上)でフライウィールの画像を回転させまくろう!」というものです。ここで「フライウィールの画像」を何にしようか迷ったのですが、前回と同じく今回も弊社フライウィールのロゴを使用させていただきます。
なお、ブラウザの環境によっては正しく動かないことがあります。以下の環境でのみ動作確認しておりますことをご了承ください。

  • Chrome 71.0.3578.98
  • Safari 11.1.1 (13605.2.8)
  • Firefox 64.0
  • Edge 42.17134.1.0
  • Vivaldi 2.2.1388.37

CSS

とりあえずは定番から参りましょう。CSSでアニメーションを指定して回してみます!

.html


.css

    /* 3秒で1回転させています。*/
    .spin {
      animation: spin-single 3s linear infinite;
    }
    @keyframes spin-single {
      0% {
        transform: rotate(0deg);
      }
      100% {
        transform: rotate(360deg);
      }
    }

Demo

JavaScript

css を直接指定できず、 JavaScript から style をいじるほうが楽な時、あると思います。特にライブラリ等を使わず書いてみました(所謂 Vanilla JS )。

.html


.js

    // load 時に処理をセット。window.onload に直接セットしても良かったのですが、
    // addEventListener を使うことで他の処理も別途 attach 可能にしています。
    // また、無名関数にアロー関数 [例: (param1, param2) => { doSomething();} ] を使っています。
    // function() {...} よりちょっとだけ簡潔になり、かつ this の参照先が変わる
    // (実行時でなく宣言時のものを参照するため直感的に扱いやすい)、などの利点があります。
    // ES2015(ES6) から使用可能です。
    window.addEventListener("load", () => {
      // 関数スコープの var よりも、
      // ブロックスコープの let(再代入可) や const(再代入不可)を積極的に使うと楽になります。
      // これも ES2015(ES6) から使用可能です。
      const SINGLE_ROTATION_MS = 3000;
      const logoImage = document.getElementById("logo-for-js");
      // アニメーションは window.requestAnimationFrame(callback) を用いて記述するのが望ましいです。
      // callback として渡す関数をまず定義します。
      // ここもノリでアロー関数にしています。 timestamp のミリ秒が引数に入ってきます。
      // (timestampMs) のカッコは取っていいのですが、個人的には書かないと読みづらい気もしています。
      const rotateImage = (timestampMs) => {
        const degree = (timestampMs / SINGLE_ROTATION_MS * 360) % 360;
        // シングルクオート(`)によるテンプレート文字列。こちらも ES2015(ES6) より。
        logoImage.style.transform = `rotate(${degree}deg)`;
        window.requestAnimationFrame(rotateImage);
      };
      window.requestAnimationFrame(rotateImage);
    });

Demo


Canvas (+ JavaScript)

人生は長いものです。CSS にも style にも手が届かない、けど canvas は手元にある!という時がくるかもしれません。そんな「もしも」のために備えましょう。

.html

 

.js

 
    window.addEventListener("load", () => {
      const SINGLE_ROTATION_MS = 3000;
      const canvas = document.getElementById("logo-canvas");
      const context = canvas.getContext("2d");
      // 画像ファイルをロードし、それが終わってから animation の処理に入ります。
      const logoImage = new Image();
      logoImage.src = "fw_logo_rotation.png";
      logoImage.onload = () => {
        const rotateImage = (timestampMs) => {
          // Canvas を clear します。そうしておかないと上書きされる形になり残像が残ってしまいます。
          context.clearRect(0, 0, canvas.width, canvas.height)
          const angleInRadians = (timestampMs / SINGLE_ROTATION_MS * (2 * Math.PI));
          // canvas のコンテクストの座標を中央へとずらし、回転させておきます。
          context.translate(canvas.width / 2, canvas.height / 2);
          context.rotate(angleInRadians);
          // 画像を描画します。現在のコンテクストでは (0, 0) が canvas の中心なので、
          // 画像サイズの半分だけずらして描画することで、画像の中心と (0, 0) とを一致させています。
          context.drawImage(logoImage, -logoImage.width / 2, -logoImage.height / 2);
          // 座標をずらしたぶんを元に戻します。 context.save() / context.restore() を用いる方が
          // 簡潔に書けますが、performance が悪いと巷で言われているようなので手動で戻しています
          // (私自身は実験したことはありませんが)。
          context.rotate(-angleInRadians);
          context.translate(-canvas.width / 2, -canvas.height / 2);
          window.requestAnimationFrame(rotateImage);
        };
        window.requestAnimationFrame(rotateImage);
      }
    });

Demo


GIF

一切 html / css / js をいじることができない!という未来もあり得ます。そんな時は過去に立ち返りましょう。今こそ、「アニメーション GIF」 という素晴らしい武器があったことを思い出すのです。ImageMagick をインストールし、 convert コマンドを使って回転アニメーションGIFを作ってみましょう。

.sh

#!/bin/sh
#
# 参考:
# http://www.imagemagick.org/Usage/warping/#animations
# http://www.imagemagick.org/Usage/warping/animate_distort_rot
# fw_logo_rotation.png を回転する gif を作る、長いひとつながりの command を作っていきます。
# まず -deley 5 と指定することで、アニメーションの一コマあたり 5/100  = 0.05秒 としています。
# -background white -flatten は png の透明部分を白に塗りつぶすためのものです。
command='convert -delay 5 fw_logo_rotation.png -background white -flatten'
# 次に、各コマを作ります。
# 6度ごとに作成することで、 合計で60コマ作成します。一コマ0.05秒なので、これで3秒で一回転となります。
for i in `seq 6 6 360`; do
  # -clone 0 は「最初に指定された画像」、すなわち fw_logo_rotation.png を複製します。
  # -distort SRT $angle と指定することで画像を回転し、かつ画像サイズが変わるのを防ぎます。
  # カッコをつけることで回転がそれぞれにだけ適用されるようにしています。
  command="$command \\( -clone 0 -distort SRT $i \\)"
done
# 最後に、-delete 0 で「最初の画像」、つまりコマンドの最初の fw_logo_rotation.png を抜いてしまいます。
# なぜなら上で作成したコマは 360 度回した時(つまり、回転していない時)も含んでいて被ってしまうからです。
# -loop 0 とすることでループ回数が無限となります。
command="$command -delete 0 -loop 0 fw_logo_rotation_animated.gif"
# 最後に、これまで作った長いコマンドを eval して出力です。
eval $command

Demo

おまけ

最後におまけとして、とある回し損ねていた箇所を回せるような仕掛けをしたリンクを置いておきます。さて、クリックするとどこが回るでしょうか?
クリックして回転スタート!

ではみなさん、フライウィールと共にあらんことを。 Happy Holidays!

著者

ハヤサカトモユキ
ソフトウェアエンジニア。2018年よりFLYWHEELに参加。以前は Google Japan にてGoogle PlayのカスタマーサポートシステムやGoogle Mapsのクライアントアプリの開発に従事。2018年よりFLYWHEELに参加。フライウィールネタはもう一生分書いたのではないか、と真剣に考えているところ。