SenchaTouch2

SenchaTouch2 Ext.Menu

More than 5 years have passed since last update.

近年のWebサイト開発では、かつてガラケーサイト開発がそうだったように、スマートフォン開発、タブレット開発が一般化してきました。「○○のような感じで」「○○のアプリにあるような」 こんな話しを要件定義の段階でよく耳にすることも増えました。スマートフォン開発では、画面をシンプルかつ、ユーザーにわかりやすい設計にすることが大事ですが、Webアプリを含め、多くのアプリが生まれた中で、ある程度ユーザーが認識してきた操作、というのもあります。左右のスワイプで、画像送りをしたり、画面上部でさらに下にひっぱることで、その画面全体の更新がかかる、など。 そうした意味でボタンの意味にも、共通の意識が芽生えてきました。その1つがメニューボタンです。menuicon.png

例えばこのようなボタンを見た時みなさんは、「メニューボタンだな」と認識できる人は多いのではないでしょうか。

「○○のような」という話しで言えば、「facebookアプリのような」メニューボタンです。

そして、このボタンをタップした時に、どのようにメニューが表示されるのか。

これも想像できる人は多いはずです。

先述のfacebookアプリでは、タップした時にメイン画面が右にスライドされるとともに、左からメニューがスライドしてきます。

Sencha Touch2.3.xでは、このようなメニューを簡単に実装できる機能が実装されました。

その名もExt.Menu

2.3.0がリリースされてからしばらくは、APIドキュメントにも記載されていなかったこの機能でしたが、

最近になって、記載されるようになりました。

使い方については、サンプルにあるKitchen Sink にExt.Menuのサンプルがあるので、

まずはこちらを見ていただくのが良いと思います。

Kitchen Sink ExampleのメニューにあるUserInterfaceから、さらに一番下にMenusがあるので、そこで確認ができます。

Ext.Viewport.toggleMenu(‘left’);などで、メニューを切り替えることができます。

そのためには、Ext.Viewport.setMenu()により、メニューを定義する必要があります。

Ext.Menuでは、このようなスライドメニューを簡単に作成できるようになりました。

しかし、みなさんが作るアプリケーションは、こんなに単純ではないかもしれません。

例えば、スライドで表示した画面が、メニューボタンだけとは限りません。

次のようなコードで、右のスライドメニューの中に、xtype:datepickerfieldがあったとしましょう。

Ext.define('MyApp.view.MyContainer', {

extend: 'Ext.Container',
alias: 'widget.my_container',
requires: [
'MyApp.view.MyTitleBar',
'Ext.Menu'
],
config: {
html: 'example',
items: [
{
xtype: 'mytitlebar'
}
]
},
initialize: function() {
this.callParent();
var me = this;
Ext.Viewport.setMenu(
Ext.create('Ext.Menu', {
width: 300,
items:[
{
xtype: 'button',
text: 'Close',
handler: function () {
Ext.Viewport.hideMenu('left');
}
}
]
}),
{
side: 'left',
reveal: true
}
);
Ext.Viewport.setMenu(
Ext.create('Ext.Menu', {
width: 300,
items: [
{
xtype: 'textfield',
label: 'text'
},
{
xtype: 'datepickerfield',
label: 'date'
}
]
}),
{
side: 'right',
reveal: true
}
);
}
});

すると、画面は下記のようになります。

menu1.png

右の歯車のアイコンをタップすると、下図になります。

menu2.png

ここに、datepickerfieldの箇所をタップすると、datepickerが現れるわけですが、、

menu3.png

このようにずれてしまいます。赤いしかくで囲った部分ですね。

こうなると、2つの対策を考えます。


  • datepickerの位置を無理やりずらす

  • Ext.Menuを諦める

datepickerの位置をずらす、という方法ですが、きっとやってできないことはないでしょうけど、

汎用性を考えると、ここはExt.Menuを使わずに、似たような動きをする作りにしてみます。

冗長な部分もあるかもしれませんが、あしからず・・・

