【Leaflet/OpenLayers】Gesture Handling 機能を実装する
はじめに
Gesture Handling とは、マップをスマートフォンで閲覧する際に2本指でしかスクロールできないようにしたり、PCで閲覧する際にCtrlキーを押さないとズームできないようにする機能の事です。今回は、この Gesture Handling 機能を Leaflet/OpenLayers に実装する方法について説明したいと思います。
なお、当サイト(DrawTrail)で Leaflet のプログラムを生成した場合、Gesture Handling を自動で実装しています(OFFにすることもできます)。使い方はこちらにあるので、興味のある方は使ってみてください。
プラグインで実装する方法
Leaflet の場合はプラグインを使って、Gesture Handling を実装することが出来ます(OpenLayers のプラグインはありません)。
Leaflet.GestureHandling の JavaScript および CSS は GitHub にあります。ソースコードと実装例は以下のようになります。
なお、サーバーにダウンロードしておかずとも、下記のファイルパスで直接アクセスも出来ます。
<link rel="stylesheet" href="//unpkg.com/leaflet-gesture-handling/dist/leaflet-gesture-handling.min.css" type="text/css">
<script src="//unpkg.com/leaflet-gesture-handling"></script>
<!DOCTYPE html>
<html>
<head>
<title>htmlMap</title>
<meta http-equiv='content-type' charset='utf-8'>
<meta name='viewport' content='width=device-width'>
</head>
<body>
<!-- 埋め込みマップのdivタグ。マップサイズはwidth(幅)とheight(高さ)で決まる -->
<div id='mapcontainer' style='width:100%; height:300px; z-index:0;'></div>
<!-- 以下LeafletのJavaScriptとCSS -->
<link rel='stylesheet' href='https://unpkg.com/leaflet@1.3.0/dist/leaflet.css' />
<script src='https://unpkg.com/leaflet@1.3.0/dist/leaflet.js'></script>
<!-- GestureHandling のプラグイン -->
<link rel='stylesheet' href='leaflet-gesture-handling.min.css のファイルパス'>
<script src='leaflet-gesture-handling.min.js のファイルパス'></script>
<script>
function init_map() {
var map = L.map('mapcontainer', {
center: [43.659, 142.141], //中心位置の座標 [緯度, 経度]
zoom: 6, //ズームレベルの初期設定
gestureHandling: true //GestureHandling を ON にする
});
//マップタイルを読み込み、引用元を記載する
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
{attribution: "<a href='http://osm.org/copyright'> ©OpenStreetMap </a>"}).addTo(map);
}
//ダウンロード時に初期化する
window.addEventListener('DOMContentLoaded', init_map());
</script>
</body>
</html>
実装例
JS / HTML で実装する方法
プラグインを使わずに Gesture Handling を実装するには、マップの上からマスクとメッセージの要素を作成しておいて、イベントに応じて表示と非表示を切り替えることによって実現します。
まず、HTML と CSS は以下のようになります。
<!-- 埋め込みマップのdivタグ-->
<div id='container'>
<div id='mapcontainer'></div>
<div id='mapmask' class='mapmask'>
<div id='mapmessage'></div>
</div>
</div>
<!-- CSS -->
<style>
#container{
position:relative;
}
#mapcontainer{
width:100%;
height:300px;
z-index:0;
}
.mapmask{
width:100%;
height:300px;
position:absolute;
top:0;
left:0;
z-index:1;
pointer-events:none;
background:rgba(0, 0, 0, 0.5);
opacity:0;
transition:0.5s;
}
.mapshow{
opacity:1;
}
#mapmessage{
width:90%;
margin-left:5%;
position:absolute;
text-align:center;
top:50%;
-webkit-transform:translateY(-50%);
-ms-transform:translateY(-50%);
transform:translateY(-50%);
color:#ffffff;
}
</style>
次に、イベントを付加する JavaScript は以下のようになります。なお、例では Leaflet になっていますが、OpenLayers の場合も同じです。jQuery を使用していませんが、jQuery を使うともう少しシンプルになります。
function init_map() {
//マップを表示する
var map = L.map('mapcontainer',{
center: [43.659, 142.141],
zoom: 6,
dragging: false,
scrollWheelZoom: false
});
//マップタイルを読み込み、引用元を記載する
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: "<a href='http://osm.org/copyright'> ©OpenStreetMap </a>"
}).addTo(map);
//ブラウザの言語を取得する
var MaskLanguage = (window.navigator.languages&&window.navigator.languages[0])
||window.navigator.language
||window.navigator.userLanguage
||window.navigator.browserLanguage;
var ctrl_down = 0;
var Timeout;
//キーボードのキーイベント
document.body.addEventListener('keydown', function(e){
if(e.ctrlKey||e.metaKey){
ctrl_down = 1; //Ctrl もしくは command キーを押した場合、ctrl_down = 1 とする
document.getElementById('mapmask').classList.remove('mapshow'); //マスクを表示する
}
});
document.body.addEventListener('keyup', function(e){
ctrl_down = 0; //キーを上げた場合、ctrl_down = 0 とする
});
//地図のマウスホイールイベント
document.getElementById('mapcontainer').addEventListener('wheel', function(e){
//ブラウザの言語に合わせて、メッセージの表示内容を切り替える
if((MaskLanguage=='ja')||(MaskLanguage=='ja-JP')||(MaskLanguage=='ja-JP-mac')){
document.getElementById('mapmessage').innerText = '地図をズームするには、Ctrl (Command) キーを押しながらスクロールして下さい';
}else{
document.getElementById('mapmessage').innerText = 'Use Ctrl (Command) + Scroll to zoom the map';
}
//Ctrlキーを押下している場合の処理
if(ctrl_down ==1){
e.returnValue = false; //ブラウザズームを妨害する
map.scrollWheelZoom.enable(); //スクロールホイールズームを許可する
document.getElementById('mapmask').classList.remove('mapshow'); //マスクを非表示する
//Ctrlキーを押下していない場合の処理
}else{
clearTimeout(Timeout); //一回リセット
map.scrollWheelZoom.disable(); //スクロールホイールズームをキャンセルする
document.getElementById('mapmask').classList.add('mapshow'); //マスクを表示する
Timeout = setTimeout(function(){
document.getElementById('mapmask').classList.remove('mapshow'); //3秒後にマスクを非表示にする
}, 3000);
}
})
//マウスポインタがマップから離れた時のイベント
document.getElementById('mapcontainer').addEventListener('mouseleave', function(e){
document.getElementById('mapmask').classList.remove('mapshow'); //マスクを非表示にする
map.dragging.disable(); //マップのドラッグをキャンセルする
});
//マウスポインタがマップ上で動いたときのイベント
document.getElementById('mapcontainer').addEventListener('mousemove', function(e){
map.dragging.enable(); //マップのドラッグを許可する
});
//マップを指でタッチした時のイベント
document.getElementById('mapcontainer').addEventListener('touchmove', function(e){
//ブラウザの言語に合わせて、メッセージの表示内容を切り替える
if((MaskLanguage=='ja')||(MaskLanguage=='ja-JP')||(MaskLanguage=='ja-JP-mac')){
document.getElementById('mapmessage').innerText = '地図を移動させるには\n指2本で操作します';
}else{
document.getElementById('mapmessage').innerText = 'Use two fingers to move the map';
}
//タッチした時の指が二本指だった時の処理
if(e.touches.length == 2){
e.preventDefault(); //デフォルトのイベントを妨害する
map.dragging.enable(); //マップのドラッグを許可する
if (map.tap) map.tap.enable(); //マップのタップを許可する
document.getElementById('mapmask').classList.remove('mapshow'); //マスクを非表示にする
//タッチした時の指の本数が二本以外の処理
}else{
clearTimeout(Timeout); //一回リセット
map.dragging.disable(); //マップのドラッグをキャンセルする
document.getElementById('mapmask').classList.add('mapshow'); //マスクを表示する
Timeout = setTimeout(function(){
document.getElementById('mapmask').classList.remove('mapshow'); //3秒後にマスクを非表示にする
}, 3000);
}
})
}
window.addEventListener('DOMContentLoaded', init_map());
実装例は以下のようになります。