ExtJSサンプル: KeyMapを使ってGoogle Mapsをマウス無しで操作
4月 26th, 2008 | By yuki | Category: Ext JS今回は、Ext.KeyMapを利用してGoogle Mapsをマウス無しで操作するサンプルを作ってみました↓
Ext.KeyMapを利用したGoogle Maps操作デモ
もともとGoogle Maps APIには、GKeyboardHandlerというクラスが用意されていて、これを組み込めば一行で簡単にキーボード操作ができるのですが、ちょっと不便なところがあったので、Ext.KeyMapで独自のキーマップを行ってみました。
ソースコードはちょっと長くなりました、以下の通り:
//名前空間を確保
Ext.namespace('KeyMap');
// メイン
Ext.onReady(function(){
var cont = Ext.get('gmap');
var map = new KeyMap(cont.dom);
map.on('pan',function(ll){ Ext.get('latlng').dom.innerHTML = ll.lat()+'/'+ll.lng();});
map.on('zoom',function(zoom){ Ext.get('zoom').dom.innerHTML = zoom;});
map.on('typechange',function(type){Ext.get('maptype').dom.innerHTML = type;});
Ext.get('latlng').dom.innerHTML = map.cfg.lat+'/'+map.cfg.lng;
Ext.get('zoom').dom.innerHTML = map.cfg.zoom;
Ext.get('maptype').dom.innerHTML = 'NORMAL';
// キーマップのためのフォーカスが得られない場合があるので、念のため
setTimeout(function(){ cont.focus(); },500);
});
KeyMap = function(cont){
this.cont = cont;
this.cfg = arguments[1] || {};
Ext.applyIf(this.cfg,{lat:35.6833, lng:139.55, zoom:12});
// イベントの定義 (Ext.util.Observableのextendに必要)
this.addEvents({
'pan' : true,
'zoom' : true,
'typechange': true,
'beforesearch' : true,
'aftersearch' : true
});
// 初期化
this.initialize();
}
// イベントを発生させるためにExt.util.Observableを継承
Ext.extend(KeyMap, Ext.util.Observable, {
initialize: function(){
this.createMap();
this.createSearchDlg();
this.bindKeys();
// 検索窓の開閉時にキーマップを切り替え
this.on('beforesearch',function(t){t.keymaps.toggle('esc')});
this.on('aftersearch',function(t){t.keymaps.toggle('reg')});
},
// 地図オブジェクトの作成
createMap: function(){
var map = new GMap2(this.cont);
var lat=this.cfg.lat, lng=this.cfg.lng, z=this.cfg.zoom;
map.enableContinuousZoom();
map.addControl(new Gmap.ScaleControl());
map.setCenter(new GLatLng(lat, lng), z);
map.disableDoubleClickZoom();
map.setCenterAndPin = function (latlng){
map.setCenter(latlng);
// GMarkerを使わずにマーカーを中心に表示して一定時間後にエフェクト付きで消すための操作
var mark_id = 'pointer_div';
var coord = map.fromLatLngToContainerPixel(latlng); // setCenter後に呼ぶこと
var cont = map.getContainer();
if(!Ext.get(mark_id)){
Ext.DomHelper.append(cont,{
tag: 'div', id: mark_id, html: '<img src="./data/mano2.png" />'
});
}
Ext.get(mark_id).setStyle({
position: 'absolute',
top: (coord.y-34)+'px',
left: (coord.x-10)+'px',
'z-index': 100000,
display: 'none'
});
// Extでもエフェクトのチェーン可能
Ext.get(mark_id).fadeIn('t', {easing: 'easeOut',duration: .5})
.pause(3.0)
.ghost('t', {easing: 'easeOut',duration: .5});
};
this.map = map;
},
// 検索窓の生成
createSearchDlg: function(){
var app = this;
var map = app.map;
// 検索窓としてExt.Panelを利用
var pnl = new Ext.Panel({
collapsed : true,
layout : 'table',
layoutConfig: {columns:3},
autoHeight : true,
width : 290,
// 場所を絶対値で配置するため
floating : true,
style : {padding:'3px'},
// itemsのデフォルト設定
defaults :{bodyStyle:'margin:3px;padding:1px',border:false},
items:[{
html: '住所/場所:'
},{
xtype: 'textfield',
width: 150,
id: 'address'
},{
xtype: 'button',
text: '検索',
handler: function(){
onSearch();
}
}]
});
var addr = pnl.getComponent('address');
pnl.render(app.cont);
pnl.setPosition(20,0);
// Panelが閉じていてもなぜかFirefoxでは入力を受け付けてしまうためdisbaleし、
// Enterキーで検索するように設定
addr.disable().on('specialkey',onSearch);
// Panelを開くときに「beforesearch」イベントを発生させる
pnl.begin = function(){
app.fireEvent('beforesearch',app);
pnl.expand();
addr.enable().focus(true, 500);
};
// Panelを閉じるときに「aftersearch」イベントを発生させる
pnl.end = function(){
pnl.collapse();
addr.disable();
app.fireEvent('aftersearch',app);
};
this.dlg = pnl;
function onSearch(){
if(arguments.length==2 && arguments[1].getKey()!=13){ return; }
var str = pnl.getComponent('address').getValue();
if(str.length){
// Google Maps APIのClientGeocodeを利用
var geo = new Gmap.ClientGeocoder();
geo.getLatLng(str,function(loc){
if(loc){
pnl.end();
map.setCenterAndPin(new GLatLng(loc.y,loc.x));
}else{
Ext.Msg.alert('「'+str+'」は見つかりませんでした');
}
});
}
};
},
// キーマップを作成
bindKeys: function(){
var app = this;
var map = app.map, dlg = app.dlg;
var keymaps = {};
// 検索窓が閉じているときのキーマップ
keymaps.reg = new Ext.KeyMap(document,[
{key: 'io', fn: zoom},
{key: 'hjkl', fn: pan},
{key: 'nsb', fn: changetype, shift: true},
{key: Ext.EventObject.UP, fn: dlg.begin}
]);
// 検索窓が開いているときのキーマップ
keymaps.esc = new Ext.KeyMap(document,[
{key: Ext.EventObject.ESC,fn: dlg.end}
]);
// どちらのキーマップもdocumentに対して行われているため、切り替えを行う必要がある
// disable→enableの順序で行うこと(disable処理はdocumentからすべてのキーイベントを削除しているため)
keymaps.toggle = function(km){
if (km == 'esc' || km == 'reg') {
keymaps.esc.disable();
keymaps.reg.disable();
keymaps[km].enable();
}
};
keymaps.toggle('reg');
this.keymaps = keymaps;
// 地図を移動
function pan(){
var e = arguments[1];
var dirs = {
'72': {x:-1,y: 0}, // h
'74': {x: 0,y:-1}, // j
'75': {x: 0,y:+1}, // k
'76': {x:+1,y: 0} // l
};
var d = dirs[e.getKey()]; if(!d){ return; }
if(!e.hasModifier()){
// 文字キーのみが押されている場合、指された方向に50%スクロール
map.panDirection(-(d.x),d.y);
}else if(e.shiftKey){
// シフトキーが同時に押されている場合、指された方向に2%スクロール
var c = map.getCenter();
var sp = map.getBounds().toSpan();
var dx = sp.lng()/50, dy = sp.lat()/50;
map.setCenter(new GLatLng(c.lat()+d.y*dy,c.lng()+d.x*dx));
}
// 「pan」イベントを発生
app.fireEvent('pan',map.getCenter());
};
// 地図の種類を変更
function changetype(){
var e = arguments[1];
var types = {
'66': {code:G_HYBRID_MAP, name:'HYBRID'}, // b hybrid
'78': {code:G_NORMAL_MAP, name:'NORMAL'}, // n normal
'83': {code:G_SATELLITE_MAP,name:'SATELLITE'} // s satellite
};
var type = types[e.getKey()]; if(!type){ return; }
map.setMapType(type.code);
// 「typechange」イベントを発生
app.fireEvent('typechange',type.name);
};
// ズームレベルを変更
function zoom(){
var e = arguments[1];
var z = map.getZoom();
if(e.getKey()==73){ z = z+1<20?z+1:z; } // i
else if(e.getKey()==79){ z = z-1>=0?z-1:z } // o
map.setZoom(z);
// 「zoom」イベントを発生
app.fireEvent('zoom',z);
};
}
});
Ext.KeyMap利用上の注意点としては、同一のDOMに対して別々のKeyMapオブジェクトを生成した場合、片方をdisableすると、もう片方も強制的にdisableされてしまいます。
これは、disable()の中において、そのDOMにマップされている「全ての」キーイベントを削除してしまっているからです。なので、片方をdisableした後に、もう片方を必ずenableする必要があります。今回、検索窓の開閉でキーマップを切り替えたかったのですが、ここで少しはまってしまいました。
ONGMAPのVersion2でも、同様のキーマップを取り入れていますので、こっちも試してみてください(若干仕様は異なります&まだどこにも説明書きしてません・・・)
ExtJS
Mind you, that does not apply if the stated deadline is missed because you failed to provide the writer with requested information or you held back on your edits until the last minute. ,