まずは、app.js


app.js

Ext.application({

views: [
'Main',
'MyContainer',
'MyTitleBar',
'MyMenu'
],
name: 'MyApp',

launch: function() {

Ext.create('MyApp.view.Main', {fullscreen: true});
}

});


Main.jsは次のようにします。


Main.js

Ext.define('MyApp.view.Main', {

extend: 'Ext.Container',
xtype: 'main',
requires: [
'MyApp.view.MyTitleBar',
'MyApp.view.MyMenu'
],
config: {
items: [
{xtype: 'my_container'},
{xtype: 'my_menu'}
]
}
});

xtype: ‘my_container’はメインの表示となるものです。


MyContainer.js

Ext.define('MyApp.view.MyContainer', {

extend: 'Ext.Container',
alias: 'widget.my_container',

requires: [
'MyApp.view.MyTitleBar',
'MyApp.view.MyMenu'
],

config: {
html: '<div style="height:768px; background: #eee;">example</div>',
items: [
{
xtype: 'my_titlebar'
}
]
}
});


次にxtype: ‘my_menu’。スライドして出てくる部分です。datepickerfieldだけつけてあります。


MyMenu.js

Ext.define('MyApp.view.MyMenu', {

extend: 'Ext.form.Panel',
alias: 'widget.my_menu',
config: {
width: '300px',
height: '100%',
hidden: true,
right: '0',
top: '0',
scrollable: false,
items: [
{
xtype: 'datepickerfield',
label: '日付',
name: 'Date',
labelWidth: '35%',
value: new Date(),
dateFormat: 'Y/m/d',
picker: {
slotOrder: ['year', 'month', 'day']
}
}
]
}

});


xtype: ‘my_titlebar’は次のようにしました。


MyTitleBar.js

Ext.define('MyApp.view.MyTitleBar', {

extend: 'Ext.TitleBar',
alias: 'widget.my_titlebar',

config: {
docked: 'top',
title: 'MenuSample',
items: [
{
xtype: 'button',
align: 'right',
handler: function(button, e) {
var menu = Ext.Viewport.down('my_menu’),
container = Ext.Viewport.down('
my_container');
if (menu.isHidden()) {
menu.setZIndex(-1);
container.setStyle({
'
-webkit-transition-property': 'translate',
'
-webkit-transition-duration': '0.3s',
'
-webkit-transition-timing-function': 'linear'
});

menu.show();
container.translate(-300, 0);
Ext.defer(function () {
menu.setZIndex(0);
}, 300);
} else {
menu.setZIndex(-1);
container.translate(0, 0);
Ext.defer(function () {
menu.hide();
}, 300);
}
},
iconCls: 'settings'
}
]
}
});


注目すべきは、このMyTitleBar.jsにある、handlerの中です。

メニュー部分と、コンテナー部分をそれぞれ取得しています。

ここでは、タップされるたびに、コンテナーに対してsetStyleしてしまっていますが、

どこか初期化部分や、CSS自体に設定してしまっても良いと思います。

そのsetStyleの中身ですが、

'-webkit-transition-property': 'translate',

'-webkit-transition-duration': '0.3s',
'-webkit-transition-timing-function': 'linear'

こうすることで、Ext.Menuにあったような、スライドを表現することができます。

(実は、Ext.Menuも中をみると、同じようなことをやっています。)

ちなみにleftの値を少しずつずらす方法だと、重たすぎます。

その上で、

container.translate(-300, 0);

とすることで、300px分、コンテナー左にスライドします。

その直前にメニューをshowしておけば、その後ろにメニューが存在するように見えるというわけです。

スタイルなどは、全然調整していないので不格好ではありますが、

menu4.png

ギアアイコンをタップすることで、左にスライドし、

menu5.png

datepickerfieldをタップしても、ちゃんと、はみ出さず表示します。

menu6.png

Ext.Menuは便利なコンポーネントですが、適材適所で使っていきたいですね。