レスポンシブ対応の画像スライダーを自作してみた

限られた領域で視覚的に情報を伝えるために、画像スライダーは有効です。

この記事では、スライダーを自作する方法やWordPressで使える便利なスライダーのプラグインを紹介します。

スライダーのデモ

jQueryの勉強のために、以下のような画像スライダーを自作しました。

パソコンで表示する場合には、大小2種類のスライダーが表示されていると思います。

画像の左右に表示される矢印で前後の画像へ移動できます。また、画像下部のドットインジケーターで任意の画像まで一気にスクロールできます。

HTML
<div class="slide_container">
<div class="arrow prev"><i class="fas fa-angle-left"></i></div>
<div class="slide_adjust_container">
<div class="slide">
<img src="https://sample.com/slide-image1.jpg">
</div>
<div class="slide">
<img src="https://sample.com/slide-image2.jpg">
</div>
<div class="slide">
<img src="https://sample.com/slide-image3.jpg">
</div>
</div>
<div class="arrow next"><i class="fas fa-angle-right"></i></div>
</div>

スライダーを表示するHTMLは上のようになります。「slide」というクラス属性を付けたタグで画像を囲います。

前後の矢印はFont Awesomeを使っており、別のアイコンに変えることも簡単です。

その他の主な特徴は、以下の通りです。

  • レスポンシブ対応しているため、スマホでもスライダーが動く
  • スマホのスワイプでもスライダーが動く
  • 前後の矢印とドットインジケーターの大きさが画像に連動する
注意
十分にテストをしているわけではなく、不具合があるかもしれません。記事内容は自己責任で参考にしてください。

スライダーを自作して思ったこと

始める前はそんなに難しくはないと思いましたが、やってみると想像以上に面倒に感じました。

例えば、以下のようなことを考慮する必要があります。

  • スマホでスワイプしたときにスライドさせるか
  • マウスでドラッグしたときにスライドさせるか
  • 最初と最後の画像では一方の矢印を表示しない
  • 一つのページに複数のスライダーを設置するケース
  • スライダーを表示した後に、ブラウザのサイズを変更するケース

そもそも、スライダーを表示するプラグインは既に多く存在するため、苦労して自作するメリットは少ないと思います。

例えば、有名なものでいえば「slick」というjQueryのプラグインがあります。

slickのスライダー

使い方は非常に簡単で、slickをダウンロードして5分ほどで上のようなスライダーを表示できます。

slickは無料で使えて、レスポンシブ対応しています。オプションを指定すれば、自動再生や垂直方向のスライドなど様々なカスタマイズも可能です。

WordPressのプラグインであれば「Smart Slider 3」が有名です。実際に使ったことがありますが、こちらも簡単です。

Smart Slider 3

ブロックエディターに対応したスライダーであれば、「Snow Monkey Blocks」というプラグインがあります。

Snow Monkey Blocks

使い方は、まずは以下のようにプラスのボタンをクリックして、メディアライブラリから画像を選択します。

Snow Monkey Blocksのスライダー

そして、管理画面の右側で、矢印やドットインジケーターを表示するか、スライドアニメーションの速度などを設定すれば、スライダーを表示できます。

Snow Monkey Blocksのスライダーの設定

ブロックエディターなのでプログラムを書く必要はなく、メディアライブラリから画像を選択できるのでimgタグすら書きません。

そのため、クライアントに納品する場合など、専門知識がない方でも使いやすいです。

以上のように、スライダーを簡単に表示できる便利なプラグインが既に多くある中で、自作する意味はありません。

自作することにメリットがあるとしたら、特殊な仕様で既存のプラグインでは対応できないケースや、簡易的なスライダーで十分なケースなどが考えられます。

例えば、「ドットインジケーターは表示しない」、「一つのページには一つのスライダーしか設置しない」、「スマホのスワイプではスライドしない」といった限定された機能であれば、とても簡単なプログラムで実現できます。

スライダーのプラグインは機能が豊富ですが、使わなければ意味がありません、スライダーを自作すれば、余計なプログラムを読み込まないで済みます。

スライダーを自作する方法

今回作成したスライダーのプログラムは以下の通りです。

なお、私が勉強のために作成しただけなので、正直あまり良いプログラムとは言えません。強引な部分や理解が足りていない部分もあります。

そのまま使うのではなく、部分的に参考にするのが良いと思います。

