tag:crieit.net,2005:https://crieit.net/users/studioTeaTwo/feed studioTeaTwoの投稿 - Crieit CrieitでユーザーstudioTeaTwoによる最近の投稿 2020-04-13T09:08:52+09:00 https://crieit.net/users/studioTeaTwo/feed tag:crieit.net,2005:PublicArticle/15823 2020-04-12T23:40:26+09:00 2020-04-13T09:08:52+09:00 https://crieit.net/posts/A-Frame-5e93285a259c3 A-Frameの日本語フォントを用意する <h1 id="はじめに"><a href="#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">はじめに</a></h1> <p>現在、A-Frameには標準で日本語フォントがありません。<code><a-text></code>タグのvalueに日本語を記述しても文字化けします。自前でMSDF(multi-channel signed distance fonts)形式のフォントファイルを用意して、<code><a-text></code>タグのfont属性やfont-image属性にセットする必要があります。</p> <p>MSDFは、ttf(TrueTypeFont)などから作成しますが、日本語フォントは巨大なこともあり、けっこうめんどくさいです。<br /> そこで作成済みのフォントをいくつか用意して公開しました。</p> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/studioTeaTwo/aframe-japanese-font">aframe-japanese-font</a></p> <p>このnpmパッケージを解説します。</p> <h1 id="アプローチ"><a href="#%E3%82%A2%E3%83%97%E3%83%AD%E3%83%BC%E3%83%81">アプローチ</a></h1> <h2 id="MSDFの生成方法"><a href="#MSDF%E3%81%AE%E7%94%9F%E6%88%90%E6%96%B9%E6%B3%95">MSDFの生成方法</a></h2> <p>MSDFを生成する<a target="_blank" rel="nofollow noopener" href="https://msdf-bmfont.donmccurdy.com/">オンラインのジェネレーター</a>がありますが、日本語は巨大になります。漢字を一通りカバーしようとすると、エラーになります。</p> <p>そこで、コマンドラインツール版を使いローカルで実行します。</p> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/soimy/msdf-bmfont-xml">msdf-bmfont-xml</a></p> <h2 id="A-Frame専用に出力するために、msdf-bmfont-xmlのラッパーを用意"><a href="#A-Frame%E5%B0%82%E7%94%A8%E3%81%AB%E5%87%BA%E5%8A%9B%E3%81%99%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AB%E3%80%81msdf-bmfont-xml%E3%81%AE%E3%83%A9%E3%83%83%E3%83%91%E3%83%BC%E3%82%92%E7%94%A8%E6%84%8F">A-Frame専用に出力するために、msdf-bmfont-xmlのラッパーを用意</a></h2> <p>msdf-bmfont-xmlをカスタマイズして<a target="_blank" rel="nofollow noopener" href="https://github.com/studioTeaTwo/node-msdf-generator">node-msdf-generator</a>を作りました。<br /> 主にやってることです。</p> <ul> <li>pngファイルを1つにする(<code><a-text></code>がマルチパートをサポートしていないため)</li> <li>日本語種類を定数としてパラメータ化する(平仮名/片仮名/常用漢字/人名漢字)</li> </ul> <p>漢字は、<a target="_blank" rel="nofollow noopener" href="https://ja.wikipedia.org/wiki/常用漢字一覧">常用漢字(約2100文字)</a>と<a target="_blank" rel="nofollow noopener" href="https://ja.wikipedia.org/wiki/人名用漢字一覧">人名漢字(約860文字)</a>を用意しています。</p> <h2 id="日本語フォント種類"><a href="#%E6%97%A5%E6%9C%AC%E8%AA%9E%E3%83%95%E3%82%A9%E3%83%B3%E3%83%88%E7%A8%AE%E9%A1%9E">日本語フォント種類</a></h2> <p>webフォント から引っ張ってきました。今は3種類です。</p> <ol> <li><a target="_blank" rel="nofollow noopener" href="https://www.google.com/get/noto/#sans-jpan">Noto Sans(ゴシック体)</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://mplus-fonts.osdn.jp/about.html">M+ FONTS(ゴシック体)</a></li> <li><a target="_blank" rel="nofollow noopener" href="http://hp.vector.co.jp/authors/VA039499/#hui">ふい字(丸文字)</a></li> </ol> <ul> <li>すべてライセンスはクリアしている認識ですが、もし万が一不備などありましたらご指摘いただければ幸いです。</li> <li>NotoSansとふい字のttfで一部の文字が用意できていないっぽくて抜け落ちています。M+フォントは全て作成できています。</li> <li>抜け落ちた文字はこのログファイルで確認できるようにしています。<br /> <a target="_blank" rel="nofollow noopener" href="https://github.com/studioTeaTwo/aframe-japanese-font/blob/master/task/build.log">フォントのビルドログ</a></li> </ul> <h1 id="使い方"><a href="#%E4%BD%BF%E3%81%84%E6%96%B9">使い方</a></h1> <h2 id="実ファイルについて"><a href="#%E5%AE%9F%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6">実ファイルについて</a></h2> <p>いちおうnpmパッケージ化しているけど、msdfファイルはクソでかいです。なにせ3000文字以上のテクスチャファイルですから。</p> <p>npm publishした時のログです。</p> <pre><code>npm notice 435.2kB fonts/hui/hui-msdf.json npm notice 531.9kB fonts/mplus/mplus-msdf.json npm notice 432.7kB fonts/noto-sans-cjk-jp/noto-sans-cjk-jp-msdf.json npm notice 9.3MB fonts/hui/hui-msdf.png npm notice 7.2MB fonts/mplus/mplus-msdf.png npm notice 6.9MB fonts/noto-sans-cjk-jp/noto-sans-cjk-jp-msdf.png </code></pre> <h2 id="組み込み方法"><a href="#%E7%B5%84%E3%81%BF%E8%BE%BC%E3%81%BF%E6%96%B9%E6%B3%95">組み込み方法</a></h2> <p>そもそもnpmインストールしたところで、これらはアセットファイルです。jsファイルにrequire/importするようなものではありません。</p> <p>ですので、基本的には直接ダウンロードして公開フォルダに格納しましょう。必要なファイルは、<a target="_blank" rel="nofollow noopener" href="https://github.com/studioTeaTwo/aframe-japanese-font">aframe-japanese-font</a>の<code>fonts</code>ディレクトリ下に各フォント毎に置いている<code>[fontName]-msdf.json</code>と<code>[fontName]-msdf.png</code>の2つです。</p> <p>その2つのファイルをheadタグなどでインクルードします。A-Frameを通常のjsアプリに組み込んでいる場合は、A-Frameが必要になったところでフォントファイルもダイナミックインポートするといいでしょう。</p> <h2 id="描画"><a href="#%E6%8F%8F%E7%94%BB">描画</a></h2> <p>A-Frameの<code><a-text></code>タグのfont属性とfont-image属性に利用したいフォント種類をセットしてvalue属性にテキストを記載すれば、OKです。</p> <pre><code class="html"><a-scene> <a-text value="こんにちは世界!" font="noto-sans-cjk-jp-msdf.json" font-image="noto-sans-cjk-jp-msdf.png" negate="false" scale="2 2 1" position="0 1 -4" > </a-text> </a-scene> </code></pre> studioTeaTwo tag:crieit.net,2005:PublicArticle/15633 2019-12-22T23:20:13+09:00 2019-12-23T03:48:26+09:00 https://crieit.net/posts/ionic-angular-WebComponents @ionic/angularがWebComponentsでテンプレートチェックを効かせるためにやっていることを調べる <h1 id="AngularでWebComponetsライブラリを使う時の基本"><a href="#Angular%E3%81%A7WebComponets%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%82%92%E4%BD%BF%E3%81%86%E6%99%82%E3%81%AE%E5%9F%BA%E6%9C%AC">AngularでWebComponetsライブラリを使う時の基本</a></h1> <p>CustomElementsは基本的にはHTMLElementです(正確にはHTMLElementを継承して作ったもの)。<code>customElements.define()</code>することでグローバルに存在するようになるので、なんらかの方法でライブラリをインクルードすれば、あとはtemplateに書いておけば動きます。Angularから見れば、<code>input</code>タグでも<code>custom-input</code>タグでも同じ話なわけです。</p> <pre><code class="html"><input type="text"/> <custom-input type="text"></custom-input> </code></pre> <p>ただし、Angularのテンプレートチェックに<code><input></code>はわかるけど<code><custom-input></code>は知らないやつだなあと怒られるので対策が必要です。</p> <pre><code class="ts">@NgModule({ schemas: [CUSTOM_ELEMENTS_SCHEMA], ←templateチェックを無効にする }) export class SomeModule {} </code></pre> <p>これで自由にHTML要素を書いてもよくなりますが、テンプレートの事前チェックが無効化されて実行時の存在判定に後送りされるため、他のタイポなども道連れで検出できなくなります。</p> <pre><code class="html"><typo-input type="text"></typo-input> ←どこにも定義されてないがビルド時にエラーにならない </code></pre> <p>このようにAngularでWebComponetsを使うには<strong>誓約と制約</strong>があります。</p> <h1 id="@ionic/angularがやっていること"><a href="#%40ionic%2Fangular%E3%81%8C%E3%82%84%E3%81%A3%E3%81%A6%E3%81%84%E3%82%8B%E3%81%93%E3%81%A8">@ionic/angularがやっていること</a></h1> <p>さて、ionicのv4はWebComponentsベースで書き直され、Angularだけでなくreactやvueでもできるようになりましたが、Angularは引き続きテンプレートチェックがビシッと効いています。</p> <p>不思議なので中身を調べてみましょう。</p> <p>なんと、同じ名称のAngularComponentが定義されていました❗️</p> <p>ソースを読むとプロキシという扱いのようなので、これをプロキシコンポーネントと呼びます。<br /> どうやらこいつがビルド時にAngularに差し出されているから、テンプレートの型チェックが効いてるようです。</p> <p><code><ion-input></code>を例に引っ張ってきました。現バージョンだと<code>@ProxyCmp</code>という新デコレータが作られていて、<code>proxyMethods</code>や<code>proxyInputs</code>の記述は省力化されていますが、構造がわかりやすいので旧スタイルで掲載しています。</p> <pre><code class="ts">export declare interface IonInput extends StencilComponents<'IonInput'> {} @Component({ selector: 'ion-input', changeDetection: ChangeDetectionStrategy.OnPush, template: '<ng-content></ng-content>', inputs: [ 'accept', 'autocapitalize', 'autocomplete', 'autocorrect', 'autofocus', 'clearInput', 'clearOnEdit', 'color', 'debounce', 'disabled', 'inputmode', 'max', 'maxlength', 'min', 'minlength', 'mode', 'multiple', 'name', 'pattern', 'placeholder', 'readonly', 'required', 'size', 'spellcheck', 'step', 'type', 'value', ], }) export class IonInput { ionInput!: EventEmitter<CustomEvent>; ionChange!: EventEmitter<CustomEvent>; ionBlur!: EventEmitter<CustomEvent>; ionFocus!: EventEmitter<CustomEvent>; protected el: HTMLElement; constructor(c: ChangeDetectorRef, r: ElementRef) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionInput', 'ionChange', 'ionBlur', 'ionFocus']); } } proxyMethods(IonInput, ['setFocus', 'getInputElement']); proxyInputs(IonInput, [ 'color', 'mode', 'accept', 'autocapitalize', 'autocomplete', 'autocorrect', 'autofocus', 'clearInput', 'clearOnEdit', 'debounce', 'disabled', 'inputmode', 'max', 'maxlength', 'min', 'minlength', 'multiple', 'name', 'pattern', 'placeholder', 'readonly', 'required', 'spellcheck', 'step', 'size', 'type', 'value', ]); </code></pre> <h2 id="全体構造"><a href="#%E5%85%A8%E4%BD%93%E6%A7%8B%E9%80%A0">全体構造</a></h2> <p>通常のAngularComponentに則ったデコレータとclassです。<code>proxyOutputs</code>、<code>proxyMethods</code>、<code>proxyInputs</code>はionicが用意しているユーティリティ関数です。</p> <h2 id="生のWebComponentsに対してバインドする方法"><a href="#%E7%94%9F%E3%81%AEWebComponents%E3%81%AB%E5%AF%BE%E3%81%97%E3%81%A6%E3%83%90%E3%82%A4%E3%83%B3%E3%83%89%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95">生のWebComponentsに対してバインドする方法</a></h2> <h3 id="接続"><a href="#%E6%8E%A5%E7%B6%9A">接続</a></h3> <p>コンポーネントデコレータのselectorをcustomElementと同じ名称にしています。相当ハックだと思ったのですが、Angularのコンポーネントはカスタム要素としてHTMLにそのまま出力するので、selectorをHTMLに存在している要素名にするとHTMLではHTMLに存在している要素として解釈するわけなんですよね。もちろん<code>_nghost-wke-c2</code>のような属性は付きます。良い子は真似するな案件ですが、customElementに限らず標準HTML要素の<code>input</code>とか<code>span</code>とかでも同じことできます。</p> <h3 id="プロキシ"><a href="#%E3%83%97%E3%83%AD%E3%82%AD%E3%82%B7">プロキシ</a></h3> <p>コンポーネントでは、elementRefから引っ張ったnativeElementの生DOM参照をクラスプロパティに保持しています。<br /> proxyInputsとproxyMethodsは、そのクラスプロパティに保持したnativeElementを通して生の<code>ionic-input</code>に対して、HTML属性のget/setやionic内部に用意されているメソッドを生やしています。</p> <pre><code class="ts">export const proxyInputs = (Cmp: any, inputs: string[]) => { const Prototype = Cmp.prototype; inputs.forEach(item => { Object.defineProperty(Prototype, item, { get() { return this.el[item]; }, set(val: any) { this.z.runOutsideAngular(() => (this.el[item] = val)); } }); }); }; export const proxyMethods = (Cmp: any, methods: string[]) => { const Prototype = Cmp.prototype; methods.forEach(methodName => { Prototype[methodName] = function () { const args = arguments; return this.z.runOutsideAngular(() => this.el[methodName].apply(this.el, args) ); }; }); }; </code></pre> <p>proxyOutputsは、rxjsの<code>fromEvent</code>(中でaddEventListenerやremoveEventListenerしながらObservable化するやつ)を、コンポーネントの各EventEmitter型のプロパティにセットしているものです。コンポーネントclassに同名のクラスプロパティが用意されています。</p> <pre><code class="ts">export const proxyOutputs = (instance: any, el: any, events: string[]) => { events.forEach(eventName => instance[eventName] = fromEvent(el, eventName)); } </code></pre> <h1 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h1> <p>プロキシするコンポーネントを用意することで、グローバルに存在しているWebComponentsライブラリでも、Angularのテンプレートの中でまるでAngularComponentかのように扱えるようになり、テンプレートの型チェックが効くようになっています。</p> studioTeaTwo tag:crieit.net,2005:PublicArticle/15079 2019-06-09T23:29:17+09:00 2019-06-10T04:54:16+09:00 https://crieit.net/posts/ionic-AngulaCLI ionicをAngulaCLIで使う <p>ionicは長らくAngularとcordovaを用いたUIフレームワークでしたが、最新バージョンではwebComponentsベースになり、reactでもvueでも生jsでもなんでもいけるようになりました。</p> <p>素晴らしいんですが、逆に構成パターンがめっちゃ増えて迷うというところもあります。<br /> Angularをベースにする上でもパッと3パターンがありえます。</p> <ol> <li>ionic CLIを用いる(従来通り)</li> <li>scriptタグで@ionic/coreをインクルードしてピュアWebComponentとして利用する</li> <li>angularCLIプロジェクトに@ionic/angularをインストールして利用する</li> </ol> <p>本記事では3番を取り上げます。普通にnpmPackageとして組み込むだけで、組み込んだ後はいつものngコマンドで開発します。</p> <hr /> <h1 id="インストール"><a href="#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">インストール</a></h1> <p>最初にインストールします。angularCLIで作成したプロジェクトの前提です。</p> <pre><code>$ npm install @ionic/angular </code></pre> <p><code>ng add @ionic/angular</code>ができそうな記述も見られますが、Anuglar v8ではダメです。さっとコードを見た限りだとangular.jsonのスキーマ変更をキャッチアップすればいけそうな感もありましたが、ionic v4はもはや別ライブラリでは?というくらい内部的には激しい刷新が行われているので焦らず待ちましょうという判断しました。</p> <p>ionicCLIでは<code>*.page.ts</code>とかの独自レイヤーがあるようですが、このやり方ではそういうのは考えない感じです。</p> <p>他にも<code>@ionic/angular-toolkit</code>というcordvaやschematicsのプラグインを提供しているパッケージがありますが、現時点では要らなそうです。cordvaはただのwebアプリなら要らないですし、schematicsは上述のようにもう少し待ちましょう判断です。もしかしたらng addだけでなく<code>*.page.ts</code>とかをng generateできるようになるのかな。</p> <h1 id="コンフィグレーション"><a href="#%E3%82%B3%E3%83%B3%E3%83%95%E3%82%A3%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3">コンフィグレーション</a></h1> <p>普通にNgModuleをimportすればOKです。WebComponentなので、Angularのtemplateコンパイラチェックを外すために<code>CUSTOM_ELEMENTS_SCHEMA</code>を加えます。</p> <p>app.module.ts</p> <pre><code class="typescript">import { BrowserModule } from '@angular/platform-browser'; import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { IonicModule } from '@ionic/angular'; import { AppComponent } from './app.component'; @NgModule({ declarations: [AppComponent], imports: [ BrowserModule, IonicModule.forRoot(), ], providers: [], bootstrap: [AppComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA], }) export class AppModule {} </code></pre> <p>ルートのcssファイルにスタイルをインポートします。著者はscssでAngularCLIを起こしていますが、それぞれ置き換えてください。</p> <pre><code class="scss">/* Core CSS required for Ionic components to work properly */ @import "~@ionic/angular/css/core.css"; /* Basic CSS for apps built with Ionic */ @import "~@ionic/angular/css/normalize.css"; @import "~@ionic/angular/css/structure.css"; @import "~@ionic/angular/css/typography.css"; /* Optional CSS utils that can be commented out */ @import "~@ionic/angular/css/padding.css"; @import "~@ionic/angular/css/float-elements.css"; @import "~@ionic/angular/css/text-alignment.css"; @import "~@ionic/angular/css/text-transformation.css"; @import "~@ionic/angular/css/flex-utils.css"; @import "~@ionic/angular/css/display.css"; </code></pre> <p>アイコンは若干トリックが要りました。参照解決してくれなかったのでassetsにコピーして利用しています。<br /> この辺りは、<code>ng add @ionic/angular</code>を実装している<a target="_blank" rel="nofollow noopener" href="https://github.com/ionic-team/ionic/blob/master/angular/src/schematics/add/index.ts">schematicsのコード</a>が大変参考になります。というか、ピックアップして手動でコピペします。</p> <p>angular.json</p> <pre><code class="json">{ "projects": { "architect": { "build": { "options": { "assets": [ "src/favicon.ico", "src/assets", { "glob": "**/*.svg", "input": "node_modules/ionicons/dist/ionicons/svg", "output": "./svg" } ], } } } } } </code></pre> <p>アイコンタグでは相対URLを記述します。</p> <pre><code class="html"><ion-item button routerLink="/home"> <ion-icon slot="start" src="/assets/svg/md-home.svg"></ion-icon> <ion-label> Home </ion-label> </ion-item> </code></pre> <p>以上となりますが、基本的にはschematicsの<a target="_blank" rel="nofollow noopener" href="https://github.com/ionic-team/ionic/blob/master/angular/src/schematics/add/index.ts"><code>ng add</code>のコード</a>を教科書にして構成するとよいです。<code>ng add @ionic/angular</code>コマンドそのものは動きませんが、リファレンスには最適です。</p> studioTeaTwo