create-react-appで絶対パスによるimportをする
src/Components/App.jsx
を src/index.js
からimportする場合、create-react-appでは
import App from './Components/App';
と書くが、これを、
import App from 'Components/App';
こう書きたい。
が、デフォルトではパスの解決ができず、 Module not found: Can't resolve 'Components/App’
エラーとなる。
NODE_PATHを設定する
プロジェクトの .env
に NODE_PATH=src
を設定することで解決する。
※ dev serverの再起動が必要
NODE_PATH=src
Same as NODE_PATH in Node.js, but only relative folders are allowed. Can be handy for emulating a monorepo setup by setting NODE_PATH=src.
余談だが、create-react-appのカスタム環境変数は、上記のAdvanced Configurationに含まれるものと、REACT_APP_
プレフィクスを持つもの、それに NODE_ENV
のみが有効となる。
Adding Custom Environment Variables
You must create custom environment variables beginning with REACT_APP_. Any other variables except NODE_ENV will be ignored to avoid accidentally exposing a private key on the machine that could have the same name.
TypeScriptによるStateless Functional Componentの定義
環境構築は create-react-app
とMicrosoft/TypeScript-React-Starterでサクッと行う
$ npx create-react-app my-app --scripts-version=react-scripts-ts $ cd my-app $ npm run start
以前、TypeScriptでReact app作ろうとしてセットアップに手間取ったときとは隔世の感がある。
type React.SFCを使う
@types/react
に React.SFC
という型が用意されているので、これを使う
DefinitelyTyped/index.d.ts at master · DefinitelyTyped/DefinitelyTyped
type SFC<P = {}> = StatelessComponent<P>; interface StatelessComponent<P = {}> { (props: P & { children?: ReactNode }, context?: any): ReactElement<any> | null; propTypes?: ValidationMap<P>; contextTypes?: ValidationMap<any>; defaultProps?: Partial<P>; displayName?: string; }
type SFC<P = {}>
とあるので、全てのpropsはoptionalにする必要がある。
そのため、requiredで定義すると以下のコンパイルエラーが発生する。
error TS2322: Type '{}' is not assignable to type 'IntrinsicAttributes & AppProps & { children?: ReactNode; }'. Type '{}' is not assignable to type 'AppProps'. Property 'name' is missing in type '{}'.
コンポーネントの書き換え
App.tsxをStateless Functional Componentに書き換えるとこのようになる
import * as React from 'react'; import './App.css'; const logo = require('./logo.svg'); interface AppProps { name?: string; } const App: React.SFC<AppProps> = ({ name }) => { return ( <div className="App"> <div className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h2>Welcome to {name}</h2> </div> <p className="App-intro"> To get started, edit <code>src/App.tsx</code> and save to reload. </p> </div> ); }; App.defaultProps = { name: 'React.SFC', }; export default App;
defaultPropsの定義をコンポーネント定義と分けたくない場合は、
const App: React.SFC<AppProps> = ({ name = 'React.SFC' }) => { return ( <div className="App"> ...
とも書ける
React Sketch.app
フロントエンドのデザインと実装のワークフローをスムーズにできないか、というのを考えているときにReact Sketch.appというそれっぽい名前のツールを見つけた。
React Sketch.appとは
そもそもSketch使ったこともなかった(UIに特化したIllustratorくらいの認識)ので、チュートリアルを触りながらどんなものか確認してみた。
Introduction · react-sketchapp
jsxのコードを変更して保存するとSketchにリアルタイム反映される。
これだけだとdev-serverで開発してるのとさして変わらんという印象を受けたので、どういうケースにハマるのかドキュメントを読んでみる。
Managing the assets of design systems in Sketch is complex, error-prone and time consuming. Sketch is scriptable, but the API often changes. React provides the perfect wrapper to build reusable documents in a way already familiar to JavaScript developers.
Sketchでデザインシステムのアセットを管理する手間を軽減する。 SketchのAPIがころころ変わるので、Reactでラッパーを作ったと。
できること
- デザインシステムを実コード(React components)で実装できる
- React Componentの実物を使って画面デザインができる
- 実データを反映したデザイン成果物を作れる
つまづきポイント
チュートリアルのコードを動かしている最中、突然コケるようになった
- saveするたびにPageが作られる
TypeError: MSAttributedString.alloc().initWithAttributedString is not a function.
というエラーを吐いてコードをSketchに反映しなくなる
踏んでいたのは以下のバグ
最新のSketch(48.2)を使っていたのが原因。47を使えとのこと
チームで使う場合は事前にSketchのバージョン合わせておく必要がありそう
まとめ
エンジニア主導の現場かつ、大きめのデザインシステムを導入しているプロジェクトではワークフローをシンプルにできそう。
一方で、JSX(React Sketch.appのAPIも加わるのでさらに難易度上がる)をデザイナーに書いてもらうのは現実的なのか、という課題は残る。
また、Sketchのバージョンアップに伴ってAPIが変更された場合に動作しなくなるケースがあるため、Sketchのバージョンを少し古いものに揃えなければならない。
renderSomething() メソッドを潰す
年末だし大掃除しよう、
ということでClass Componentにある render*
というメソッドを潰している。
stateless functionへの書き換え
具体的には、こんな感じになっているのを
class Foo extends React.Component { renderSomething() { return <div>something</div> } render() { return <div>{this.renderSomething()}</div> } }
こうしたい
const Something = () => <div>something</div> const Foo = () => ( <div> <Something /> </div> )
eslintにruleを追加する
都度search and destroyするのは効率が悪いので
eslintのruleを追加して引っかけることにした。
.eslintrc
に以下の設定を追加。
rules: no-restricted-syntax: - warn - selector: "MethodDefinition[key.name=/^render.+/]" message: "Don't use render* method"
プロジェクトでは error
でCIをコケさせてマージを禁止しており、
本件はMUSTではないため warn
にとどめている。
作ればわかる、FM音源
この記事はSpeee Advent Calendar 2017 15日目の記事です。
前日は@cho3による「サンタクロースの正体を暴く」でした。
動機
昔からFM音源の音は好きだったが、どういう原理で音作りをしているのか詳しくは知らなかった。
FM音源がどんな音色を出すかについては、ここに貼った動画を見てほしい。
一言で言うと80年代の音。
とはいえ90年代以降も多用されていて、メガドライブの内蔵音源や、ガラケーの着メロを鳴らしていたのもFM音源だ。
とある記事(アナログシンセとはまったく違う、“誰でもわかるFM音源”講座 : 藤本健の“DTMステーション”)を読んでいたら、仕組みについてはなんとなく理解できたので、実際に動くものをWebAudioとReactで作ってみることにした。
そうFM音源とは周波数変調をかける音源であり、OP2でOP1にビブラートをかけるというか、くすぐるんです。OP1はもともとポーっと鳴っていた音が、くすぐったいのでピーとかジーとがギャーって鳴るんです
本稿では一般的なFM音源の原理については詳しく触れないので、気になる方は記事を読んでみてほしい。
記事中に出てくるReface DXのUIはグラフィカルでFM音源の音作りも直感的にできそうだ(欲しい)。
以前、DX7の実機でパラメータをいじったときは全然理解できなかった。
オペレータを作る
FM音源において音を構成する最小要素、オペレータから作っていく。
OscillatorNodeとGainNodeをまとめたものにEG(ADSR)を追加してOperator classを作った。
ADSRは数値だけだと直感的にエディットしづらいので可視化した。
SVGを使ったのは、DOMで記述できReactとの相性が良いため。
オペレータのルーティングをする
Operatorには変調をかけられる側(キャリア)とかける側(モジュレータ)があり、それぞれノードを接続する先が違う。
キャリアは音を出すので出力に直結するGainNodeに、モジュレータは変調をかけるのでキャリアのOscillatorNode(のfrequency)に接続する。
例えば、後述するElectric Pianoのルーティングは以下のようになる。
※ nodeの可視化にはFirefoxのWeb Audio エディターを使った
勘のいい読者ならお気づきと思うが、このルーティングはFM音源シンセサイザーにおけるアルゴリズムとほぼ同じだ。
音(ノート)を鳴らす
キーを押すとOscillatorNodeの発振が開始されて音が出る。
今回作ったFM音源は4OPなので1音発音するのに4つのOscillatorNodeのstart()
を呼ぶ。
このとき、各オペレータに内包されているGainNodeに対し、ADSRのパラメータによるレベルの時間的変化をスケジュールする。 これによって、ゆっくり立ち上がる(長いattack)音や、鋭さを伴った(短いdecayと低いsustain)変調、キーを話したときの余韻(長いsustain)といった表現が可能になる。
WebAudio APIのOscillatorNodeは1回鳴らすごとに使い捨てなので、発音のたびに4つのOperatorを内包するNoteオブジェクトを生成し、アルゴリズムに沿ってNodeの接続をした後にOscillatorNodeの再生を行っている。
音色を作る
FM音源の音色作りは変調による波形の変化が極端なため、一般的には難解とされるが「くすぐり」を意識しながら作っていくとそれらしい音ができあがる。
1. Electric Piano
いかにもFM音源といった感じのエレクトリックピアノ。
バリエーションとして 4. Toy Piano
も作った
- carrier #0 に 減衰の速い(
decay
を短めにした) modulator #1 をかけて「コツン」という感じの立ち上がりを与える - 音に厚みを出すため、デチューンした carrier #2 を用意し、金属的な響きを加えるために
freq. ratio
を大きめにとった modulator #3 をかける
2. Organ
オーソドックスなオルガン。
- carrier #0 ~ #3を並列に並べ、それぞれを別の
freq. ratio
で鳴らす
3. Synth Bass
クセの強いシンセベース。
- carrier #0 に modulator #1~3 をかける。modulatorごとに異なる
ADSR
を設定し、音色に時間的な変化を与えている
別途LPFをかけるとそれらしくなりそう
DEMO
※ A~Lのキーを鍵盤に見立てて演奏ができます
Chrome(63以降)推奨。 FirefoxだとAudioParam.cancelAndHoldAtTime()が使えないのでリリースが不自然になる問題が残っている。
今回のdemoのビルドにはParcelを使った。
production buildするときにこのバグを踏んだのだけれど、翌日にリリースされた1.2.0で修正されていた。
Unable to `build` index.html · Issue #8 · parcel-bundler/parcel
TODO
- とりあえず音が出るよう作ったのでリソースの管理が雑なのをなんとかする
- オペレータにフィードバックを追加する
- フィルタを追加する
- アルゴリズムをグラフで可視化する
所感
難解だと思っていたFM音源も、自分で作ることによって理解が進み、ある程度までは思い通りの音を作れるようになった。
D-50のLA音源や物理モデル音源など、原理が気になる音源方式は無数にあるので、機会があればそれらも挑戦してみたい。
明日は@yoppiによる「GoとECSの格闘技について」 お楽しみに!
Parcelで絶対パスを使う場合はbabel-plugin-module-resolverを使う
問題
- Parcelのdocsには相対パスによるimportのサンプルしか載っていない
import Box from ‘../../components/box’
とか書きたくない
babel-plugin-module-resolverでrootを解決する
本家のIssueより、
babel-plugin-module-resolver を使う。
Alias definition · Issue #25 · parcel-bundler/parcel
babel-plugin-module-resolver
をインストールして、pluginの設定を追加する。
.babelrc
{ "plugins": [ ["module-resolver", { "root": ["./src"] }] ] }
これでimportをsrc以下の絶対パスで書けるようになる。
例えば、 /src/components/box.jsx
をimportするには、
import Box from ‘components/box’
となる。
注意点
パスに一致する名前を持つnpm package(上の例ではcomponents)がインストールされていると、node_modules/components/box
を参照するようになり、動作しなくなる。
ちょっとしたappのビルドはParcelでいいかもしれない
Parcelというapplication bundlerが話題なので早速試してみた。
リリースから5日で7000以上のGitHub starを集めている。
Parcelとは
fast, zero configurationを謳うapplication bundler。
要するにconfigの要らない、ビルドの速いwebpackのようなもの。
ちょっとしたものを書くにも(boilerplateを流用できるとはいえ)configの管理は手間で、自分のようにちょっとしたアイデアを試すのにappを作るような場合には非常に助かる。
parcel-bundlerのインストール
$ yarn init -y $ yarn add parcel-bundler --dev
エントリーポイントの作成
parcel-bundler/parcel: 📦🚀 Blazing fast, zero configuration web application bundler
Parcel can take any type of file as an entry point, but an HTML or JavaScript file is a good place to start. If you link your main JavaScript file in the HTML using a relative path, Parcel will also process it for you, and replace the reference with a URL to the output file.
htmlをエントリーポイントにすることで、パスの解決をいい感じに行ってくれる。
index.html
<html> <body> <script src="./application.js"></script> </body> </html>
application.js
import { message } from './message' document.body.innerText = `message: ${message}`
message.js
export const message = 'Hello, Parcel!'
$ yarn parcel index.html yarn run v1.3.2 $ “/path_to_project/node_modules/.bin/parcel" index.html Server running at http://localhost:1234 ✨ Built in 160ms.
これだけである。
dev-serverもビルトインされているので http://localhost:1234 にアクセスして確認する。
ファイルの保存をすればauto reloadもちゃんと動作する。
※ ビルド済みのコードが dist/
、キャッシュが .cache/
以下に作成されるので .gitignore
に追加しておきたい
React
「ちょっとしたapp」を作る場合、最近だとReactを使うことが多いので対応状況が気になる。
$ yarn add react react-dom $ yarn add babel-preset-react babel-preset-env --dev
.babelrc
{ "presets": ["env", "react"] }
box.jsx
import React from 'react' export default function Box(props) { return <h1>{props.message}</h1> }
application.js
import React from 'react' import { render } from 'react-dom' import Box from './box' render( <Box message="Hello React component!!" />, document.querySelector('#app') )
index.html
<html> <body> <div id="app"> <script src="./application.js"></script> </body> </html>
$ yarn parcel index.html yarn run v1.3.2 $ “/path_to_project/node_modules/.bin/parcel" index.html Server running at http://localhost:1234 ✨ Built in 1.48s.
たったこれだけ。
拡張子 .jsx も解決できている。
React込みのビルドをしているとは思えないほど速い。
所感
Zero configurationであるが故に、仕事で使っていると痒いところに手がとどかないということは当然出てくると思う。
しかし、休日にちょっとしたスケッチ気分でappを書きたいときには十分使えるなという印象。
もともとwebpackのビルド速度もそれほど遅いと感じたことはないし、趣味で作っているものなんてサイズはたかが知れているのだが、それでもビルドが速いというのは気持ちがいいものだ。