Help us understand the problem. What is going on with this article?

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は便利なコンポーネントですが、適材適所で使っていきたいですね。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした