browserify+babelでasync/awaitを使用した快適p5.js開発環境構築

Friday, April 26, 2019

今回は、processing のjs版として、ブラウザでインタラクティブなグラフィックを実装できるp5js を使用してwebsiteに以下のようなアニメーションを実装した経験から、自分がどのように快適な開発環境を構築したかをご紹介します。

async/awaitを使おう

上記の例では使用していませんが、jsでapiを叩いたりするとpromiseのハンドリングが必要になります。しかし、promiseが頻繁に登場するとthen/catchが複雑になったりして可読性が損なわれることがあるので、モダンなasync/awaitで同期処理をスッキリ書きたいところです。

比較的新しい機能であるこのasync/awaitを各主要ブラウザで使用するには、babel等のツールでブラウザに互換性のある形式にjsをトランスパイルする必要があります。

babelとasync/awaitについてはこちらの記事で詳しく説明されているのでご参照ください。

browserify+babel

数あるツールの中でも、今回はbrowserifyを選択しました。理由は機能が大きすぎず学習コストを抑えながら自分の目的を果たしてくれそうだったからです。

browserify+babelの基本的な使い方はこちらの記事で紹介されています。

念の為自分のpackage.jsonの一部を掲載しておきます。以下の内容でbrowserifyを走らせれば、module化されたファイルをimportrequireで複数読み込んでいる場合でもブラウザで実行できる形式によしなに変換してくれて、かつbabelがjsをトランスパイルしてくれます。ありがたや〜。

{
    "scripts": {
        "watch-js": "watchify -t babelify static/js/src/*.js -o static/js/dist/bundle.js -dv",
        "watch": "npm run watch-js",
        "build": "browserify static/js/src/main.js -o static/js/dist/bundle.js"
    },
    "browserify": {
        "transform": [["babelify", { "presets": ["@babel/preset-env"] }]]
    },
    "devDependencies": {
        "@babel/core": "^7.3.4",
        "@babel/preset-env": "^7.3.4",
        "babel-preset-env": "^1.7.0",
        "babelify": "^10.0.0",
        "babel-polyfill": "^6.26.0"
    },
    "dependencies": {
        "p5": "^0.8.0"
    }
}

localにwatchifyがインストールされていれば、以上のwatch-jsを実行するとjsファイルの変更を検知して自動ビルドしてくれるようになります。

p5.jsを使おう

自分は以下のようにCDNでp5.jsをhtmlファイル内で読み込んで使用しています。適宜最新のバージョンを指定するようにしましょう。

<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/addons/p5.dom.min.js"></script>

実際にp5.jsの処理を記述するファイルでは、まず冒頭でimport 'babel-polyfill';とすることで一部機能が利用可能になります。

公式によると、

This will emulate a full ES2015+ environment (no < Stage 4 proposals) and is intended to be used in an application rather than a library/tool. (this polyfill is automatically loaded when using babel-node).

This means you can use new built-ins like Promise or WeakMap, static methods like Array.from or Object.assign, instance methods like Array.prototype.includes, and generator functions (provided you use the regenerator plugin).

だそうで、プロミスだったり、WeakMapだったりの便利機能が実際には利用できるようになっているらしいです。

また、自分はinstance mode でp5.jsを記述しています。この方が、複数のsketchを管理したり、他のjsモジュールを使用する際に便利だからです。

以下にp5.jsのサンプルコードを貼り付けておきます。

import 'babel-polyfill';

const sleep = msec => new Promise(resolve => setTimeout(resolve, msec));
const sketch = function(p5) {
    p5.setup = function() {
        p5.createCanvas(800, 800);
        p5.background(0);
    }

    p5.draw = async function() {
        p5.fill(255);
        const x = p5.mouseX;
        const y = p5.mouseY;
        await sleep(1000);
        p5.ellipse(x, y, 20, 20);
    }
}

new p5(sketch)

以上のサンプルが以下のcanvasで実行されています。draw関数の中でawaitをつかって1秒後に描画されるようにしました。canvasの上でマウスを動かすと1秒遅れで円がついてくるのがわかると思います。async/awaitが解釈され、ちゃんと動いてますね!

programmingp5.jsprocessingbabelbrowserifyjavascript

Seita Uchimura

Software Engineer in Tokyo

ParityのPOAプライベートネット建ててtruffleを使う時はbyzantium対応をお忘れなく

HugoでSCSS/SASSをCSSに変換するならVersionに注意