Onsen UIの見た目は、CSSコンポーネントによって定義されています。Onsen UIを利用する時、次のCSSを必ず読み込んでいると思いますが、これがOnsen UIのCSSコンポーネントです。
<link rel="stylesheet" type="text/css" href="path/to/onsen-css-components.css">
Onsen UIのデフォルトの見た目を変更するには、このCSSコンポーネントをカスタマイズして下さい。Onsen UIのCSSコンポーネントは、SassやLessのような独自のCSSメタ言語ではなく、標準化されている文法を持つcssnextによって記述されているのでCSSさえわかっていれば誰でもカスタマイズすることができます。
このページでは、CSSコンポーネントのカスタマイズ方法について紹介します。
CSSコンポーネントのソースコードは、複数のCSSファイルによって構成されています。これらのCSSファイルは、ビルドすることで一つのCSSファイル(onsen-css-components.css
)にすることができます。ここではまずビルドするためのセットアップの手順を紹介します。
まず、npmを使ってOnsen UIのパッケージをインストールします。プロジェクトのディレクトリにnpmのパッケージファイル(package.json)がない場合には、npm init
を実行してパッケージファイルを作成して下さい。
$ npm init # package.jsonが無い場合
次にonsenui
パッケージをインストールします。onsenui
パッケージのバージョンは自身が利用しているOnsen UIのバージョンに合わせて下さい。
$ npm install onsenui
$ npm install onsenui@2.7.0 # バージョンを指定する場合
onsenui
パッケージがnode_modules
ディレクトリにインストールされるので、パッケージのcss-components
ディレクトリに移動します。
$ cd node_modules/onsenui/css-components-src
次にyarnを使って依存するパッケージをインストールします。yarn
をインストールしていない場合は、yarnのインストール手順を参照して下さい。
$ yarn install --pure-lockfile
無事依存するパッケージを解決できたら、次のコマンドを実行してプレビュワーを起動して下さい。
$ yarn run serve
$ npm run serve # もしくはこちら
もし利用するOnsen UIのパッケージのバージョンがv2.7.0よりも前の場合には、yarn run serve
ではなくgulp serve
を実行して下さい。
$ npm install -g gulp
$ gulp serve
するとビルドが始まり、成功すると次のようなメッセージが表示されます:
...
[15:37:02] Finished 'build' after 5.25 s
[15:37:02] Starting 'serve'...
Access URLs:
Local: http://localhost:4321/
External: http://(IP Address):4321/
Built CSS Files:
./build/onsen-css-components.css
http://localhost:4321/
をウェブブラウザで開くとCSSコンポーネントのプレビュー用のUIが表示されます。
モバイル端末でプレビューを確認するには、プレビューを動かしているPCとモバイルを同じネットワークに接続した上でモバイル端末のウェブブラウザでコマンドラインに表示されたhttp://(IP Address):4321/
を開いて下さい。
プレビューを起動するのではなく、単にCSSコンポーネントを一度だけビルドする場合には次のコマンドを実行して下さい:
$ yarn run build
$ npm run build # もしくはこちら
先程のyarn run serve
コマンドを実行している状態でCSSファイルを編集すると、CSSファイルが自動的にビルドされ、プレビューに新しくビルドされたCSSがインジェクトされて即座に反映されます。開発者がCSSコンポーネントをカスタマイズする場合には、この状態でコンポーネントの見た目を確認しつつ、個別のCSSファイルを編集していくことになります。
css-componentsディレクトリ以下は次のようなディレクトリ構造になります:
css-components
├── build
├── misc
├── node_modules
├── previewer-src
└── src
├── components
└── img
src/components
ディレクトリ以下に、Onsen CSS Componentsを構成するCSSファイルなどが収められています。
プレビューを起動した状態で、試しにSwtichコンポーネントのCSSファイル(src/components/switch.css
)を編集してみましょう。するとyarn run serve
を実行しているコマンドラインでは次のようなメッセージが表示され、CSSが自動的にビルドされプレビューにも反映されます。
[17:07:44] Starting 'css-clean'...
[17:07:44] Finished 'css-clean' after 7.94 ms
[17:07:44] Starting 'stylelint'...
[17:07:45] Finished 'stylelint' after 1.33 s
[17:07:45] Starting 'cssnext'...
[17:07:45] Finished 'cssnext' after 257 ms
[17:07:45] Starting 'cssmin'...
[17:07:46] Finished 'cssmin' after 453 ms
[17:07:46] Starting 'build-css'...
[17:07:46] Finished 'build-css' after 8.11 μs
[17:07:46] Starting 'generate-preview'...
[17:07:46] Finished 'generate-preview' after 156 ms
Access URLs:
Local: http://localhost:4321/
External: http://192.168.100.100:4321/
Built CSS Files:
./build/onsen-css-components.css
もしCSSの文法が間違っている場合にはコマンドライン上にエラーが表示されます。
カスタマイズしたCSSコンポーネントを自身のプロジェクトで利用する場合には、./build/onsen-css-components.css
のファイルを自身のプロジェクトのフォルダ内にコピーして利用して下さい。
src/theme.css
には、CSSコンポーネント内で利用する色の変数(CSS Variables)が定義されています。
:root {
--background-color: #efeff4;
--text-color: #1f1f21;
--sub-text-color: #999;
--highlight-color: #0076ff;
--second-highlight-color: #25a6d9;
--border-color: #ccc;
...
この変数の定義を変更することで、CSSコンポーネントで利用する色を変更できます。Material Design用のコンポーネントで利用している変数は、変数名にmaterial
プレフィクスがついています。iOS用のコンポーネントで利用している変数には、プレフィクスは付きません。
:root {
(...)
--material-notification-background-color: #e91e63;
--material-switch-active-thumb-color: #009688;
--material-switch-inactive-thumb-color: #f1f1f1;
--material-switch-active-background-color: #77c2bb;
--material-switch-inactive-background-color: #b0afaf;
--material-range-track-color: #bdbdbd;
初めてCSSコンポーネントのソースを見る人は、CSSのクラス名が醜くでたらめな命名規則によって記述されているように見えるかもしれません。
例えば、src/components/switch.css
を見てみましょう。このCSSファイルに記述されているSwitchコンポーネントのクラス名には、--
や__
によって区切られた冗長に見えるクラス名を使っていることがわかります。
.switch
.switch__toggle
.switch__input
.switch__handle
.switch--active__handle
.switch--material__toggle
.switch--material__input
しかし安心して下さい、Onsen UIのCSSコンポーネントは設計を堅牢にするために、かつ高速なCSSセレクタの記述をするために、設計規約と命名規則にBEMとMindBEMdingを採用しています。
BEMでは、クラスはBlock, Element, Modifierの三つの構成要素によって記述されます。Blockは、ある独立するグループの境界を宣言します。ElementはあるBlockの配下の要素を表現します。ModifierやBlockやElementを修飾するのに用います。
次のSwitchコンポーネントを例に説明します。
<label class="switch">
<input type="checkbox" class="switch__input">
<div class="switch__toggle">
<div class="switch__handle"></div>
</div>
</label>
まずいちばん外側の要素では、クラス属性にswitch
が宣言されています。その下にはswitch__input
やswitch__toggle
やswitch__handle
が配下の要素のクラス属性で宣言されているのがわかります。ここでは、switch
がBlockで、switch__input
やswitch__toggle
やswitch__handle
がElementを表現しています。MindBEMdingでは、Block名とElement名の区切りには__
を用います。
この設計規約について詳しく知りたい場合は、BEMとMindBEMdingの解説を参照して下さい。
個別のCSSコンポーネントのプレビューは、CSS内に埋め込まれたアノテーションに基づいて生成されています。例えば、Switchコンポーネントのプレビューは、次のような画面になります:
このプレビューに用いられているHTMLのコードは、src/components/switch.css
に埋め込まれている次のアノテーションで定義されています:
/*~
name: Switch
category: Switch
elements: ons-switch
markup: |
<label class="switch">
<input type="checkbox" class="switch__input">
<div class="switch__toggle">
<div class="switch__handle"></div>
</div>
</label>
<label class="switch">
<input type="checkbox" class="switch__input" checked>
<div class="switch__toggle">
<div class="switch__handle"></div>
</div>
</label>
<label class="switch">
<input type="checkbox" class="switch__input" disabled>
<div class="switch__toggle">
<div class="switch__handle"></div>
</div>
</label>
*/
アノテーションはCSSのコメント内部にYAMLで埋め込まれています。もしCSSコンポーネントのプレビューで用いるHTMLを変更したり、プレビューを増やしたい場合にはアノテーションを編集したり新たに埋め込むことができます。
yarn run serve
を実行している状態であれば、このアノテーションの変更も即座にプレビューUIに反映されます。
Patternsページでは、幾つかのCSSコンポーネントを組み合わせた画面の表示を確認することができます。テーマをカスタマイズする際にこのページを見ながら調整すると便利です。ここではこのパターンのカスタマイズ方法について説明します。
パターンとして表示されるHTMLは、css-components/patterns.yaml
ファイル内に記述されています。
---
name: Basic
markup: |
<div class="page">
<div class="toolbar">
<div class="toolbar__left"><span class="toolbar-button">Label</span></div>
<div class="toolbar__center">Title</div>
...
Patternsページで表示されるパターンのHTMLを修正したい場合には、このpatterns.yaml
を編集して下さい。プレビューを起動しているときにこのpatterns.yaml
を編集すると、CSSファイルに変更を加えたときと同様に自動的に反映されます。
Onsen UIのデフォルトのテーマをカスタマイズするのではなく、別のテーマを追加する方法を説明します。
src/(任意の単語)-onsen-css-components.css
というファイルを追加して下さい。追加したCSSファイルはビルドのエントリーポイントのひとつになります:
@import url('./license.css');
@import url('./yet-another-theme.css');
@import url('./components/index.css');
さらにsrc/yet-another-theme.css
ファイルを作成します。内容はsrc/theme.css
ファイルからコピーしてください。
yarn run serve
でプレビューを起動してビルドが完了すると、次のように表示されます。build
ディレクトリにyet-another-onsen-css-components.css
がビルドされていることがわかります。
Built CSS Files:
./build/dark-onsen-css-components.css
./build/onsen-css-components.css
./build/yet-another-onsen-css-components.css
プレビュー画面でも追加したテーマを選択できるようになります。
yet-another
テーマが無事ビルドできることを確認できれば、あとは先程作成したsrc/yet-another-theme.css
を開いてCSS変数を編集することで追加したテーマの色をカスタマイズすることができます。
modifier(モディファイアー)は、Onsen UIのコンポーネントにクロスプラットフォーム性とカスタマイズ性を与える独自のHTML属性です。modifierはCSSクラスによって定義されています。Onsen UIでは、modifierを作成してコンポーネントに適用することでコンポーネントのスタイルをカスタマイズすることができます。いくつかのコンポーネントはすぐに使えるプリセットmodifierを持っています。
例えば、以下のボタン群はmodifierを使うことによりそれぞれ別の見た目になっています。なお、動的にmodifierを変更するには、modifier
属性をJavaScriptで変更してください。
<ons-button modifier="quiet">Quiet</ons-button>
<ons-button modifier="light">Light</ons-button>
<ons-button modifier="large">Large</ons-button>
<ons-button modifier="cta">Call To Action</ons-button>
<ons-button modifier="material">Material Design</ons-button>
より詳しい情報については、このブログ記事(英語)をご覧ください。
Onsen UIのコンポーネントは、アプリが動作しているプラットフォームに応じて自動的にスタイルが変化します。この機能はブラウザの開発者ツール(Dev Tools)でiOSとAndroidのビューを切り替えることで簡単にテストできます。もしくは、URLに?platform=ios
や?platform=android
をクエリ文字列として加えることでもテストできます。
オートスタイルは、ons.platform.isAndroid()
がtrue
の時にmodifier="material"
をコンポーネントに付加します。オートスタイルは、onsenui.js
の読み込み直後(つまり、アプリの初期化の直前)にons.disableAutoStyling()
を実行することで無効にできます。
オートスタイルをより細かく制御できるように、いくつかのツールが提供されています。
ons.platform
オブジェクトを使うと、ons.platform.isIOS()
やons.platform.isWebView()
などのメソッドが利用できます。
ons.platform.select('android')
でプラットフォームをAndroidに固定すると、全てのプラットフォームでマテリアルデザインのコンポーネントを強制的に表示することができます。ただし、このメソッドはアプリが初期化される直前(つまりonsenui.js
を読み込んだ直後)に呼び出す必要があります。
ons-if
という名前の条件要素を使うと、プラットフォームや画面の向きに応じて表示するコンテンツを変えることができます。
なお、ReactバインディングとVueバインディングでは<ons-if>
コンポーネントは利用できませんので、ons.platform.isIOS()
などをご利用ください。
<ons-if platform="android">
Androidでのみ表示されるコンテンツ
</ons-if>
<ons-if platform="ios other">
Android以外でのみ表示されるコンテンツ
</ons-if>
これを使うと、例えば、Androidにだけons-fab
を表示したり、iOSにだけボタンを表示したりすることができます。
AngularJS 1バインディングではons-if-platform
というディレクティブも提供しています:
<div ons-if-platform="android">
Androidのみで表示されるコンテンツ
</div>
ons-icon
はオートスタイルをより簡単に活用するためのショートカットを提供しています:
<ons-icon icon="ion-navicon, material:md-menu" size="24px, material:20px"></ons-icon>
md-menu
はmaterial
modifierが付加されている時のみ表示されます。なお、material
以外のmodifierでも同様のことができます。
Onsen UIのルーティングコンポーネント(ナビゲーター、スプリッター、タブバー等)やダイアログには、いくつかのビルトインアニメーションが用意されています。しかし、自分でカスタムアニメーションを作ることもできます。また、ビルトインアニメーションを拡張したり、改造したりすることもできます。しかし、アニメーションの自作や拡張、改造についてはOnsen UIのコアコードに深入りする必要があり、比較的複雑なため注意してください。
Onsen UIはAnimitという小さなアニメーションライブラリに依存しています。AnimitはモバイルブラウザでのCSSトランジションを制御するライブラリです。
Animitにはons.animit
またはimport { animit } from ons;
でアクセスすることができます。Animitは以下のように、CSSアニメーションをキューしたり、ディレイを適用したり、コールバック関数を呼んだりするためのメソッドを提供しています:
let animation1 = animit(myElement) // 与えられた要素に対するアニメーションを定義
.saveStyle() // 要素が元々持っていたスタイルを保存
.queue({ // アニメーションの初期状態(位置・スタイル)を設定
css: {
transform: 'translate3D(0, 100%, 0)'
},
duration: 0
})
.wait(0.2) // トランジション開始前にディレイ(遅延)を適用
.queue({ // アニメーションの次の状態を設定
css: {
transform: 'translate3D(0, 0, 0)',
},
duration: 0.6,
timing: 'linear'
})
.restoreStyle() // 要素が元々持っていたスタイルを復元
.queue(done => { // トランジション終了時にコールバック関数を呼ぶ(オプション)
callback();
done();
}
);
animation1.play(); // アニメーションを再生
Animitは要素のstyle
プロパティを操作するので、以前のスタイルを失ってしまわないようにするためにsaveStyle()
とrestoreStyle()
の2つのメソッドを提供しています。queue({css: {...}, duration: 0, timing: 'linear'})
またはqueue({...}, {duration: 0, timing: 'linear'})
メソッドはアニメーションのキューにトランジションを追加します。アニメーションを再生すると、まずキューの先頭のトランジションが適用され、その後キューの残りのトランジションが適用されていきます。上述のサンプルコードでは、新しいページを右から左に向かってビュー内に移動させています。translate3d(0, 100%, 0)
という位置から始まってtranslate3d(0, 0, 0)
という位置に移動しているのはそのためです。wait(...)
メソッドはトランジション間のディレイ(遅延)を入れるのに利用できます。最後にqueue(function(done) { ...; done(); })
メソッドについてですが、これは必要に応じてコールバック関数を呼び出すのに利用できます。
もし同じアニメーションを複数の要素に適用する必要があるときは、animit([el1, el2]).saveStyle()...
のように、animit
オブジェクトにHTML要素の配列を渡すことも可能です。
また、2つ以上のアニメーションを同時に再生したい場合は、animation1.play(); animation2.play(); animation3.play();
とする代わりにanimit.runAll(animation1, animation2, animation3);
メソッドが利用できますので、活用してください。
アニメーターはOnsen UIが提供しているアニメータークラスを拡張することで一から作成することができます。Onsen UIの各コンポーネントはNavigatorAnimator
とAlertDialogAnimator
、DialogAnimator
、PopoverAnimator
、ModalAnimator
、TabbarAnimator
、SplitterAnimator
のようなアニメーターインターフェースを公開(expose)しています。これらを拡張・実装することでアニメーターを作成することができます。もしあなたがOnsen UIを改造したり、Onsen UIのリポジトリにプルリクエストを送ってみたい場合には、この方法でアニメーターを実装するのが最も理想的です。詳しくは、各コンポーネントの既存のアニメーターのソースコードをご覧ください。
アニメーターを作成する際はES2015 (ES6)を利用するのが推奨されますが、特にこだわる必要はありません。ES5でのサンプルコードについてはこちらをご覧ください。
新しいアニメーターを作成する方法には、既存のアニメーターを拡張して挙動の一部または全部を修正するという方法もあります。もし、ただ単に既存のアニメーターの見た目やタイミングを少し修正したいだけならこちらの方法の方が簡単です。完全に新しいアニメーターを作る場合でも、こちらの方法の方が簡単です。既存のアニメーターを拡張するには、既存のアニメーターのソースコードのどれかを1つ選んで一通り目を通し、プロパティとメソッドを確認することがまず必要です。アニメーターの具体的な仕様についてですが、どのアニメーターも、新しいアニメーターを返すextend({...})
クラスメソッドを提供しています。各コンポーネントの既存のアニメーターはons.elements.Navigator.animators
やons.elements.AlertDialog.animators
などの形で公開(expose)されています。これらのオブジェクトはコンポーネントに登録済みの全てのアニメーターを含んでおり、以下のように拡張できます。
var fadeIOS = ons.elements.Navigator.animators['fade-ios'];
var customAnimator = fadeIOS.extend({
timing: 'cubic-bezier(.1, .7, .1, 1)',
delay: 0.1,
push: function(enterPage, leavePage, callback) {
ons.animit.runAll(
ons.animit(enterPage)...,
ons.animit(leavePage)...
);
}
});
// 以下の処理は任意です
ons.elements.Navigator.registerAnimator('customAnimationName', customAnimator);
上記のサンプルコードでは、push
アニメーションを上書きしています。pop
アニメーションは既存のものがそのまま使用されます。 ここで、timing
とdelay
プロパティはpush
アニメーションとpop
のアニメーションの両方に影響しています。いくつかのアニメーターはbackgroundMask
のような追加プロパティを持っています。使用可能な追加プロパティを確認するには、拡張したいアニメーターのソースコードを確認してください。
新しいアニメーターを作成して登録すると、myNavigator.pushPage('page.html', {animation: 'customAnimationName'})
のように、カスタムアニメーションを登録時のアニメーション名だけで簡単に参照できるようになります。<ons-navigator animation="customAnimationName">
のように、カスタムアニメーションをナビゲーターのデフォルトのアニメーションにするというようなことも可能です。これは他のコンポーネントについても同様です。