myscript.js
jQuery(function($){

	// スライダーがない場合は処理を終了
	if($('.slide_container').length == 0) return;

	let slideId;
		
	$('.slide_container').each(function(i){
		$(this).attr('id', 'slide' + (i + 1));
		slideId = '#' + $(this).attr('id');
				
		$(slideId + ' .slide:first-of-type').addClass('active');
				
		// 矢印を表示
		resizeArrow();
		changeArrow();
				
		// ドットインジケーターを表示
		let pointerHtml = '<div class="pointer_container">';
		$(slideId + '.slide_container .slide').each(function(n){
			pointerHtml += '<div class="pointer" id="' + n + '"></div>';
		});
		pointerHtml += '</div>';
		$(slideId + '.slide_container').append(pointerHtml);
				
		$(slideId + ' .pointer:first-of-type').addClass('active');
		resizePointer();
	});
			
	// ドットインジケーターがクリックされたとき
	$('.pointer').click(function() {
		slideId = '#' + $(this).closest('.slide_container').attr('id');
		$(slideId + ' .active').removeClass('active');
		let selectSlideIndex = $(this).attr('id');
		$(slideId + ' .slide').eq(selectSlideIndex).addClass('active');
				
		slideImage();
	});

	// 次の矢印がクリックされたとき
	$('.next').click(function() {
		nextArrow($(this));
	});
			
	// 前の矢印がクリックされたとき
	$('.prev').click(function() {
		prevArrow($(this));
	});
	
	// ブラウザサイズを変えたときに正しい位置に写真を表示
	$(window).resize(function() {
		$('.slide_container').each(function(){
			slideId = '#' + $(this).attr('id');
			resizeArrow();
			resizePointer();
			slideImage();
		});
	});

        //  スワイプされたとき
	setSwipe();		
		
	// 表示領域に応じて矢印のサイズを変更
	function resizeArrow() {
		let arrowSize = $(slideId + ' .slide').outerWidth(true) * 0.1 + 'px';
		$(slideId + ' .arrow').css({ 'display': 'block', 'font-size': arrowSize, 'line-height': arrowSize });
	}
		
	// 表示領域に応じてドットインジケーターのサイズを変更
	function resizePointer() {
		let pointerSize = $(slideId + ' .slide').outerWidth(true) * 0.03 + 'px';
		$(slideId + ' .pointer').css({ 'display': 'inline-block', 'width': pointerSize, 'height': pointerSize, 'border-radius': pointerSize });
	}
		
	// 先頭と最後の写真では矢印を非表示にする
	function changeArrow() {
		let activeSlideIndex = $(slideId + ' .slide').index($(slideId + ' .active'));
		$(slideId + ' .arrow').show();
		if(activeSlideIndex == 0){
			$(slideId + ' .prev').hide();
		}else if(activeSlideIndex == $(slideId + ' .slide').length - 1){
			$(slideId + ' .next').hide();
		}
	}
		
	// 次の写真をアクティブにする
	function nextArrow($this) {
		slideId = '#' + $this.closest('.slide_container').attr('id');
		let $nowSlide = $(slideId + ' .slide.active');
		$(slideId + ' .active').removeClass('active');
		$nowSlide.next().addClass('active');
		slideImage();
	}
		
	// 前の写真をアクティブにする
	function prevArrow($this) {
		slideId = '#' + $this.closest('.slide_container').attr('id');
		let $nowSlide = $(slideId + ' .slide.active');
		$(slideId + ' .active').removeClass('active');
		$nowSlide.prev().addClass('active');
		slideImage();
	}
		
	// アクティブな写真を表示する
	function slideImage() {
		let activeSlideIndex = $(slideId + ' .slide').index($(slideId + ' .active'));
		let imageWidth = $(slideId + ' .slide').outerWidth(true);
		let slideWidth = activeSlideIndex * imageWidth;
		$(slideId + ' .slide_adjust_container').stop(true).animate({'right': slideWidth + 'px'}, 600);
				
		$(slideId + ' .pointer').eq(activeSlideIndex).addClass('active');
								
		changeArrow();
	}
		
	// スマホでスワイプしたときの動きを定義
	function setSwipe() {
		let startPosition;
		let endPosition;
		const judgmentDistance = 60;
			
		let trigger = document.querySelectorAll(".slide_container");
		trigger.forEach(function(target) {	
				
			// スマホにタッチしたとき
			target.addEventListener('touchstart', function(e) {
				e.stopPropagation();
				startPosition = e.touches[0].pageX;
				endPosition = 0;
			});
				
			// スマホでタッチを動かしたとき
			target.addEventListener('touchmove', function(e) {
				e.stopPropagation();
				endPosition = e.changedTouches[0].pageX;
			});
				
			// スマホでタッチを外れたとき
			target.addEventListener('touchend', function(e) {
				slideId = '#' + $(this).closest('.slide_container').attr('id');
				let activeSlideIndex = $(slideId + ' .slide').index($(slideId + ' .active'));
					
				if(endPosition != 0 && startPosition > endPosition + judgmentDistance) {
					// 一定距離より長く左にスワイプしたとき
					if(activeSlideIndex != $(slideId + ' .slide').length - 1) {
						// 現在が最後の写真でなければ次の写真を表示
						nextArrow($(this));
					}	
				}else if (endPosition != 0 && startPosition + judgmentDistance < endPosition) {
					// 一定距離より長く右にスワイプしたとき
					if(activeSlideIndex != 0) {
						// 現在が最初の写真でなければ前の写真を表示
						prevArrow($(this));
					}				
				}
			});
		});
	}
});
style.css
/* スライダー */
.slide_container {
    overflow: hidden;
    width: 800px;  /* スライダーと同じサイズ */
    max-width: 100%;
    position: relative;
    margin: 1.5em 0;
}

