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のビルド速度もそれほど遅いと感じたことはないし、趣味で作っているものなんてサイズはたかが知れているのだが、それでもビルドが速いというのは気持ちがいいものだ。
Google Apps Scriptを正確なスケジュールで実行する
課題
- GASで「毎日 HH:MM に実行」といった感じのタスクスケジューリングをしたい
- トリガー「時間主導型/日タイマー」だと「午前8時〜9時」のような、ざっくりとした指定しかできない
- 「時間主導型/特定の日時」は
YYYY-MM-DD HH:MM
で指定ができ、指定した時間に正確に実行してくれるが、日時のピンポイント指定のみで条件指定ができないため、1実行につき1件のトリガーを作成しなければならず、運用するのは非現実的
ソリューション
「特定の日時」トリガーを動的に生成するタスクを「日タイマー」トリガーで実行する
サンプルコード
- 下の例では
createTriggers()
で、土日祝を除く毎日、12:00、13:00、14:00にmain()
を実行するためのトリガーを生成する - 生成された
main()
のトリガーは実行が完了した後も残り続けるため、ScriptApp.deleteTrigger()
で掃除する- 削除する際に、
createTriggers()
のトリガーを一緒に削除しないよう、trigger.getHandlerFunction() === 'main’
のものに絞って削除を行う
- 削除する際に、
createTriggers()
を実行するためのトリガーを「時間主導型/日タイマー/午前10時〜11時」で作成する
function isHoliday(date) { if (date.getDay() === 0 || date.getDay() === 6) { return true } // 日本の祝日カレンダーに終日予定があれば祝日とする var calendar = CalendarApp.getCalendarById('ja.japanese#holiday@group.v.calendar.google.com') var events = calendar.getEventsForDay(date) return events.length > 0 } function createTriggers() { console.log('createTriggers') var now = new Date() // 残っているトリガーを掃除する var triggers = ScriptApp.getProjectTriggers() if (Array.isArray(triggers)) { triggers.forEach(function(trigger) { // mainのトリガーのみを削除する if(trigger.getHandlerFunction() === 'main') { ScriptApp.deleteTrigger(trigger); } }) } // 土日祝はスケジュールしない if (isHoliday(now)) { console.log('there are no schedules today') return } var hours = [12, 13, 14] hours.forEach(function(hour) { var date = new Date() date.setHours(hour) date.setMinutes(0) if (now.valueOf() < date.valueOf()) { // main() のトリガーを指定した日時で作成 ScriptApp.newTrigger("main").timeBased().at(date).create() } }) } function main() { console.log('hello.') }
実行結果
createTriggers()
が日タイマーで実行された結果、11:00、12:00、13:00 のトリガーが生成されている。
(なぜか並び順は滅茶苦茶だが...)
各トリガーの実行結果。
秒はまちまちだが、時/分までは指定した時刻に実行されているのがわかる。
(GASで console.log()
した内容はStackdriver Loggingで確認できる)
まとめ
- GASを正確に定期実行したい場合、デフォルトで用意されている時間主導型トリガーのタイマーではなく、
ScriptApp.newTrigger()
を使用する - 生成したトリガーは実行された後も残るので、次のトリガー生成のタイミングにリセットが必要
iPhone X ファーストインプレ
iPhone X、11/3(金)に入手することができた。
Apple表参道にピックアップに行ったが、3日の18時すぎだったので受け取りはスムーズに完了した。
忘れないうちにインプレを記しておく。
気づいた点
- ホームボタンないのは1日で慣れる
- 3日経つと「最初からホームボタンなどいらなかったのでは?」という気持ち
- Control Center、右上から引っ張り出さないとならないのは少し遠く感じる
- Face IDはコツさえつかめば十分Touch IDを置き換えられる
- キッチンやお風呂で指が濡れてても使える
- 真っ暗な場所でも動作する
- ただし、横になっているときは失敗しやすい。要因の一つとして、横になっているときは顔と画面の距離が近い。気持ち遠くで構えることで成功率を高められる
- Face ID認証後、ロック画面でスワイプしないとロック解除されないのが不便。通知を見逃さないためには仕方ないと思うが、Touch IDのときのように認証完了後ダイレクトにホーム画面に遷移するオプションがほしい
- 対策としては、Face IDの認証が成功することを見込んで、スリープ解除したらすぐにスワイプしておく
- OLEDはなんといっても黒
- ディスプレイサイズ
- 8 Plusに比べて広くなった感じはないが、縦の情報量が増えたのでスクロールを要する画面は快適になった
- 上部の欠けは使ってるとそれほど気にならない
- カメラ
- ずっしり重い
- 8 Plusよりは軽いが、比較的コンパクトなフットプリントのおかげかずっしり重く感じる
- 4.7インチのiPhoneを使っていた人は重く感じるかも
- ワイヤレス充電
- オフィシャルのレザーケースを着けているが、PanasonicやAnkerのQi充電器で問題なく充電を行えた
- シルバーを選んだが、クロムメッキされたステンレスフレームは初代〜3GSや初期のiPod Touchを思い起こさせる
まとめ
- Plus系からのアップグレードだと、機能追加とコンパクトさを同時に手に入れられて満足度が高い。
- 操作系に対する懸念はあったが、3日で慣れた。ただし、従来のものと全く別の操作を要求されることもある(Control Centerなど)ので、プライベートがX、仕事用は8などの複数台持ちをすると少しストレスがかかりそう
- 黒が黒であることのありがたみ。テレビもそのうち有機ELにしたい