Vue.jsのFAQサイトを作ってみた
書いてはみたものの、かなり偏った考えかもしれないので、読むときには多少警戒しながら読んで下さい。あと、色々単純化している説明もあるんですが、まずい説明になっている所もあるかもしれません。こういうの、プルリクとか受け取れればいいんでしょうね。
コンテンツ置き場にはScrapboxを使っています。Scrapboxはどちらかと言うと複数人で書く時に威力を発揮するWikiみたいなものなので、書きたい人がいたらTwitterにリプ下さい。
経緯
- Rails等のサーバサイドの経験がある人がSPAに初挑戦して、その際にVueを選択して詰まることが増えている模様
- 考えてみれば、サーバサイドフレームワークからSPAへのスキルセットの移行が結構きつい
- そこで闇を抱えてSPA嫌いになる人もいるっぽい
- そういう場合、Vueだけの解説だと足りていなくて、Node.jsやwebpackやSPA等の周辺知識も教える教材かなにか必要なんじゃないか…と思った
のだけど、書いてみてもやっぱり範囲広すぎて書ききれないなぁ、という感じでした。難しいね。
フロントエンドの教材がどこにあるのかまとめがほしい感
あと、フロントエンドの上級者と初心者の乖離が激しいなぁとはずっと思っていて、HTMLを知らない人がフロントエンドの最前線までたどり着くのは相当辛いんじゃないでしょうか。それが専門職と言えばそれまでなんですが。
N高の教材とか、かなりいい教材は点在してるっぽいんですが、フロントエンド上級者の人はちょっと戻ってきて、自分の辿ってきた道に道しるべを置いてもらえると嬉しいです。
Excelシートを憎み、愛した男
Excelシートが憎い。
Excelシートでの書類作業の辛さは言わずもがな。
以上のものについて、すべてExcelでの作成経験がある。あの世にもExcelシートを作らされる地獄がきっと存在するだろう。
Excelシートが憎い。Excelの万能性ゆえ、落とし込まなくていいものまで落とし込まれ、地獄を生み出す。みんなExcelなら慣れてるので、その他の選択肢は選ばれない。何度かMarkdownからExcelにコンバートするようなツールを書いたけれど、書式が変われば捨てざるを得ない。結局今もExcelを書き続けている。辛い。
でも待って欲しい。あれほど一括編集をエレガントにこなせるUIが他にあるだろうか(2大エディタに習熟した場合を除く)。図も書ける。式は素人でも使えるが、プログラミングの一形態ですらある。Excelやっぱり凄いよ。こんな凄いものが存在するなんて信じられない。気がつくとExcelのことばかり考えている。Excelのことが本当は好きなのかも知れない。
だから、SVGでExcel風スプレッドシートを自作した。車輪の再発明だ。
ロゴも作った。
リポジトリはこちら。
すでにhandsontableという、実用的なスプレッドシートコンポーネントが存在するため、 フルスクラッチで小さく書くのを目標にしている。
下記の通り、コンポーネントは1ファイルの.vueだけで書かれており、Vue以外への依存はない(…と思ったけど、考えたらrebootに依存してた。そのうちどうにかします)。今の所413行。
vue-spreadsheet-lite/SushiGrid.vue at master · anydown/vue-spreadsheet-lite · GitHub
結構頑張って作ったけど、機能不足やバグが多数あったり、そもそもまだコンポーネント単体としてnpmから使えないとか、いろいろな不備がある。まだ実用は出来ないので、ご容赦願いたい。
SVGとVueで難しいコンポーネントを作ろう
SVGとVue.jsの組み合わせが最高なのは下記の記事でも書いたとおり。身の回りでも多くの人がVue.js + SVG遊びに興じているのを観測している。
もっと多くの人にVueとSVGを使って欲しいので、今回は複雑なものを作る時のTipsも足しておく。
スプレッドシートを作る手順
最初はまずSVGを手書きし、プロトタイピングするのをおすすめする。
ちょっと思い出しながら書いてるけど、初期のコミットは下記のようなコードだった。
<template> <svg width=500> <rect x=0 y=0 width=100 height=24> <text x=0 y=12 width=100 height=24>hoge</text> <rect x=100 y=0 width=100 height=24> <text x=100 y=12 width=100 height=24>fuga</text> </svg> </template> <style> rect{ fill: white; stroke: #999; } </style>
- vue createで作成されるHello.vueファイルを編集しながら作り始める。
- この時点でロジックはまったくない。SVGのrectやtextで作りたいものが出来るのかをまず検証する。
- ある程度要素同士にまとまりが見えてきたら、g要素でグルーピングし、
transform="translate(x, y)"
の形に書き直す。
それから、Vueのdataに配列を入れて、v-forで要素を生成する。
コツは、やはり見た目から先に作り込むこと。特にSVGにはz-indexが無いため、書いた順に重ね合わせされるなどの罠が存在する。 自分はDIVなどのHTML要素でコンポーネントを作るときも、まずHTMLタグでモックアップを作ってから、後でロジックを差し込む作り方をするほうが多い。
その場編集(In-place editing)
要素をクリックして、その場で文字列を書き換えられるような仕組みをIn-place editingと呼ぶ。
In-place editingは知る限り下記の3つの方法がある。
- contenteditable属性を使う
- クリック後、要素の上にテキストフィールドを乗せる
- 見えないテキストフィールド(opacity=0)を最初からコンポーネント上に置いておき、クリック位置に移動する
今回のスプレッドシートは、任意のタイミングでのキー入力もテキスト入力として扱わないといけないので、3番目の見えないテキストフィールドを採用した。2番目は付箋を作ったときに採用した。
In-place editingを作るときには、特にIMEの取扱に注意が必要で、ラテン語圏のOSSはIMEを上手く扱えていないケースが多い。そもそも入力出来なかったり、変換確定のEnterキーで入力が終了してしまったり。 最近は同じくIMEを必要とする中国語圏のユーザがOSSに関わることが多くなったからか、状況は改善している。
選択範囲など
SVGはz-indexによる重なり順の制御が出来ないと書いたが、下記のように分類すれば大抵のケースでうまくいく。
UIレイヤーは、選択範囲やボタン、ダイアログなどに当たる。ベジェハンドルやバウンディングボックスなどもここ。
コンテンツレイヤーは、編集対象のコンテンツに当たる。今回の場合であればセルの内容。
これら2つのレイヤーを重ね合わせることで、多くの問題が解決できる。具体的にはそれぞれをSVG要素でくくり、position: absolute
で同じ位置に配置してしまう。
UIとコンテンツを同じSFCの中にまとめてしまいがちだが、そうなると表示順の罠を踏むことになってしまう。 UIのような、クリッカブルな要素がコンテンツの下に来ることは、通常ありえないので、コンテンツと完全に分けてしまうという考え方になる。
今後の予定について
Biliでバンドルを作って、npmに公開したいと思っている。機能不足を解決したタイミングで頑張りたい。
あと、ちょっと作りかけているけど、markdown tableをスプレッドシート / テキストエリアで双方向に編集できるものも作りたい。下記はスペース区切りのもの。
テキストからヘッダを生成する場合は、ヘッダの幅を自動算出しなくてはならない。ちょっと考えないといけない。
おわりに
Excelを追いかければ追いかけるほど、Excel作った奴らまじで凄いという感想しか出ない。今回作ったものをExcelにするには、複数の選択領域をサポートしたり、セル内の改行や結合セル、罫線、図、数式など、とんでもないカロリーの機能を実装しなくてはならない。
だからこそ、Excelは代替されにくいのだろう。互換ツールを作ることも難しいし、そのメリットも少ない。出来の良いツールが市場に居座ることで、そのツールに依存したエコシステムが形成され、誰も逃げられなくなってしまう。
あと、ツールに対して恨みごとを言いたくなってしまうときは、やはり車輪の再発明をしてみるべきなんだと思う。今回で自分はExcelを作った人に対する敬意が少し増したような気がする。作ってみなきゃ人の苦労は分からない。
まぁ、スプレッドシートの仕事が来たら、こんなんじゃなくて普通にhandsontable使おう。ちょっとでかいけどほんと良く出来てます。
vue-cliでビルドしたプロジェクトをgh-pagesにデプロイする
vue-cliでcreateしたプロジェクトは、そのままビルドすると、出力されたdist/index.html
が/js/vendor.a58fe84b.js
のように、絶対パスでアセットを読みに行く。
基本的にはこれで構わないケースが大半だけれど、Github Pagesにデプロイしたい場合は例外で、プロジェクト名と同じサブディレクトリ下に配置されてしまう。そうなると絶対パスから見に行くと404になってしまい、ページは動作しない。
相対パスからアセットを読みに行くようにする
こういったケースはbaseUrlオプションを設定することで回避出来る。下記のファイルを作成する。
vue.config.js
module.exports = { baseUrl: "./" };
こうすると、(先の例でいうと)index.htmlが./js/vendor.a58fe84b.js
を読みに行くようになる。
アセットが色々存在するケースでは確認していないので、これではダメなケースもあるかもしれない。
vue.config.jsについては公式のドキュメントを参照のこと。
デプロイ
デプロイはどうするかというと、gh-pagesというツールを入れるのが楽。
#インストール $ yarn add gh-pages --dev
でインストールした後、package.jsonのscriptsに下記の行を追加する。
"deploy": "gh-pages -d dist"
あとはyarn deploy
を実行するだけ。
動きとしては、まっさらなgh-pagesブランチを作成して、dist下のファイルをすべて配置するのと同じになる。
Github pagesはmasterやdocsフォルダをそのままWebページとして公開出来るように最近なっているが、ビルド成果物をコミットに含めたくない場合は、firebase hostingのようなホスティングサービスを使うか、旧来のgh-pagesブランチを使う形にするのが良いと思う。ちなみにこのやり方はegoist先生がやってたやり方。
気が散らない作業BGMサービス「#作業してくる」
普段のコーディング中のBGM、基本的にSoundCloudなんだけど、下記が不満だったりする。
- まず最初に曲を見繕う作業で時間を10分とか使ってしまう
- ずっと聞いてると気が散るイベントが発生する
- 全然違うジャンルに飛ばされる
- 今ひとつ音の合ってないマッシュアップが流れたりする
- 探し方次第だけど、レーベルの宣伝用ラジオとかも入ってきてしまう事がある
- DJMIXは気が散りにくくていいけど、なんか気分じゃない時がある
- 多分踊るにはいいけど、長時間聞くには疲れる
こういう感じで、曲を飛ばす作業が発生してコーディングが中断してしまうということがよくあった。
環境音は猫が最強
で、環境音が良さそうということで、色々試していた。
ここがそれなりに良いのだけど、環境音でもイライラしたりすると気が散ることがあった。「何このせせらぎ、オフィスに川なんてねえぞ!イライラしてきた!!!!」みたいな感じで。
で、環境音系で一番使ってたのはここ。
こっちのほうが良いです。猫がゴロゴロ言ってるのを延々と聞ける。youtubeにも似たコンセプトの動画が上がってるけどそれもリアリティがあって良いです。なんか、膝上に猫が来て思う存分くつろいでて、 自分もなんか西日とかで温くなってしまって、やることあるんだけど動けなくて一緒に寝ちゃうみたいな、最高なやつをVR体験出来ます。今思ったけど、膝に何か重いものを置くといいだろうな。
でもこれでリラックス出来るのは猫ユーザーだけだと思う。
「作業してくる」の認知負荷の低さ
しかしメロディが欲しいし、昔のMIDIが恋しいなと思っていた所で、「#作業してくる」というサービスが公開された。(曲提供したのでダイマ気味だけど)
ここ数日使ってるけど良いです。
何が良いかというと、とにかく聞いていて認知負荷が低い。作業してくる自体のことを意識することがほとんどない。
- 曲数が多くて100曲くらいある
- 曲の雰囲気やテンポが揃っている
- 時間帯ごとにテーマが決まっている
- 激しい曲は深夜など、特定の時間帯に流れるようにまとめられている
- 全体的に打ち込みっぽくて、ボーカルがない
- ループがシームレス(WebAudioでループさせているらしい)
特に、曲間の空白が気にならないというのは本当に良い!
全体的にどうぶつの森みたいな、ほっとする感じのゲームBGMっぽいのが多くて、自分は好きな感じの曲が多いかな。 1曲気に入ったらだいたい全部気に入るだろうし、1曲合わなかったらほとんど合わないと思う。 これは、企画時にリファレンス楽曲が提示されていて、雰囲気合わせが上手く行ったということだと思う。
サイトの企画者は「cagpie」さんという人で、わずか1ヶ月の間に100人以上の作曲者を集めて、かなりの曲数が集まった。おそらくその多くが書き下ろしだと思う。 下記の募集告知からどういう企画だったかが窺い知れる。
作業用BGM垂れ流しサイト「#作業してくる」の楽曲募集! - TwiPla
技術的なことに関しては下記に書いてある。
cagpieさんは、picotuneの作者でもあったりと、多才だしこれからが楽しみ。
ちなみに、曲一覧は右下の「配信楽曲一覧」で出せて、そこから曲名直接クリックでも聞ける。
今のところ考えた作業BGMの流れ
やっぱり気分に応じて色々使い分けられるようにするのが良さそう。
- とりあえずやる気が無いときは、SoundCloudのお気に入りとかハードコアテクノとかを流して気付けをする
- 猫のゴロゴロを徐々にミックスし、落ち着いて資料などを眺めて計画を練る
- 作業内容が決まったら、作業してくるを立ち上げ、slackの分報チャンネルに作業開始宣言
って感じですかね。あとはamazon musicとかのストリーミングサービスもいいと思う。みんなのおすすめがあればtwitterにリプしてください。
グリグリ動くUIをVueとSVGでサクッと書く
これは Vue.js #3 Advent Calendar 2017 – Qiita 4日目の記事です。
こんにちは。SVGで色々なコンポーネントを作っているものです。最近の作品は下記のような感じです。
Webでグリグリ動くUIを作りたい!!という一心でやっています。
これらはほとんどSVGとVueの組み合わせのみで作っています。依存が少ないというのは大事で、ライブラリ間の相性でハマったり、いろんなドキュメント間を往復することがなくなります。
Webでグリグリ動くUIを作るのは基本的にめんどくさいです。jQuery pluginを駆使して作るのも闇が多いですし、divやcanvasをゴリゴリするのも結構手間がかかります。
ですが、最近はSVGで高度なUI実装されることが増えてきた気がします。特に自分が衝撃を受けたのは、CacooがFlashからSVGにスイッチしたことです。
SVGに遅いイメージを持っていた自分にとってこのニュースは衝撃的で、SVGやれば出来るじゃん!と勇気づけられました。というわけで、VueがSVGを標準で扱えることもあって、最近はSVGのポテンシャルを試していたわけです。他にもMSのSarahさんのVueConfでの発表もきっかけの一つでした。
SVGはcanvasに比べて出来ることが多いわけではないのですが、だいぶ実装の手数が削減できます。サクッと実装できるかどうかの差は大きく、僕はこれでグラフィカルなUIを書こうというモチベーションが湧いてくるようになりました。 ※個人の感想です
SVGの基本的な使い方&バインディング
で、どのくらいサクッとなのかというと、例えば線を引きたければ、下記のように書くだけです。
<svg viewbox="0 0 300 300" width="300" height="300"> <line x1=100 y1=100 x2=200 y2=200 stroke="black"></line> </svg>
で、canvasとは違って、この線はDOM要素なので、Vue.jsを使うとバインディングが効きます。すなわちこういうことが出来ます。
コードはこれだけです。
<div id="app"> <svg viewbox="0 0 300 300" width="300" height="300"> <line :x1="x1" y1=100 x2=200 y2=200 stroke="black"></line> </svg> <div> <input type="range" v-model="x1"> </div> </div> <script> new Vue({ el: "#app", data: { x1: 100 } }) </script>
バインディングが出来るフレームワークであれば、VueでなくてもReactでもRiotでもElmでもいいのですが、とにかくSVGはバインディングとの相性が良いです。バインディングだらけになるとスパゲッティになる懸念がありますが、コンポーネントに切り分けることである程度防ぐことが出来ます。
SVG要素にイベントリスナをつける
また、SVG要素はイベントリスナをひっつけられます。例えば円をクリックした時にmethodを呼びたければ、これだけで良いです。
<circle @click="myMethod" cx=100 cy=100 r=50></circle>
これを利用して、クリックした時にトグルする何かを作りました。
<svg viewbox="0 0 300 300" width="300" height="300"> <circle r=50 cx=150 cy=150 @click="active = !active" :class="{'active': active}"></circle> </svg>
new Vue({ el: "#app", data: { active: false } })
circle{ fill: #333; transition: all 0.4s cubic-bezier(.96,.04,.04,.96); stroke: rgb(119, 0, 255); stroke-width: 1px; } .active{ fill: white; stroke-width: 50px; }
クリックした時に、active変数をトグルし、連動してclassもトグルさせています。で、切り替わる時にCSSアニメーションさせています。楽しいですね。
2017-12-05追記:IE11ではSVG要素のCSS Transitionは動かないそうなので、プロダクションで使う方はご注意下さい。
更に複雑なUIを作るには
以前書いた記事の中で、ヒントになりそうなものをピックアップしました。どれもVue + SVGでの実装です。
また、SVG要素に精通するのが大事です。MDNのSVG要素リファレンスを熟読しましょう。
- コンポーネントとしてSVG要素をまとめる際は、groupという要素でラップします。groupにxやyという属性はなく、transform属性で変形する必要があります。
- SVG子要素でクリック位置を取得する場合は、offsetX / offsetYの解釈がIE+Chrome / Firefoxで異なることに注意する必要があります。offsetX / offsetYは使わないほうが無難です。
終わりに
さて、高度なUIといえばドラッグ&ドロップですが、ざっくり言うとmousedown, mousemove, mouseupを各要素に付けてよしなにします。これに関しては紹介すると長くなるので、またいつかやり方を書こうと思います。
ドラッガブルな要素の実装についてはまだ研究中で、シンプルに書く方法を見いだせていません。ただカスタムディレクティブでさっくり行けるんじゃないかという手応えは感じています。一緒に研究してくれる仲間を募集しています。
おまけ
最後に、この記事を読んでSVGをやってみたくなった人に向けて、雛形HTMLを作ったので置いていきます。
よく使うSVG要素を幾つか表示させているので、属性をいじるなり、Vueとバインドさせるなりさせてみてください。
上記に表示されている基本図形の組み合わせで表現できるUIを考えて、実装してみるのがおすすめです。
ボツネタ
この記事を書くにあたって、無駄に作りかけてしまったSVGエディタです。何も使ってないじゃん。人間は愚かだ…
Vue.js + SVG練習8 : 付箋を作った&vuexに入門した
ふつーのドラッグ出来たりリサイズ出来たりする付箋。 SVGで作る必要があったのかは謎。
コードはこちら。
ポイント
- SVGで複数行のテキストを表示するには、foreignObject + divを使うのが定番らしい。
- ただしその場合、IE11は見捨てなければならない。
- cacooなんかもIEは非対応にしている。
- エディタに関してはSVG外でやっている。cacooをみると、absoluteでiframeとcontenteditableを乗っけている。
- iframeを介す理由は謎だけど、IE対応のためかも?という話を教えてもらった
- 今回はプレーンテキストでいいので、textareaをabsoluteで単に乗っける形とした。
詰まったとこ
下記のエラーが出てしまう。computedにしたselectedItemが、参照するときに関数として取れてきてしまう。
(30,34): error TS2339: Property 'y' does not exist on type '() => FusenItem'.
一旦代入するとうまくいく。
https://github.com/hashrock-sandbox/study-vue-touch-ui-svg/blob/master/src/App.vue#L28
computedってこんな感じだったっけ?多分、TypeScript力が足りないだけなんだと思う。
vuexに入門した
- 実はvuexをまだ使ったことがなかったので、これを期にえいやした
- 公式のドキュメント読みながらなんとか形にした。結構型を書く必要がある箇所が多い。まだ初心者なので言えることは何もない
- 整理されてるんだろうか?ちょっとまだわからないけど、不用意な代入が防がれるのは良いかもしれない。
Vue.js + SVG練習7: 文字がにょろっと出てくるやつ
動いてるところは下記ツイートから見れる(gifがでかくなりすぎてはてなにアップロードできなかった)
文字をアニメーションさせて遊ぶやつができた https://t.co/mTYNrkBLxi pic.twitter.com/UT4inhu0My
— はっしゅろっく (@hashedrock) 2017年11月18日
デモサイトは下記。文字を入力したり、文字サイズや太さを変えて遊べる。
https://hashrock.github.io/canvas-lff/
ソースは下記。
リポジトリについて
- もともとは昔に作ったリポジトリで、LFFというラインフォントフォーマットの存在を知って作ったやつ
- canvas上に描くようにして、光らせたりアニメーションさせたりはしていた
- 今回はVue + SVGに移植した
- ついでにES5 -> ES2015 -> TypeScriptに段階移行した
フォントについて
- LFFはCAD用のフォーマットらしく、プロッターとかで描画したり、物理的に掘ったりしてたんじゃないかなぁと思う。
- ここにあるKST32Bというフォントがレトロでかっこよかったので使わせて頂いた。GPL2。
- 内容としては、0-10までの範囲の点列をテキストで記述しているだけ
- なので、JSでゴリゴリパースした
SVG周りについて
- もともとcanvas向けに処理を書いていたので、楽をするためにd3-pathを利用した
- d3-pathを使うと、canvas互換のAPIで引いた線をpath要素にシリアライズすることが出来る
- (なんかy軸反転してたり、座標が細かい小数点になっている気がする。自分のバグかもしれんが)
アニメーションについて
- animejsを利用
- animejsとmo.jsは2大Exampleかっこいいけど全く使いこなせないライブラリと勝手にみなしている
- よくあるパスアニメーションを利用。点線ハックするやつ。SVGの仕様決めた人こういう使われ方想定してなかっただろうな
- 文字を重ねて3色で描き、それぞれずらしながら線を描画していけば色がついてかっこよくなる(mo.jsのexampleにあったテクニック)
Vueについて
- 今回は1行と1文字、それぞれをSFCにしてある
- 最初はフォントデータの検索が遅く、データ構造変えたりキャッシュを期待してcomputedにもたせてみたり、設計をがちゃがちゃ変えた
- 文字の太さ変更については、CSSプロパティとinput: rangeを紐付ける必要があったので、下記の知見を使った(CSS VariablesはJSから変更したときに再評価される)
CSS variable、JSから変更するとcalcの再評価走る!!!リアクティブ!!! https://t.co/ILcjch7OHH pic.twitter.com/OMLcCOVKul
— はっしゅろっく (@hashedrock) 2017年6月28日
思い
- これが何なのかはわからないけど、今までの中では結構遊んでて楽しいやつができた。
- すっごいアクセシビリティの荒廃したサイト作れそう