LitElementをつかってみている~
使い勝手のいいコンポーネントをあちらこちらで使えるようにしたくなり、WebComponentsの技術をつかってつくりたいなーどうしようかなーと思っています。 というのも、いまつくっているものたちのJavaScript開発環境は、概ねノンフレームワークもしくはVue.js製なのですが、特定のフレームワークに依存しない形でコンポーネントを定義して共通化して、 じぶんのサイトのいろいろなところで使いたいなという気持ちになっているのでした。
また、コンポーネントを仮に広く一般に公開するときも、WebComponentsの形になっていればどの環境でも基本的には使えそうなので便利そう~と思っています。
そんなこんなを考えているとき、LitElementというものがあることを知りました。
lit-element.polymer-project.org
WebComponentsをつくるには、Shadow Treeにつかいたい要素を放り込んだり、CSSを入れたり、イベントハンドリングは~...。といろいろやらないといけないことがありそうです。 それらを比較的簡単に、つくりやすくしてくれるのがLitElementです。ライブラリがコンパクトな点がよさそうかもと思っています。
とりあえずLitElementの開発環境を スタートガイド に従って準備します。
npm install -g polymer-cli
これをインストールしておけば、ビルド環境を用意しなくても開発ができます。ちゃんとコンポーネントとしてつかうときにはビルド環境はあったほうがいいかもですが、とりあえずパパっと試してみたい~というときには便利。
こんなかんじの雑プロジェクトを用意して、素振り実装をしてみました。
まずはHello Worldっぽいことをやってみる
基本的に、 LitElement
クラスを継承する形でコンポーネントのクラスを定義し、最後に customElements.define
でカスタム要素を登録すればよさそうです。
まずは適当にテキストを表示するコンポーネントをつくってみます。
// src/sample-element.js Import { LitElement, html } from 'lit-element'; class SampleElement extends LitElement { constructor() { super(); this.name = 'default name'; } static get properties() { return { name: { type: String }, }; } render(){ return html` <p>${this.name}</p> `; } } customElements.define('sample-element', SampleElement);
カスタム要素の属性は static get properties()
が返すオブジェクトで定義できるようです。このカスタム要素は name
というString型を受け取ります。
属性値は、ふつうのHTML要素と同じく基本的には文字列しか受け取れませんが、type
に指定したコンバータによって LitElement側 で指定した型に変換されます。
render()
でテンプレートを定義してコンポーネントのDOMを構築していきます。上記の場合は <p>
タグで name
属性値を表示するだけの簡単なもの。
では、つくったカスタム要素を使ってみます。これをつかいたいHTMLで読み込めばよいので、以下のようにしてみます。
<!-- sample/index.html --> <!DOCTYPE html> <html> <head> <title>Sample page</title> <meta charset="utf-8"> </head> <body> <sample-element></sample-element> <sample-element name="hoge fuga"></sample-element> <script src="../src/sample-element.js" type="module"></script> </body> </html>
ここで、 polymer-cli
をつかってサーバを立ち上げておきます。 polymer serve
すると、開発サーバが立ち上がります。
私のプロジェクトはこんな感じのディレクトリ構成なので、lit-element-playground
上で polymer serve
を実行すると、 http://localhost:8081/sample
で上記HTMLにアクセスできます。
lit-element-playground ├── README.md ├── node_modules ├── package.json ├── sample │ └── index.html └── src └── sample-element.js
表示してみるとこんなかんじ。 name
属性を設定したほうは hoge fuga
とプリントされています。かんたん!
リストコンポーネントをつくってみる
こんどはリストコンポーネントをつくってみます。ここで、 list
属性値の type
を Array
にしているのがポイント。
// src/list-element.js import { LitElement, html } from 'lit-element'; class SampleElement extends LitElement { constructor() { super(); this.list = []; } static get properties() { return { list: { type: Array }, }; } render(){ return html` <ul> ${this.list.map(item => html`<li>${item}</li>`)} </ul> `; } } customElements.define('sample-list', SampleElement);
同じようにHTMLを書いて実行してみます。
<!-- sample/index.html --> <!DOCTYPE html> <html> <head> <title>Sample page</title> <meta charset="utf-8"> </head> <body> <sample-list list='["hoge","fuga","piyo"]'></sample-list> <script src="../src/sample-list.js" type="module"></script> </body> </html>
sample-list
要素の属性値 list
に ["hoge","fuga","piyo"]
という文字列を指定していますが、先ほど定義した list
プロパティの type
が Array
なので、SampleElement
クラスでは配列値として取り扱うことができます。ちなみに、['hoge','fuga','piyo']
のように、シングルクォートで属性値を書くとパースエラーとなってしまうようです。
表示してみるとこんなかんじ。
属性値の変更を監視することもできそうなので、たとえば list
属性を更新したら自動で表示を更新する、みたいなこともできそう(試していません)。
スタイルを当てつつテキスト入力要素をつくってみる
こんどはテキスト入力要素をつくってみます。指定した正規表現にマッチしなかったらメッセージを表示する機能もつけてみます。 また、そろそろスタイルもつけてみたい~ということで適当なスタイルを当ててみます。
// src/text-element.js import { LitElement, html, css } from 'lit-element'; import { classMap } from 'lit-html/directives/class-map'; class SampleTextbox extends LitElement { constructor() { super(); this.regexp = ''; this.message = ''; this.messageClasses = { hidemsg: false } } static get properties() { return { regexp: { type: String }, message: { type: String }, }; } static get styles() { return css` input { border: 1px solid gainsboro; padding: 10px 20px; border-radius: 3px; } .hidemsg { display: none; } `; } isValid(value) { const re = new RegExp(this.regexp); return !!value.match(re); } validate(e) { if( !this.regexp ) return; this.messageClasses = { hidemsg: this.isValid(e.target.value.toString()) } this.requestUpdate(); } render(){ return html` <div> <input @input="${this.validate}" type="textbox"></input> <span class="${classMap(this.messageClasses)}">${this.message}</span> </div> `; } } customElements.define('sample-textbox', SampleTextbox);
スタイルは static get styles()
で定義します。デフォルトでShadow TreeにCSS定義が入るので、基本的にはCSSのスコープは要素内で閉じています。つかうときは、クラス名を <input class="hoge">
のように指定してもいいですが、 classMap
をつかえば、動的にスタイルを変更できます。ただし、classMap
に指定するObjectのプロパティを変更したとき(たとえは this.messageClasses.hidemsg = true
のように変更する)はスタイルが反映されず、Object全体を変更したとき(たとえば this.messageClasses = { hidemsg: true }
)にスタイルが反映されました。このへんの挙動はまだよく理解できてないかもですが、めちゃめちゃハマってしまった。。。今回はメッセージの表示・非表示切り替えに classMap
をつかってみています。
render()
で返すテンプレートの @input
は oninput
の意で、入力内容が変更されたら呼び出されるイベントです。イベント系は基本的に @hoge
のように表記すればよいようです。(もちろん addEventListener
とかもつかえる)
入力値を捕捉して、指定された正規表現 ( regexp
属性)を満たさない場合はメッセージ ( message
属性)を表示します。メッセージの表示・非表示は @input
イベントを捕捉して hidemsg
クラスをあてるかどうかで制御していますが、メッセージ表示のクラスの適用を管理している this.messageClasses
は変更されても何も起きないので、じぶんで this.requestUpdate();
を呼び出して描画に反映させる必要があります。
<!-- sample/index.html --> <!DOCTYPE html> <html> <head> <title>Sample page</title> <meta charset="utf-8"> </head> <body> <sample-textbox regexp="^\d+$", message="数字をいれてね"></sample-textbox> <script src="../src/sample-textbox.js" type="module"></script> </body> </html>
こんなかんじにつくったカスタム要素をつかってみます。数字にマッチしないときは「数字を入れてね」と表示されるはず。
それっぽくなった!また、input text
にも(わかりづらいですが)ちゃんとスタイルが当たっています。
まとめ
とりあえずいろいろさわってみたけど、もうちょっとさわってみたい~となっています。ではでは~。
JavaScriptで書いたあたり判定処理をnpmモジュールとして公開してみました
みんなでつくるダンジョンでつかわれているあたり判定処理は、実は自前で書いていたりします。 アルゴリズムは以下の記事を多分に参考にさせていただいています。
で、インターネットに公開されている記事を参考に作ったものだし、ソースコードもなるべく誰でも見られるようにしたいなという気持ちで、あたり判定処理まわりを切り出してGithubで公開することにしました。できたてほやほやのリポジトリですが、一応1年くらい手を入れながら使っているプログラムです。 (READMEとかは、英語できないのに無理して書いたふしがあるので、だいぶはちゃめちゃな感じになっていそうですが...)
動作サンプルは以下の通り。(これもとりあえず即席で作ったのでだいぶ雑感はありますが、なんとなくできることがわかるかな...という程度のつくりにはなっているとおもいます。なっていなかったらPull Requestください...)
切り離したライブラリについては、今後はGithubの公開リポジトリで管理しつつ、npmコマンドでインストールしたいなという気持ちになり、モジュールを公開してみることにしました。
以下のコマンドでお使いのプロジェクトにインストールできます。
npm install --save @piyoppi/colision-detector
こんなかんじで、公開できそうなものはなるべく自分のプロダクトから切り離して運用できたらいいなという気持ちなのでした。 ではでは~
作ったものまとめウェブページを新しくしつつGithub Pagesで公開しました
こんにちは。表題の通り、がらくたツールボックスのウェブページをあたらしくしました。
ちなみにGithub Pagesで公開しています。ソースコードはこちら。
Github PagesはリポジトリにPushしたら勝手にページが公開されるのでべんり。しかもオープンソースなら、独自ドメインも無料であてられます。
というわけで、上記の公式ドキュメントの通りに設定しました。今回は garakuta-toolbox.com
のドメインを当てたいので、ドキュメントに従ってDNSサーバの Aレコードを設定 しています。
なんであたらしくしたの?
以前の作品まとめページ は、凝った作りにはしたものの、このご時世になんとスマートフォン対応していなかったのでした。
この間 Picotachiで発表した ときに、「わたしはこういうものをつくっているのですよ~」とすぐに言える状態にしておくのは大事だなと思いつつ、そういう状況のときには大体スマホでページを見せながら紹介したいわけで、そうなると「スマホで見られない」という点がだいぶもったいないなという気持ちになりました。
というわけで、今回は凝りすぎず、いろいろなデバイスで表示できるようなページを作ろうということで新しくしました。 ページも更新しやすくなった分、なにかつくったらここにこまめに上げていきたいな~と思っています。
ではでは~
Vue.jsのコンポーネントをWebComponents化してみる
きょうはVue.jsのコンポーネントをWebComponents化してみたよという話をば。
どうやって
WebComponents化しておけば、カスタム要素とJSをひとつ置けば、かんたんに任意のVue.js製コンポーネントを配置できるので便利そう~ということでお試ししてみました。
コンポーネント化には「vue-web-component-wrapper」をつかいます。 github.com
まずはインストールをば。
npm install --save-dev @vue/web-component-wrapper
あとは、コンポーネント化したいものを wrap
して、カスタム要素を登録すれば完了です。
import Vue from 'vue' import Player from 'src/player/player.vue' import wrap from '@vue/web-component-wrapper' const CustomElement = wrap(Vue, Player) window.customElements.define('map-player', CustomElement)
これで、HTMLを書いてゆけば、任意のVue.jsなコンポーネントを気軽に置くことができました。Props
もちゃんとカスタム要素の属性として記述できます。べんり。
<map-player mapid="1"></map-player> <!-- スクリプトはDOMが出来上がったら読まれるようにする --> <!-- ref: https://developer.mozilla.org/ja/docs/Web/Web_Components/Using_custom_elements --> <script src="/player.bundle.js"></script>
とおもいきや、なんだか思ったようにスタイルが当たらない...。これは、 vue-style-loader
が head
要素の中にビルドしたスタイルを差し込むためのようなので(WebComponents配下はShadow DOMでスタイルが分離される)、以下のようにloaderを設定し、WebComponentsのShadowDOM配下にスタイルタグが差し込まれるようにします。(Webpackでビルドされる前提)
/* webpack.config.js */ { // ... 省略... { test: /\.vue$/, loader: 'vue-loader', options: { shadowMode: true }, } { test: /\.css$/, use: [ { loader: 'vue-style-loader', options: { shadowMode: true } }, 'css-loader', ] } }
めでたしめでたし。
(ちなみに、「vue-web-component-wrapper」をつかわずに、 HTMLElement
を継承したクラスでVueインスタンスを作成し、window.customElements.define
する方法も試してみましたが、どうもスタイルをShadowDOMに入れ込む周りで苦戦して、とりあえずあきらめたのでした。)
なんのために
わたしのポートフォリオサイトをつくりたい(正確にはつくりなおしたい)と思い、せっかくなので「みんなでつくるダンジョン」のマップをポートフォリオサイトに埋め込みたいなと思ったのでした。そこで、Vue.js製マップ表示+たんけんコンポーネントをWebComponents化すれば、気軽にマップを張り付けられるじゃん~と思いついてやってみたのでした。
ではでは~。
わくわくアバター作成日記リターンズ
ひさびさに開発っぽくない最近のあれこれをばをば。
今年の2~3月ごろに、わたしの3Dモデルが欲しいなとふと思い、アバター作成をしていた時期がありました。
garakuta-toolbox.hatenablog.com garakuta-toolbox.hatenablog.com
一応それっぽいアバターは出来たのですが、以下のような気に入らなさポイントがありました。
- 等身が思っていたより小さい...
- 画面上で見ると「おお~いいじゃん」感を(少なくとも私は)感じていたのですが、ヘッドマウントディスプレイをつけてVR空間で見ると、明らかに頭が大きく感じる
- まるで着ぐるみを着ているかのよう...
- 一部のディティールがふわふわしている
- 手とか、足とか、あごまわりとか
- 服を体が貫通しちゃう問題
- 鬼の調整業によってだいぶマシにはなったものの、どうしてもポーズによっては皮膚がはみ出ちゃう
特に、等身が気に入らないのは致命的です。第一印象が完全に「着ぐるみ」になってしまうので...。
敗因はおそらく、キャラクターの設定不足なのだろうなという反省をしました。当時はとにかくさっさと3Dモデルづくりを試してみたかったということで、半ば見切り発車的にモデルを作っていましたが、これが失敗の原因だと感じています。
最近「やっぱり自身がウェブ上に存在するなら3Dモデルはほしいよな~」という気持ちに再びなったので、今度はキャラクターをある程度詰めてモデリングに挑もう、ということで、わくわくアバター作成日記を再開したいなと思っています。
蛇足ですが、いままでは一般的なお絵かきソフトと慣れないペンタブをつかって設定画を描いていましたが、私の絵心ではどうもうまくいかないようなので、ドロー系ソフト(ベクタ画像をつくるソフト)でお絵かきしてみた結果が上のものです。少なくとも今のところは、「はじめからこれをつかっておけばよかった...」という程度には作業しやすいです。線の微修正なども、ベジェ曲線のハンドルをちょっといじるだけで簡単にできたりします。
(ちなみにわたしは Inkscape というドローソフトを好んで使っています)
ではでは~。ほかのことをしながらゆっくり進めるので、3Dアバターになるのは年末ごろになるかも....。
「みんなでつくるダンジョン」開発中のチャット機能をおためししてみたよ
チャットをお試ししてみたよ!
先日、開発中の「みんなでつくるダンジョン」のチャット機能を時間限定で公開してみました。(チャット機能の概要については 前回の記事 をご覧ください)
以下のようなツイートをして、わたしがマップに待機していたところ、数名の方に来ていただいたのでした。(既にチャット機能は無効になっているので悪しからず。)
ちょっとおためしで、マップでおしゃべりできる機能を公開してみます!ぜひログインした状態でマップに来てみてください~(1時間ちょっとくらいで非公開にします)
— ぴよっぴ (@piyorinpa) October 13, 2019
マップ「おしゃべりマップ」であそんでみよう! https://t.co/3A7d3NX3HL #みんなでつくるダンジョン
チャットな様子はこんなかんじ。ふきだしは5秒ほどで消えてしまうために、他の方とおしゃべりしている様子なスクリーンショットを撮るのが大変で、わたしがしゃべっている様子しか撮れなかった...。
数名の方に来ていただいて、以下のような課題が発見できました。
- ふきだしが数秒で消えてしまうので、ちょっと目をそらすと話の内容が分からなくなる(ログが必要そう)
- アバターの大きさが人それぞれなので、マップの作りによっては他の人が通れない場所が出そう
ちなみに、スマートフォンでもおしゃべりできるように、以下のようなUIを調整中です。(スマホは画面の大きさが限られるので、毎度悩まされる..。)
今後の予定
今後は以下のような調整をしていこうかな~と思っています。
絵文字をかんたんにつぶやけるようにしたい
なるべく「会話しなければならない」みたいなプレッシャーを感じてほしくないので、絵文字をかんたんにつぶやけるといいのかな?と思っています。私自身、最近でこそ慣れてきたものの、インターネット越しのコミュニケーションに苦手意識がある時期もあったので、そういう方にも気軽に入っていただけるような仕組みを取り入れたいのでした。(あと、絵文字はかわいいので🐤🌳🍣...というのもある。)
チャット機能をどのように提供するか
いつでも、どのマップでもチャットっぽいことができるのが理想かもしれませんが、サーバのリソースや管理まわり的になかなか難しそうです。現状はとりあえずつくってみた、という感じですが、どのように提供していくかを考えねば~という状況なのです。また、マップの作者が「チャット機能を提供したくない」という状況も考えられそうなので、その点も考慮したいなと思っています。
今後も不定期にテスト公開をしたい
今後もツイートを通じた告知で、こんなかんじのテスト公開をしたいなと考えています。事前告知するかどうかはわかりませんが、開催の際にはTwitterにてつぶやくので、興味がある方はアカウント登録をしてツイートをたまにちらちらとチェックしていただけるとです。
ではでは~!
マップにいるほかのユーザーとおはなしできるようにしてみている
きょうもきょうとて作業報告をばをば。前回記事 にひきつづき、マップでわいわいできる機能を引き続き作っています。
ただいま「おなじマップにいる他のユーザーとおはなしできる」機能をつくってみています。文章で説明するよりも動画をご覧いただいたほうが説明がはやいので、以下の動画をみてみてください。
いわゆるチャット機能ですね。チャット機能は実装するか否かだいぶなやみました。みんなかならずしも平和にチャットしてくれるとは限らないので、そういう点での管理コストとかどうなんだろう~とか、そもそもチャット機能を嫌う人もいるかな~とか、いろいろ思うところがあったのです。
ただ、技術的に機能の実装を試したかったことや、いろいろなマップを渡り歩けるというサービスの特性から、ほかのひととコミュニケーションがとれてもおもしろいかも、と思ったことなどなどから、ひとまず実装してみることにしました。
はじめはとあるマップ限定で、かつ時間も限定して私がマップで待機しながら様子をみて動かしてみる...ということを本番環境下でお試しできるようにすることを目標に開発を続けています。将来的には、マップの作者が自身のマップ上でのチャット機能のON/OFFを設定できるようにしたいな~と思っています。
ではでは~