スワイプ・ドラッグで閉じられるドロワーメニューを作る方法
スワイプ・ドラッグで閉じられるドロワーメニューを作る方法を紹介します。このサンプルでは、画面左側から表示されるドロワーメニューを、マウスで右方向へドラッグしたり、スマートフォンやタブレットで右方向へスワイプしたりすると閉じられるメニューを作成します。Pointer Eventsを使用しているため、PCではドラッグ操作、タッチ対応端末ではスワイプ操作を同じコードで実装できます。
ハンバーガーメニューを利用するWebサイトや管理画面、会員サイト、ECサイトなどで利用しやすく、閉じるボタンへカーソルを移動しなくても、直感的な操作でメニューを閉じられます。一定距離以上ドラッグ・スワイプした場合のみ閉じるため、軽く触れただけで誤って閉じてしまうことも防げます。移動距離が足りない場合は、ドロワーメニューが元の位置へ戻るため、自然な操作感で利用できます。
HTMLではタイトルやメニュー項目、説明文を変更でき、CSSではドロワーメニューの横幅、背景色、文字色、余白、影などを調整できます。JavaScriptでは閉じるまでに必要な移動距離やアニメーション時間を変更できるため、用途に合わせた操作感へカスタマイズできます。レスポンシブ対応のため、スマートフォン・タブレット・PCでそのまま利用できます。
コードについて
本記事のコードはサンプルコードです。ご利用前に必ず動作確認を行ってください。
免責事項
本コードの利用により発生した損害について、当サイトは一切の責任を負いません。
デモ
「メニューを開く」をクリックしたあと、ドロワーメニュー上または半透明の背景部分から左方向へ100px以上スワイプ・ドラッグすると閉じます。移動距離が足りない場合は元の位置へ戻ります。
コードをコピーして使おう!
<!DOCTYPE html>
<html lang="ja">
<head>
<!-- 文字コード -->
<meta charset="UTF-8">
<!-- スマートフォン対応 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- ページタイトル -->
<title>スワイプ・ドラッグで閉じられるドロワーメニュー</title>
<!-- CSSファイル -->
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- ドロワーメニューを開くボタン -->
<button
class="swipe-drag-close-drawer_open-button"
type="button"
data-swipe-drag-drawer-open
aria-expanded="false"
aria-controls="drawer-menu">
メニューを開く
</button>
<!-- オーバーレイ -->
<div
class="swipe-drag-close-drawer_overlay"
data-swipe-drag-drawer-overlay
aria-hidden="true">
<!-- ドロワーメニュー本体 -->
<nav
class="swipe-drag-close-drawer_panel"
id="drawer-menu"
aria-label="ドロワーメニュー"
data-swipe-drag-drawer>
<!-- タイトル -->
<div class="swipe-drag-close-drawer_title">
メニュー
</div>
<!-- 説明文 -->
<p class="swipe-drag-close-drawer_text">
左へスワイプ・ドラッグするとメニューを閉じられます。
</p>
<!-- メニュー一覧 -->
<ul class="swipe-drag-close-drawer_list">
<!-- メニュー項目 -->
<li>
<a href="#">ホーム</a>
</li>
<!-- メニュー項目 -->
<li>
<a href="#">サービス</a>
</li>
<!-- メニュー項目 -->
<li>
<a href="#">お問い合わせ</a>
</li>
<!-- メニュー項目 -->
<li>
<a href="#">会社概要</a>
</li>
</ul>
</nav>
</div>
<!-- JavaScriptファイル -->
<script src="script.js"></script>
</body>
</html>
/* ページ全体 */
body{
margin:0; /* ブラウザ標準の余白を削除 */
padding:40px 20px; /* 上下40px・左右20pxの余白。値を大きくすると画面端との間隔が広がる */
background:#ffffff; /* ページ全体の背景色 */
font-family:sans-serif; /* ページ全体で使用するフォント */
}
/* メニューを開くボタン */
.swipe-drag-close-drawer_open-button{
display:block; /* ボタンを横幅いっぱいにせず単独表示する */
margin:40px auto; /* 上下40pxの余白を付けて中央へ配置 */
padding:14px 28px; /* 上下14px・左右28px。値を大きくするとボタンが大きくなる */
border:none; /* ボタンの枠線を非表示 */
border-radius:10px; /* ボタンの角丸。値を大きくすると丸みが強くなる */
background:#2563eb; /* ボタンの背景色 */
color:#ffffff; /* ボタンの文字色 */
font-size:16px; /* ボタンの文字サイズ。値を大きくすると文字が大きくなる */
font-weight:700; /* ボタンを太字にする */
cursor:pointer; /* マウスカーソルをポインターへ変更 */
}
/* オーバーレイ */
.swipe-drag-close-drawer_overlay{
position:fixed; /* 画面全体へ固定表示 */
inset:0; /* 上下左右いっぱいに広げる */
display:none; /* 初期状態では非表示 */
background:rgba(15,23,42,.45); /* 半透明の背景 */
overflow:hidden; /* メニューが画面外へ出ても表示しない */
}
/* オーバーレイ表示中 */
.swipe-drag-close-drawer_overlay.is-active{
display:block; /* ドロワーメニュー表示時だけ表示する */
}
/* ドロワーメニュー本体 */
.swipe-drag-close-drawer_panel{
position:absolute; /* オーバーレイ内へ配置 */
top:0;
left:0;
width:320px; /* ドロワーメニューの横幅。値を大きくすると幅が広がる */
max-width:90%; /* 小さい画面では画面幅に合わせて縮小する */
height:100%; /* 画面の高さに合わせる */
padding:32px 24px; /* 上下32px・左右24px。値を大きくすると余白が広がる */
box-sizing:border-box; /* paddingを横幅へ含める */
background:#ffffff; /* ドロワーメニューの背景色 */
box-shadow:4px 0 20px rgba(0,0,0,.12); /* 右側へ影を付ける */
transform:translateX(0); /* 初期位置 */
transition:transform .3s ease;
/*
0.3秒で開閉アニメーションする。
値を大きくするとゆっくり、
値を小さくすると速くなる。
*/
touch-action:pan-y; /* 縦スクロールは許可し、横方向はJavaScriptで制御する */
}
/* タイトル */
.swipe-drag-close-drawer_title{
margin:0 0 12px; /* 下側12pxの余白。値を大きくすると説明文との間隔が広がる */
font-size:24px; /* タイトルの文字サイズ。値を大きくすると文字が大きくなる */
font-weight:700; /* タイトルを太字にする */
}
/* 説明文 */
.swipe-drag-close-drawer_text{
margin:0 0 28px; /* 下側28pxの余白。値を大きくするとメニューとの間隔が広がる */
color:#4b5563; /* 説明文の文字色 */
line-height:1.7; /* 説明文の行間。値を大きくすると読みやすくなる */
}
/* メニュー一覧 */
.swipe-drag-close-drawer_list{
margin:0; /* 外側余白を削除 */
padding:0; /* 内側余白を削除 */
list-style:none; /* リストマークを非表示 */
}
/* メニュー項目 */
.swipe-drag-close-drawer_list li+li{
margin-top:10px; /* 項目同士の間隔。値を大きくすると広がる */
}
/* メニューリンク */
.swipe-drag-close-drawer_list a{
display:block; /* リンク全体をクリックできるようにする */
padding:14px 16px; /* 上下14px・左右16px。値を大きくすると押しやすくなる */
border-radius:8px; /* リンクの角丸。値を大きくすると丸みが強くなる */
color:#111827; /* リンク文字色 */
text-decoration:none; /* 下線を非表示 */
transition:background-color .2s ease;
/*
0.2秒で背景色を切り替える。
値を大きくするとゆっくり、
値を小さくすると速くなる。
*/
}
/* マウスを重ねたとき */
.swipe-drag-close-drawer_list a:hover{
background:#eff6ff; /* ホバー時の背景色 */
}
/* 画面幅640px以下 */
@media (max-width:640px){
/* ドロワーメニュー本体 */
.swipe-drag-close-drawer_panel{
width:280px; /* スマートフォン用の横幅。値を大きくすると幅が広がる */
padding:24px 20px; /* スマートフォンでは余白を少し小さくする */
}
/* タイトル */
.swipe-drag-close-drawer_title{
font-size:22px; /* スマートフォンではタイトルを少し小さくする */
}
}
/* ドロワーメニューを開くボタンを取得 */
const openButton=document.querySelector("[data-swipe-drag-drawer-open]");
/* オーバーレイを取得 */
const overlay=document.querySelector("[data-swipe-drag-drawer-overlay]");
/* ドロワーメニュー本体を取得 */
const drawer=document.querySelector("[data-swipe-drag-drawer]");
/* ドラッグ開始位置を保存 */
let startX=0;
/* 現在の移動量を保存 */
let moveX=0;
/* ポインター操作中か管理する */
let isPointerDown=false;
/* 横方向のドラッグと判定したか管理する */
let isHorizontalDrag=false;
/* 操作中のポインターIDを保存 */
let activePointerId=null;
/* ドラッグ開始と判定する距離 */
const dragStartDistance=10;
/*
10は10pxを表す。
値を大きくするとドラッグとして認識しにくくなり、
値を小さくすると少しの移動でもドラッグとして認識する。
*/
/* ドロワーメニューを閉じる距離 */
const closeDistance=120;
/*
120は120pxを表す。
値を大きくすると閉じるまで長くドラッグする必要があり、
値を小さくすると短いドラッグでも閉じる。
*/
/* ドロワーメニューを開く */
function openDrawer(){
overlay.classList.add("is-active");
overlay.setAttribute("aria-hidden","false");
openButton.setAttribute("aria-expanded","true");
drawer.style.transform="";
drawer.style.transition="";
}
/* ドロワーメニューを閉じる */
function closeDrawer(){
drawer.style.transition="transform .3s ease";
/*
0.3秒で閉じる。
値を大きくするとゆっくり、
値を小さくすると速く閉じる。
*/
drawer.style.transform="translateX(-100%)";
drawer.addEventListener("transitionend",function handleTransitionEnd(event){
if(event.propertyName!=="transform"){
return;
}
overlay.classList.remove("is-active");
overlay.setAttribute("aria-hidden","true");
openButton.setAttribute("aria-expanded","false");
drawer.style.transform="";
drawer.style.transition="";
drawer.removeEventListener("transitionend",handleTransitionEnd);
});
}
/* ドラッグ状態を初期化する */
function resetDragState(){
startX=0;
moveX=0;
isPointerDown=false;
isHorizontalDrag=false;
activePointerId=null;
}
/* 開くボタン */
openButton.addEventListener("click",openDrawer);
/* ドラッグ開始 */
overlay.addEventListener("pointerdown",(event)=>{
if(!overlay.classList.contains("is-active")){
return;
}
startX=event.clientX;
moveX=0;
isPointerDown=true;
isHorizontalDrag=false;
activePointerId=event.pointerId;
drawer.style.transition="none";
});
/* ドラッグ中 */
overlay.addEventListener("pointermove",(event)=>{
if(!isPointerDown){
return;
}
if(event.pointerId!==activePointerId){
return;
}
moveX=event.clientX-startX;
if(moveX<=-dragStartDistance){
isHorizontalDrag=true;
}
if(!isHorizontalDrag){
return;
}
event.preventDefault();
drawer.style.transform=`translateX(${moveX}px)`;
});
/* ドラッグ終了 */
function finishDrag(event){
if(!isPointerDown){
return;
}
if(event.pointerId!==activePointerId){
return;
}
if(!isHorizontalDrag){
drawer.style.transition="";
drawer.style.transform="";
resetDragState();
return;
}
if(moveX<=-closeDistance){
closeDrawer();
}else{
drawer.style.transition="transform .3s ease";
/*
0.3秒で元の位置へ戻す。
値を大きくするとゆっくり、
値を小さくすると速く戻る。
*/
drawer.style.transform="translateX(0)";
}
resetDragState();
}
/* ポインターを離したとき */
overlay.addEventListener("pointerup",finishDrag);
/* ポインター操作が中断したとき */
overlay.addEventListener("pointercancel",finishDrag);
コードをコピーする場合
使用するコードのタブを選び、「このタブをコピー」をクリックすると、現在表示中のコードをコピーできます。
サンプル一式を取得する場合
HTML・CSSなどすべてのファイルが必要な場合は、「サンプル一式をダウンロード」をクリックしてください。
ファイル構成と設置方法
このサンプルはHTML・CSS・JavaScriptの3ファイルで構成されています。HTMLではドロワーメニュー本体やオーバーレイ、開閉ボタン、メニュー項目を配置し、CSSではレイアウトやデザイン、アニメーションを設定します。JavaScriptではPointer Eventsを利用して、スマートフォンではスワイプ、パソコンではマウスドラッグでドロワーメニューを閉じる処理を実装しています。
ドロワーメニューは画面左側から表示され、左方向へスワイプ・ドラッグすると指やマウスの動きに合わせて移動します。一定距離以上移動した状態で操作を終了するとメニューが閉じ、移動距離が不足している場合は元の位置へ戻る仕組みです。Pointer Eventsを使用しているため、1つのコードでマウス操作とタッチ操作の両方へ対応できます。
サンプルはindex.html、style.css、script.jsの3ファイルだけで動作します。すべて同じsampleフォルダへ配置し、index.htmlをブラウザで開くだけで利用できるため、初めてドロワーメニューを実装する方でも導入しやすい構成になっています。
sample/
├── index.html
├── style.css
└── script.js
各ファイルの役割
index.htmlはドロワーメニュー全体を構成するファイルです。開くボタン、メニュータイトル、説明文、メニュー項目を記述しています。タイトルや説明文、リンク名、リンク先を変更したい場合はindex.htmlを編集します。
style.cssはドロワーメニューの見た目を設定するファイルです。ボタンのデザイン、オーバーレイの背景色、ドロワーメニューの横幅、余白、文字サイズ、角丸、影、ホバー時の背景色、レスポンシブ表示などを変更できます。
script.jsはドロワーメニューの動作を制御するファイルです。開閉処理、Pointer Eventsによるスワイプ・ドラッグ操作、移動距離の判定、一定距離以上ドラッグした場合に閉じる処理、元の位置へ戻す処理を記述しています。
設置方法・使い方
sampleフォルダへindex.html、style.css、script.jsを配置します。3つのファイルは同じ階層へ保存してください。index.htmlではstyle.cssとscript.jsを読み込んでいるため、ファイル名や配置場所を変更する場合は読み込み先も合わせて修正します。
sample/index.htmlをブラウザで開き、「メニューを開く」ボタンを押すとドロワーメニューが表示されます。表示されたドロワーメニューまたはオーバーレイ上で左方向へスワイプ・ドラッグすると、メニューが移動します。一定距離以上移動した状態で指やマウスを離すと閉じ、距離が不足している場合は元の位置へ戻ります。
カスタマイズできる内容
index.htmlでは、開くボタンの文章、メニュータイトル、説明文、メニュー項目の表示内容、リンク先、表示順を変更できます。メニュー項目を追加・削除する場合もindex.htmlを編集します。
style.cssでは、ボタンやドロワーメニューの背景色、文字色、横幅、余白、角丸、影、タイトルサイズ、リンクのホバー色、オーバーレイの背景色などを変更できます。用途に合わせてデザイン全体を自由に調整できます。
script.jsでは、ドラッグとして認識する移動距離や、ドロワーメニューを閉じるために必要な移動距離、開閉時のアニメーション時間を変更できます。スワイプ・ドラッグの操作感を用途に合わせて調整できます。
数値調整
style.css
width:320px;
ドロワーメニューの横幅です。値を大きくすると広くなり、小さくするとコンパクトになります。
max-width:90%;
画面幅に対する最大サイズです。値を変更すると小さい画面で表示される横幅が変わります。
padding:32px 24px;
ドロワーメニュー内側の余白です。値を大きくすると余白が広がり、小さくすると詰まった表示になります。
border-radius:10px;
ボタンの角丸です。値を大きくすると丸みが強くなり、小さくすると四角に近づきます。
transition:transform .3s ease;
ドロワーメニューの開閉アニメーション時間です。0.3秒で動作します。値を大きくするとゆっくり、小さくすると速く動きます。
script.js
dragStartDistance = 10;
10は10pxを表します。ドラッグとして認識する移動距離です。値を大きくするとドラッグ判定が遅くなり、小さくすると少しの移動でもドラッグとして認識します。
closeDistance = 120;
120は120pxを表します。左方向へ120px以上移動した場合にドロワーメニューを閉じます。値を大きくすると閉じにくくなり、小さくすると短い距離でも閉じるようになります。
transform .3s ease
script.js内でも0.3秒で閉じる・戻るアニメーションを設定しています。style.cssと同じ値へ変更すると動作を統一できます。
レスポンシブ対応
このサンプルはレスポンシブ対応です。style.cssではmax-width:90%;を設定しているため、画面幅が狭い場合でもドロワーメニューが画面内へ収まります。
640px以下ではメディアクエリを使用し、ドロワーメニューの横幅を280px、余白を24px 20pxへ変更しています。タイトルの文字サイズも22pxへ調整されるため、スマートフォンでも見やすく表示できます。
Pointer Eventsを使用しているため、パソコンではマウスドラッグ、スマートフォンやタブレットではスワイプ操作で同じようにドロワーメニューを閉じることができます。
コメント