タイトルだけ見るとGoogle Mapとかと連携?かと思われるかもしれませんが、すみません違います。。
先日、サイト上に配置したマップ(案内図等)とカルーセルスライダーを連動させたいというご要望を受け、swiper.jsと画像で配置したマップにピンを立て、ピンをクリックしたらスライダーも一緒に動くということを行いました。意外と使うことがあるのでは?と思いその実装方法です。
https://swiperjs.com/swiper-api
ついでに、スライダーの中身とピンはjsonからajaxで出力させるようにしてしまいます。(更新運用が走ることを考えるとHTMLが煩雑になる可能性があるためjsonを更新した方がよいのではという考え)
デモはこちら
swiper.jsのインストール
まずswiper.jsをCDNもしくはダウンロードします。
■CDNの場合
下記のソースをHTMLに記載します。
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@9/swiper-bundle.min.css">
<script src="https://cdn.jsdelivr.net/npm/swiper@9/swiper-bundle.min.js"></script>
■ダウンロードの場合
下記からダウンロードします。
https://www.jsdelivr.com/package/npm/swiper
■npmの場合
npmの場合は下記に公式の方法が記載されています。
https://swiperjs.com/get-started#install-from-npm
swiper.jsとマップの連携させる実装方法
今回は上記で説明したとおりjsonからスライダーの中身、ピンの位置等の情報を取得するようにします。ピンをクリックするとスライダーがそのピンに該当するスライダーの位置に移動するといったものです。
またjQueryも使用するのでCDNかダウンロードして読み込んでおいてください。
まずjsonを下記のように作成しておきます。
js/data.json
[
{
"id": "1",
"name": "ただの木",
"text": "これはただの木です。",
"img": "img_01.svg",
"pos_y": "243",
"pos_x": "518"
},
{
"id": "2",
"name": "でかいビル",
"text": "でかい商業ビルです。",
"img": "img_02.svg",
"pos_y": "68",
"pos_x": "349"
},
{
"id": "3",
"name": "なんか変な塔",
"text": "たぶんなんかの電波塔です。",
"img": "img_03.svg",
"pos_y": "132",
"pos_x": "750"
},
{
"id": "4",
"name": "マリオが入りそうなやつ",
"text": "土管です。",
"img": "img_04.svg",
"pos_y": "106",
"pos_x": "178"
},
{
"id": "5",
"name": "Yeah!!",
"text": "イエェー(家)!",
"img": "img_05.svg",
"pos_y": "289",
"pos_x": "732"
}
]
id・・・スライドのid番号
name・・・スライドのタイトル的なもの
text・・・スライドの説明文
img・・・スライドの画像ファイル名
pos_y・・・ピンの縦位置(top)
pos_x・・・ピンの横位置(left)
こんな感じにしています。
続いてHTMLを作成してしまいます。
index.html
<div id="wrap">
<div id="map">
<img src="img/img_map.png">
<div class="pins"></div>
</div>
<div class="swiper">
<div class="items swiper-wrapper"></div>
</div>
</div>
4行目と7行目の
<div class="pins"></div>
<div class="items swiper-wrapper"></div>
ここにjsでピンとスライダーのHTMLが生成されるようにjs(jQuery)を作成します。
js/slider_map.js
$(function () {
let data = [];
$.ajax({
type: 'GET',
url: 'js/data.json',
dataType: 'json',
cache: false
}).then(
function (json) {
for (let i = 0; i < json.length; i++) {
data.push({
id: json[i].id,
name: json[i].name,
text: json[i].text,
img: json[i].img,
pos_y: json[i].pos_y,
pos_x: json[i].pos_x
});
let pinHtml = '<div class="pin num'+ data[i].id +'" data-index="'+ i +'" style="top:'+ data[i].pos_y +'px;left:'+ data[i].pos_x +'px;"><div><img src="img/pin.svg"><span>'+ data[i].id +'</span></div></div>';
$('.pins').append(pinHtml);
let sliderHtml = '<div id="num'+ data[i].id +'" class="item swiper-slide"><a href="#num'+ data[i].id +'"><img src="img/'+ data[i].img +'"><div class="detail"><span class="name">'+ data[i].name +'</span><span class="text">'+ data[i].text +'</span></div></a></div>';
$('.swiper-wrapper').append(sliderHtml);
$('.pin').eq(0).addClass('is-active');
}
},
function () {
alert('データの取得に失敗しました。');
}
);
const swiper = new Swiper('.swiper', {
spaceBetween: 10,
slidesPerView: 1.8,
centeredSlides: true
});
swiper.on('activeIndexChange', function(){
let sliderActiveNum = swiper.activeIndex;
$('.pin').removeClass('is-active').eq(sliderActiveNum).addClass('is-active');
});
$(document).on('click', '.pin', function(){
let indexNum = $(this).data('index');
swiper.slideTo(indexNum);
});
});
2行目〜31行目はajaxでjsonを取得していて、
20行目〜25行目の
let pinHtml = '<div class="pin num'+ data[i].id +'" data-index="'+ i +'" style="top:'+ data[i].pos_y +'px;left:'+ data[i].pos_x +'px;"><div><img src="img/pin.svg"><span>'+ data[i].id +'</span></div></div>';
$('.pins').append(pinHtml);
let sliderHtml = '<div id="num'+ data[i].id +'" class="item swiper-slide"><a href="#num'+ data[i].id +'"><img src="img/'+ data[i].img +'"><div class="detail"><span class="name">'+ data[i].name +'</span><span class="text">'+ data[i].text +'</span></div></a></div>';
$('.swiper-wrapper').append(sliderHtml);
$('.pin').eq(0).addClass('is-active');
ここで前述した、
<div class="pins"></div>
<div class="items swiper-wrapper"></div>
ここにHTMLを追加するようにしていて、最初のピンにクラス「is-active」を付与しています。
大事なのは、
data-index="'+ i +'"
この部分。data属性を付与して data-index に0からループ回数分の連番のインデックス番号を値に入れます。
33行目〜37行目の、
const swiper = new Swiper('.swiper', {
spaceBetween: 10,
slidesPerView: 1.8,
centeredSlides: true
});
ここでswiperを呼び出し、オプションの設定をしています。
39行目〜42行目の、
swiper.on('activeIndexChange', function(){
let sliderActiveNum = swiper.activeIndex;
$('.pin').removeClass('is-active').eq(sliderActiveNum).addClass('is-active');
});
これは、アクティブになっているスライダーが変わった時に処理され、 swiper.activeIndex で現在アプティブなスライダーのインデックス番号を取得して、それと同じ番号を持つピンにクラス「is-active」を付与するようにしています。
44行目〜47行目の、
$(document).on('click', '.pin', function(){
let indexNum = $(this).data('index');
swiper.slideTo(indexNum);
});
ここでピンのクリックイベントを登録していて、クリックすると、
let indexNum = $(this).data('index');
swiper.slideTo(indexNum);
クリックしたピンのdata-indexの値を取得し、 swiper.slideTo で取得したインデックス番号の枚数のところにスライドするようにしています。
swiper.slideTo や swiper.activeIndex 等はSwiper APIの公式ドキュメントを参照すると分かりやすいです。
https://swiperjs.com/swiper-api
補足で、
$('.pin').on('click', function(){
ではなく、
$(document).on('click', '.pin', function(){
としているのは、「.pin」はjsで生成してDOMに追加していて、その場合イベントが効かなくなってしまうためです。
これで実装完了になります。あとはCSSでデザイン調整すれば、下記のデモのような感じで動くと思います。
まとめ
もっと効率的なやり方(例えばswiper.jsデフォルトのページネーションのビュレットを使うとか)はあるかもしれませんが、今回は私が思いつく限り要件を満たせるのがこの方法しかなかったため、このように実装しました。
上記の方法を少し変更すれば、モーダル内にスライダーを簡単に実装できたりします。
swiper.jsは生jsで動いているので、vueやreact等でも使い勝手はよさそうですね。