.slide_container::after {
    content: "";
    display: block;
    clear: both;
}

.slide_container .arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    -webkit-transform: translateY(-50%);
    -ms-transform: translateY(-50%);
    z-index: 10;
    cursor: pointer;
    color: rgba(204,204,204,0.9);  /* 好きな色で */
    display: none;
}

.slide_container .next {
    right: 5px;
}

.slide_container .prev {
    left: 5px;
}

.slide_container .slide_adjust_container {
    width: 3200px;  /* 全てのスライダーが収まるように大きめのサイズ */
    position: relative;
}

.slide_container .slide_adjust_container .slide {
    float: left;
    width: 800px;  /* 好きなサイズで */
    height: 500px;  /* 好きなサイズで */
}

.slide_container .slide_adjust_container .slide img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.pointer_container {
    position: absolute;
    bottom: 5px;
    left: 50%;
    transform: translateX(-50%);
    -webkit-transform: translateX(-50%);
    -ms-transform: translateX(-50%);
    z-index: 10;
}

.pointer_container .pointer {
    content: '';
    width: 14px;
    height: 14px;
    border-radius: 14px;
    min-width: 14px;  /* 小さくなり過ぎるのを防ぐ */
    min-height: 14px;
    background: rgba(204,204,204,0.9);  /* 好きな色で */
    display: none;
    margin-right: 5px;
    cursor: pointer;
}

.pointer_container .pointer.active {
    background: rgba(0,0,0,0.9);  /* 好きな色で */
}

@media(max-width:990px) {
    .slide_container,
    .slide_container .slide_adjust_container .slide {
        width: 100vw;
    }
	
    .slide_container .slide_adjust_container .slide {
        height: 250px; /* 好きなサイズで */
    }
}

分かりにくいと思う部分を少し補足します。

スライダーの仕組み

表示するスライドやドットインジケーターには「active」というクラス属性を付与して、見た目や動きを制御しています。

スライダーに表示する画像をCSSのfloatを使って横に並べて、jQueryのanimateという関数で右端からの距離を変えることでスライドさせています。

スライダーがない場合

スライダーが画面上にない場合は処理を終了しています。

特定のページだけにスライダーを設置する場合は、それ以外のページではスライダーのJavaScriptファイルを読み込まない方が表示速度は上がると思います。

複数のスライダー

一つのページに複数のスライダーを設置するケースを想定して、それぞれのスライダーに「slide1」「slide2」という形でID属性に連番を付与しています。

そして、セレクタではそれを親要素に指定することで、対象のスライダーに操作を絞っています。

矢印とドットインジケーターのサイズ

resizeArrow、resizePointerという自作の関数で、表示領域に応じて矢印とドットインジケーターのフォントサイズを動的に変更しています。

そのため、パソコンなどで表示したときに、画像は大きいのに矢印とドットインジケーターだけ小さいといった違和感を解消できます。

outerWidthというjQueryの関数でスライダーの横幅を取得して、その一定割合をフォントサイズに指定しています。

スワイプしたときのスライド

setSwipeという自作の関数で、スマホをスワイプしたときにスライドさせています。矢印とドットインジケーターだけでスライドさせる場合には不要です。

touchstart、touchmove、touchendというイベントを使い、横にスワイプした距離を取得して、一定距離を上回る場合にスライドさせています。

これによって、画像に触れただけの状態など、意図しないスライドを防いでいます。

補足
なお、WordPressで自作のスクリプトファイルを読み込むには、以下のようにfunctions.phpでwp_enqueue_scriptを使うのが一般的です。

functions.php
function my_enqueue_script() {
    wp_enqueue_script('my-script', get_stylesheet_directory_uri() . '/myscript.js', array('jquery'));
}
add_action('wp_head', 'my_enqueue_script');

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA