class化しました
objectの要素を入れ替えて汎用的に使えるようclass化しました。
また、手動スライドと自動スライドを統合して、autoPlayでtrueかfalseで自動と手動を選べるようにしました。
touchイベントに関してはclassの外で関数にしましたが、これもclassの中に入れた方が良かったかなと後から思ったりしました。classを使うのは初めてなので今は勉強中ということで。
touchイベントとclickイベントの制御が難しかったです。
cssは一部変更を加えましたが、ほぼ前バージョンと同じです。css次第でいろんなレイアウトができるかなと。
htmlは同じものを使用してるので、jsだけ更新します。
普通のスライド
$(function () {
//要素取得object
const slideEl = {
panelImg: $('.panelImg'),//画像を二つ並べるところ
next: $('#next'),
prev: $('#prev'),
thumbID: '#thumb',//タッチイベント除外用
thumb: $('#thumb li'),//要素格納
act: 'act',//マーカー
autoPlay: true,//自動再生ON/OFF
wait: 3000,//自動再生スピード
speed: 800,//スライド待機時間
itemW: $('#slidePanel'),//1枚の画像幅スライドの基準
pointBreak: 768,//レスポンシブ切替幅
panel: $('.panel'),//タッチイベント領域
swipeThreshold: 20// スワイプ判定距離(px)
}
//class宣言
class Slide {
constructor() {
this.imgW = 0;
this.panelImg = slideEl.panelImg;
this.thumb = slideEl.thumb;
this.act = slideEl.act;
this.next = slideEl.next;
this.prev = slideEl.prev;
this.itemW = slideEl.itemW;
this.autoPlay = slideEl.autoPlay;
this.pointBreak = slideEl.pointBreak;
this.click = true;
this.timerID = null;
}
slideW() {
if (window.innerWidth > this.pointBreak) {
this.imgW = this.itemW.width();
} else {
this.imgW = $(window).width();
}
// エラーハンドリング追加
if (!this.imgW || this.imgW === 0) {
console.error('Failed to calculate slide width');
// デフォルト値を設定してクラッシュを防ぐ
this.imgW = $(window).width(); // または適切なフォールバック値
return false; // エラーを示すためにfalseを返す
}
return true; // 成功時にtrueを返す
}
// タイマー管理を統一(クラスのメソッドとして)
clearTimer() {
if (this.timerID) {
clearTimeout(this.timerID);
this.timerID = null;
}
}
//次へ
nextSlide() {
let nextImg, cloneImg;
if (!this.click) return; // 早期リターン
this.click = false;
//カルーセルにする為、最後のliになったら最初のliを指定
if (this.thumb.last().hasClass(this.act)) {
nextImg = this.thumb.first();
} else {
nextImg = this.thumb.filter('.' + this.act).next();
}
//要素クローン
cloneImg = nextImg.children().clone();
this.panelImg.append(cloneImg);
//actの付替え
nextImg.addClass(this.act).siblings().removeClass(this.act);
//スライド
this.panelImg.stop().animate({ 'margin-left': this.imgW * -1 }, slideEl.speed,
() => {
this.panelImg.children().first().remove();
this.panelImg.css('margin-left', 0);
//スライドが終わったらクリックOK
this.click = true;
});
}
//前へ
prevSlide() {
let prevImg, cloneImg;
if (!this.click) return; // 早期リターン
this.click = false;
//最初のliになったら最後のliを指定
if (this.thumb.first().hasClass(this.act)) {
prevImg = this.thumb.last();
} else {
prevImg = this.thumb.filter('.' + this.act).prev();
}
//要素クローン
cloneImg = prevImg.children().clone();
this.panelImg.prepend(cloneImg).css('margin-left', this.imgW * -1);
prevImg.addClass(this.act).siblings().removeClass(this.act);
//スライド
this.panelImg.stop().animate({ 'margin-left': 0 }, slideEl.speed,
() => {
this.panelImg.children().last().remove();
//スライドが終わったらクリックOK
this.click = true;
});
}
slideStart() {
//自動スライド
if (this.autoPlay === true) {
this.timerID = setTimeout(() => {
this.nextSlide();
this.slideStart();
}, slideEl.wait);
}
}
nextClick() {
//off()で重複防止
this.next.off('click').on('click', () => {
this.clearTimer();
this.nextSlide();
if (this.autoPlay === true) {
this.slideStart();
}
});
}
prevClick() {
this.prev.off('click').on('click', () => {
this.clearTimer();
this.prevSlide();
if (this.autoPlay === true) {
this.slideStart();
}
});
}
//サムネイルクリック
clickThumb() {
// off()で重複防止
this.thumb.off('click').on('click', (e) => {
// タッチ中の場合はクリックイベントを無視
if (isTouching) {
e.preventDefault();
return false;
}
this.clearTimer();
if (!this.click) return; // 早期リターン
this.click = false;
let point = this.thumb.index(e.currentTarget);
this.thumb.eq(point)
.addClass(this.act)
.siblings()
.removeClass(this.act);
let imgNum = this.thumb.eq(point);
this.panelImg.html(imgNum.html());
if (this.autoPlay === true) {
this.slideStart();
}
this.click = true;
});
}
init() {
if (!this.slideW()) {
console.error('Slide initialization failed');
return;
}
this.clickThumb();
this.nextClick();
this.prevClick();
this.slideStart();
}
}
const slide = new Slide();
slide.init();
// リサイズ処理
let resizeTimer;
$(window).on('resize', () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
slide.slideW();
}, 200);
});
//サムネイルのaリンク無効化、ページに飛んでしまうのを防止
//.is()メソッド(要素がセレクタにマッチするか判定)
if (slideEl.thumb.children().is('a')) {
$('a', slideEl.thumb).on('click', function () {
return false;
});
}
//touchイベント
/* 変数宣言 */
let moveX, posiX;
let isTouching = false; // タッチ中フラグを追加
/* 指が触れたか検知 */
// サムネイル領域を除外
slideEl.panel.on('touchstart', function (e) {
// サムネイルをタッチした場合は無視
if ($(e.target).closest(slideEl.thumbID).length > 0) {
return;
}
start_check(e);
});
/* 指が動いたか検知 */
slideEl.panel.on('touchmove', function (e) {
if ($(e.target).closest(slideEl.thumbID).length > 0) {
return;
}
move_check(e);
});
/* 指が離れたか検知 */
slideEl.panel.on('touchend', function (e) {
if ($(e.target).closest(slideEl.thumbID).length > 0) {
return;
}
end_check(e);
});
// サムネイルのタッチイベント対応
slideEl.thumb.off('touchend').on('touchend', (e) => {
if (moveX !== '') return; // スワイプ中は無視
e.preventDefault(); // デフォルトのクリックイベントを防止
$(e.currentTarget).trigger('click'); // 手動でクリックイベントを発火
});
//タッチ開始時の処理
function start_check(e) {
isTouching = true; // タッチ中フラグをON
//タイマーストップ
slide.clearTimer();
/* 現在の座標取得 */
posiX = getX(e);
/* 移動距離状態を初期化 */
moveX = '';
}
//スワイプ中の処理
function move_check(e) {
if (!isTouching) return; // タッチ中でなければ何もしない
e.preventDefault();// スクロール防止
if (posiX - getX(e) > slideEl.swipeThreshold) // swipeThreshold以上移動でスワイプと判断
{
/* 右→左と判断 */
moveX = 'left';
} else if (posiX - getX(e) < -slideEl.swipeThreshold) // swipeThreshold以上移動でスワイプと判断
{
/* 左→右と判断 */
moveX = 'right';
}
}
// タッチイベントの end_check 関数
//指が離れた時の処理
function end_check(e) {
if (!isTouching) return;
if (!slide.click) {
isTouching = false; // フラグをリセット
return;
}
if (moveX === 'left') {
slide.nextSlide();
}
else if (moveX === 'right') {
slide.prevSlide();
}
// 自動再生を再開(クラスのメソッドを使用)
if (slideEl.autoPlay === true) {
slide.slideStart();
}
// タッチ終了後、少し遅延してからフラグをリセット
// これによりclick イベントとの競合を防ぐ
setTimeout(() => {
isTouching = false;
}, 100);
}
function getX(e) {
if (e.originalEvent && e.originalEvent.touches && e.originalEvent.touches[0]) {
return e.originalEvent.touches[0].pageX;
}
return 0;
}
});
横幅100% 3枚並び自動スライド
$(function () {
//要素取得object
const slideEl = {
panelImg: $('.panelImg'),
thumb: $('#thumb li'), //要素格納
thumbID: '#thumb', //タッチイベント除外用に使う
next: $('#next'),
prev: $('#prev'),
act: 'act', // マーカー
speed: 800, //スライド速度
wait: 3000, //スライド間隔
itemW: $('.panel'), //1枚の画像幅スライドの基準
panel: $('.panel'), //タッチイベント領域
swipeThreshold: 20 // スワイプ判定距離(px)
}
class WideSlide {
constructor() {
this.panelImg = slideEl.panelImg;
this.thumb = slideEl.thumb;
this.next = slideEl.next;
this.prev = slideEl.prev;
this.act = slideEl.act;
this.speed = slideEl.speed;
this.wait = slideEl.wait;
this.imgW = 0;
this.itemW = slideEl.itemW;
this.imageNum = slideEl.thumb.length;//画像枚数
this.click = true;
this.timerID = null;
}
slideW() {
this.imgW = this.itemW.width();
// エラーハンドリング追加
if (!this.imgW || this.imgW === 0) {
console.error('Failed to calculate slide width');
// デフォルト値を設定してクラッシュを防ぐ
this.imgW = $(window).width(); // または適切なフォールバック値
return false; // エラーを示すためにfalseを返す
}
return true; // 成功時にtrueを返す
}
// タイマー管理を統一(クラスのメソッドとして)
clearTimer() {
if (this.timerID) {
clearTimeout(this.timerID);
this.timerID = null;
}
}
//次へ
nextSlide() {
let nextImg, cloneImg;
//カルーセルにする為、最後のliになったら最初のliを指定
if (this.click === true) {
this.click = false;
if (this.thumb.last().hasClass(this.act)) {
nextImg = this.thumb.first();
} else {
nextImg = this.thumb.filter('.' + this.act).next();
}
if (this.thumb.eq(this.thumb.length - 2).hasClass(this.act)) {
//要素クローン
cloneImg = this.thumb.first().children().clone();
} else {
//要素クローンは二つ先を取得
cloneImg = nextImg.next().children().clone();
}
this.panelImg.append(cloneImg);
//actの付替え
nextImg.addClass(this.act).siblings().removeClass(this.act);
//スライド
this.panelImg.stop().animate({ 'margin-left': this.imgW * -2 }, this.speed,
() => {
this.panelImg.children().first().remove();
this.panelImg.css('margin-left', this.imgW * -1);
this.click = true;
});
}
}
slideStart() {
this.timerID = setTimeout(() => {
this.nextSlide();
this.slideStart();
}, this.wait);
}
nextClick() {
this.next.off('click').on('click', () => {
this.clearTimer();
this.nextSlide();
this.timerID = setTimeout(() => { this.slideStart(); }, this.wait);
});
}
//前へ
prevSlide() {
let prevImg, cloneImg;
if (this.click === true) {
this.click = false;
//最初のliになったら最後のliを指定
if (this.thumb.first().hasClass(this.act)) {
prevImg = this.thumb.last();
} else {
prevImg = this.thumb.filter('.' + this.act).prev();
}
if (this.thumb.eq(1).hasClass(this.act)) {
//要素クローン
cloneImg = this.thumb.last().children().clone();
} else {
//要素クローン
cloneImg = prevImg.prev().children().clone();
}
this.panelImg.prepend(cloneImg).css('margin-left', this.imgW * -2);
prevImg.addClass(this.act).siblings().removeClass(this.act);
//スライド
this.panelImg.stop().animate({ 'margin-left': this.imgW * -1 }, this.speed,
() => {
this.panelImg.children().last().remove();
//スライドが終わったらクリックOK
this.click = true;
});
}
}
prevClick() {
this.prev.off('click').on('click', () => {
this.clearTimer();
this.prevSlide();
this.timerID = setTimeout(() => { this.slideStart(); }, this.wait);
});
}
thumbClick() {
//サムネイルクリック
let imgNum, imgPrev, imgNext;
this.thumb.off('click').on('click', (e) => {
this.clearTimer();
if (!this.click) return; // 早期リターン
this.click = false; // 連打防止
// タッチ中の場合はクリックイベントを無視
if (isTouching) {
e.preventDefault();
this.click = true; // フラグをリセット
return false;
}
let point = this.thumb.index(e.currentTarget);
//クリックした場所を検索
this.thumb.eq(point).addClass(this.act).siblings().removeClass(this.act);
switch (point) {
case this.imageNum - 1:
imgPrev = this.thumb.eq(this.thumb.length - 2);
imgNum = this.thumb.last();
imgNext = this.thumb.first();
break;
case 0:
imgPrev = this.thumb.last();
imgNum = this.thumb.first();
imgNext = this.thumb.eq(1);
break;
default:
imgPrev = this.thumb.eq(point - 1);
imgNum = this.thumb.eq(point);
imgNext = this.thumb.eq(point + 1);
}
this.panelImg.html(imgPrev.html() + imgNum.html() + imgNext.html());
// フラグをリセット
this.click = true;
// タイマーを再開
this.timerID = setTimeout(() => { this.slideStart(); }, this.wait);
});
}
init() {
if (!this.slideW()) {
console.error('Slide initialization failed');
return;
}
this.slideStart();
this.nextClick();
this.prevClick();
this.thumbClick();
}
}
const slide = new WideSlide();
slide.init();
//リサイズ
// リサイズのデバウンス追加
let resizeTimer;
$(window).on('resize', function () {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
slide.slideW();
}, 200);
});
//サムネイルのaリンク無効化、ページに飛んでしまうのを防止
//.is()メソッド(要素がセレクタにマッチするか判定)
if (slideEl.thumb.children().is('a')) {
$('a', slideEl.thumb).on('click', function () {
return false;
});
}
//touchイベント
/* 変数宣言 */
let moveX, posiX;
let isTouching = false; // タッチ中フラグを追加
/* 指が触れたか検知 */
// サムネイル領域を除外
slideEl.panel.on('touchstart', function (e) {
// サムネイルをタッチした場合は無視
if ($(e.target).closest(slideEl.thumbID).length > 0) {
return;
}
start_check(e);
});
/* 指が動いたか検知 */
slideEl.panel.on('touchmove', function (e) {
if ($(e.target).closest(slideEl.thumbID).length > 0) {
return;
}
move_check(e);
});
/* 指が離れたか検知 */
slideEl.panel.on('touchend', function (e) {
if ($(e.target).closest(slideEl.thumbID).length > 0) {
return;
}
end_check(e);
});
//タッチ開始時の処理
function start_check(e) {
isTouching = true; // タッチ中フラグをON
//タイマーストップ
slide.clearTimer();
/* 現在の座標取得 */
posiX = getX(e);
/* 移動距離状態を初期化 */
moveX = '';
}
//スワイプ中の処理
function move_check(e) {
if (!isTouching) return; // タッチ中でなければ何もしない
e.preventDefault();// スクロール防止
if (posiX - getX(e) > slideEl.swipeThreshold) // swipeThreshold以上移動でスワイプと判断
{
/* 右→左と判断 */
moveX = 'left';
} else if (posiX - getX(e) < -slideEl.swipeThreshold) // swipeThreshold以上移動でスワイプと判断
{
/* 左→右と判断 */
moveX = 'right';
}
}
//指が離れた時の処理
function end_check(e) {
if (!isTouching) return;
if (!slide.click) {
isTouching = false; // フラグをリセット
return;
}
if (moveX === 'left') {
slide.nextSlide();
}
else if (moveX === 'right') {
slide.prevSlide();
}
// 自動再生を再開
slide.timerID = setTimeout(() => { slide.slideStart(); }, slide.wait);
// タッチ終了後、少し遅延してからフラグをリセット
// これによりclick イベントとの競合を防ぐ
setTimeout(() => {
isTouching = false;
}, 100);
}
function getX(e) {
if (e.originalEvent && e.originalEvent.touches && e.originalEvent.touches[0]) {
return e.originalEvent.touches[0].pageX;
}
return 0;
}
});
モーダルスライド class ver.
//touchイベントとモーダル
$(function () {
const modalItem = {
next: $('#next'), //次へボタン
prev: $('#prev'), //前へボタン
act: 'act', //アクティブクラス
thumb: $('#thumb li'), //サムネイルリストli
modalBg: $('#overlay'), //モーダル背景
close: $('#close_modal'), //閉じるボタン
modal: $('.changeItem'), //モーダルコンテンツ
};
class MyModal {
constructor(item) {
this.thumb = item.thumb;
//次へ、前へボタンがある場合のみ設定
if (item.next && item.prev) {
this.next = item.next;
this.prev = item.prev;
} else {
this.next = null;
this.prev = null;
}
this.act = item.act;
this.modal = item.modal;
this.close = item.close;
this.modalBg = item.modalBg;
//連打クリック禁止条件
this.click = true;
}
//サムネイルクリック
clickModal() {
this.thumb.on('click', (e) => {
if (!this.click) return; // 早期リターン
this.click = false;
let point = this.thumb.index(e.currentTarget);
//e.currentTargetでクリックした要素を取得、thisと同じ作用
this.thumb.eq(point)
.addClass(this.act)
.siblings()
.removeClass(this.act);
let imgNum = this.thumb.eq(point);
this.modal.html(imgNum.html());
this.modalBg.stop().fadeIn(800, () => {
this.click = true;
// fadeIn完了後にナビを更新
if (this.next !== null && this.prev !== null) {
this.navChange();
}
});
});
}
//閉じる
closeModal() {
this.close.on('click', () => {
this.modalBg.stop().fadeOut(600);
});
}
//ナビの処理
navChange() {
if (this.thumb.last().hasClass(this.act)) {
this.next.hide();
this.prev.show();
} else if (this.thumb.first().hasClass(this.act)) {
this.prev.hide();
this.next.show();
} else {
this.prev.show();
this.next.show();
}
}
//次へ
nextSlide() {
const classAct = this.thumb.filter('.' + this.act);
let nextImg = classAct.next();
if (nextImg.length === 0) return; // nextImgが空の場合
this.modal.html(nextImg.html());
nextImg.addClass(this.act).siblings().removeClass(this.act);
}
//前へ
prevSlide() {
const classAct = this.thumb.filter('.' + this.act);
let prevImg = classAct.prev();
if (prevImg.length === 0) return; // prevImgが空の場合
this.modal.html(prevImg.html());
prevImg.addClass(this.act).siblings().removeClass(this.act);
}
clickNext() {
if (this.next !== null && this.prev !== null) {
this.next.on('click', () => {
this.nextSlide();
this.navChange();
});
}
}
clickPrev() {
if (this.next !== null && this.prev !== null) {
this.prev.on('click', () => {
this.prevSlide();
this.navChange();
});
}
}
//実行関数まとめる
behavior() {
this.clickModal();
this.closeModal();
this.clickNext();
this.clickPrev();
}
}
//モーダル本体の要素 呼び出しインスタンス
const myModal = new MyModal(modalItem);
myModal.behavior();
//touchイベント
/** 変数宣言 */
let moveX, posiX
/** 指が触れたか検知 */
modalItem.modalBg.on('touchstart', start_check);
/** 指が動いたか検知 */
// パッシブリスナー対応
modalItem.modalBg.on('touchmove', { passive: false }, move_check);
/** 指が離れたか検知 */
modalItem.modalBg.on('touchend', end_check);
//タッチ開始時の処理
function start_check(e) {
/** 現在の座標取得 */
posiX = getX(e);
/** 移動距離状態を初期化 */
moveX = '';
}
//スワイプ中の処理
function move_check(e) {
e.preventDefault();// スクロール防止
if (posiX - getX(e) > 20) {
if (modalItem.thumb.last().hasClass(modalItem.act)) {
moveX = 'stop';
} else {
moveX = 'left';
}
} else if (posiX - getX(e) < -20) {
if (modalItem.thumb.first().hasClass(modalItem.act)) {
moveX = 'stop';
} else {
moveX = 'right';
}
}
}
//指が離れた時の処理
function end_check() {
if (moveX === 'left') {
myModal.nextSlide();
myModal.navChange();
}
else if (moveX === 'right') {
myModal.prevSlide();
myModal.navChange();
}
}
function getX(e) {
//横方向の座標を取得
if (e.originalEvent && e.originalEvent.touches && e.originalEvent.touches[0]) {
return e.originalEvent.touches[0].pageX;
}
return 0;
}
});
前バージョンの記事
便利なプラグインがたくさんあるスライドショーですが、できれば自作のものを使いたいと思いjsで開発しました。
ベースを作って自分のストック用として手動、自動、モーダルと1セット揃えました。
この際、逃げ腰だったタッチイベントにも挑戦。
手動スライド
<div id="slidePanel">
<!-- 表示領域 -->
<div class="panel">
<figure class="panelImg">
<!-- デフォルトに画像一つ置いておく -->
<img src="./image/slide1.jpg" alt="">
</figure>
</div>
<!-- prevボタンとnextボタン -->
<div id="prev"><img src="./image/prev.svg" alt="前へ"></div>
<div id="next"><img src="./image/next.svg" alt="次へ"></div>
</div>
<!-- //.slidePanel -->
<!-- サムネイル スライド画像を格納 -->
<ul id="thumb">
<li class="thumbLi act"><img src="./image/slide1.jpg" alt=""></li>
<li class="thumbLi"><img src="./image/slide2.jpg" alt=""></li>
<li class="thumbLi"><img src="./image/slide3.jpg" alt=""></li>
<li class="thumbLi"><img src="./image/slide4.jpg" alt=""></li>
<li class="thumbLi"><img src="./image/slide5.jpg" alt=""></li>
<li class="thumbLi"><img src="./image/slide6.jpg" alt=""></li>
</ul>
普通だったらスライドのブロックに画像をliで書き出しますが、サムネイルのliに格納して非表示にしました。
actクラスでアクティブになっている要素と紐付けし、actが付与されたli直下のimgを取得します。
サムネをクリックした時、この方が分かりやすいと思ったもので。
・下準備 サイズ設定
//スライド一つ分の長さを取得
let imgW;
const panelImg = $('.panelImg');
//スライド幅のレスポンシブ
function slideW(){
if(window.innerWidth > 768) {
imgW = $('img',panelImg).width();
} else {
imgW = $(window).width();
}
}
slideW();
//次の画像をセットするので幅を追加しておく
panelImg.width(imgW * 2);
//リサイズ
$(window).on('resize',function(){
slideW();
panelImg.width(imgW * 2);
});
・nextボタンの実行
スライド中、クリック連打を防ぐ為if分で制御しました。
nextボタンが押されたら、actクラスは次のliへ付け変わります。
let click = true;
const next = $('#next');
const prev = $('#prev');
let nextImg, prevImg, cloneImg;
//次へ
function nextSlide(){
//連打クリック禁止
if(click == true){
click = false;
//actの次のliを取得
nextImg = $('.act').next();
//クローンしないと元データが消えてしまった
cloneImg = $(nextImg).children('img').clone();
panelImg.append(cloneImg);
//actの付替え
$(nextImg).addClass('act').siblings().removeClass('act');
//スライド
panelImg.stop().animate({'margin-left': imgW * -1},600,'swing',
function(){
$('img:first-child',this).remove();
$(this).css('margin-left', 0);
//クリックできる状態へ
click = true;
});
}
}
next.on('click',function(){
nextSlide();
//ナビの表示非表示
navChange();
});
・前へ
nextの逆です。
左側に画像を足すので、予めmarginをマイナス値に。
//前へ
function prevSlide(){
//連打クリック禁止
if(click == true){
click = false;
prevImg = $('.act').prev();
cloneImg = $(prevImg).children('img').clone();
//スライドブロックを画像一つ分マイナスに
panelImg.prepend(cloneImg).css('margin-left',imgW * -1);
$(prevImg).addClass('act').siblings().removeClass('act');
//スライドはmargin-leftを0に
panelImg.stop().animate({'margin-left': 0},600,'swing',
function(){
$('img:last-child',this).remove();
click = true;
});
}
}
prev.on('click',function(){
prevSlide();
//ナビの表示非表示
navChange();
});
・ナビの表示制御
最初だったらprevを非表示、最後だったらnextを非表示に。
ナビが隠れることによってスライドは不可になります。
navChange();
//ナビの処理
function navChange(){
if($('#thumb li:last-child').hasClass('act')) {
next.css('display','none');
prev.css('display','block');
} else if($('#thumb li:first-child').hasClass('act')) {
prev.css('display','none');
next.css('display','block');
} else {
prev.css('display','block');
next.css('display','block');
}
}
・サムネイルクリック
画像指定以外、ほぼnextと同じです。
//サムネイルクリック
let point,imgNum;
$('#thumb li').on('click',function(){
//連打クリック禁止
if(click == true){
click = false;
//クリックした場所を検索
point = $('#thumb li').index(this);
$(this).addClass('act').siblings().removeClass('act');
imgNum = $('#thumb li').eq(point);
//cloneではない方法で取ってみる
panelImg.append('<img src="'+ $('img',imgNum).attr('src') +'" alt="">');
panelImg.stop().animate({'margin-left': imgW * -1},600,'swing',
function(){
$('img:first-child',this).remove();
$(this).css('margin-left', 0);
click = true;
});
navChange();
}
});
・touchイベント
最後と最初の画像の場合、スワイプ制御したかったので、スワイプ不可の値として’stop’を代入しています。値は’left’と’right’以外は何でもいいです。
//touchイベント
let moveX, posiX;
/* 指が触れたか検知 */
$('.panel').on('touchstart', start_check);
/* 指が動いたか検知 */
$('.panel').on('touchmove', move_check);
/* 指が離れたか検知 */
$('.panel').on('touchend', end_check);
//タッチ開始時の処理
function start_check(e) {
/* 現在の座標取得 */
posiX = getX(e);
/* 移動距離状態を初期化 */
moveX = '';
}
//スワイプ中の処理
function move_check(e) {
if (posiX - getX(e) > 20) // 20px以上移動でスワイプと判断
{
/* 右→左と判断 */
if($('#thumb li:last-child').hasClass('act')){
//画像が最後だったら移動しない
moveX = 'stop';
} else {
moveX = 'left';
}
} else if (posiX - getX(e) < -20) // 20px以上移動でスワイプと判断
{
/* 左→右と判断 */
if($('#thumb li:first-child').hasClass('act')){
//画像が最初だったら移動しない
moveX = 'stop';
} else {
moveX = 'right';
}
}
}
//指が離れた時の処理
function end_check(e){
if(moveX == 'left'){
nextSlide(e);
navChange();
} else if(moveX == 'right'){
prevSlide(e);
navChange();
}
}
function getX(e) {
//横方向の座標を取得
return e.originalEvent.touches[0].pageX;
}
自動スライド
手動スライドにsetTimeoutをつけました。
エンドレスにループさせた為、navChange()は無くしてカルーセルにしています。
スワイプ不可の条件文も外しています。
function slideStart(){
nextSlide();
timerID = setTimeout(slideStart, 3000);
}
//タイマー起動
slideStart();
横幅100%自動スライド
ファーストビューでよく見かけるブラウザ幅100%で端が見えているタイプです。
cssはかなり変更しました。
<div class="panel">
<figure class="panelImg">
<img src="./image/slide_big6.jpg" alt="">
<img src="./image/slide_big1.jpg" alt="">
<img src="./image/slide_big2.jpg" alt="">
</figure>
</div>
スライド領域のデフォルト画像は3つセットにし、真ん中がアクティブになります。なので次の画像指定は2つ先のものを取得します。
nextメソッドを入れたセレクタを変数に代入し、再度nextメソッドを使います。
function nextSlide(){
//カルーセルにする為、最後のliになったら最初のliを指定
if($('#thumb li:last-child').hasClass('act')){
nextImg = $('#thumb li:first-child');
} else {
nextImg = $('.act').next();
}
if($('#thumb li:nth-last-child(2)').hasClass('act')){
//画像クローン
cloneImg = $('#thumb li:first-child').children('img').clone();
} else {
//画像クローンは二つ先を取得
cloneImg = $(nextImg).next().children('img').clone();
}
panelImg.append(cloneImg);
//actの付替え
$(nextImg).addClass('act').siblings().removeClass('act');
//スライド
panelImg.stop().animate({'margin-left': imgW * -2},800,'swing',
function(){
$('img:first-child',this).remove();
$(this).css('margin-left', imgW * -1);
click = true;
});
}
モーダルスライド
モーダルウィンドウが浮かび上がってからattrメソッドで切り替わる仕様です。
サムネイルに画像の他、htmlデータも格納しました。
<!-- オーバーレイ -->
<div id="overlay">
<!-- 表示領域 -->
<div id="slidePanel">
<!-- サムネのデータより呼び出し -->
<figure class="panelImg">
<!-- attrメソッドで差し替え -->
<img src="./image/slide1.jpg" alt="">
</figure>
<article id="modal_contents">
<!-- htmlメソッドで丸ごと上書き -->
</article>
<div id="prev"><img src="./image/prev.svg" alt="前へ"></div>
<div id="next"><img src="./image/next.svg" alt="次へ"></div>
<div id="close_modal">×</div>
</div>
<!-- //.slidePanel -->
</div>
<!-- //#overlay -->
・サムネイルclick
//サムネイルクリック
let point,imgNum;
$('#thumb li').on('click',function(){
//連打クリック禁止
if(click == true){
click = false;
point = $('#thumb li').index(this);
//クリックした場所を検索
$(this).addClass('act').siblings().removeClass('act');
imgNum = $('#thumb li').eq(point);
$('img',panelImg).attr('src',$('img',imgNum).attr('src'));
$('#modal_contents').html($('.modal_text',imgNum).html());
$('#overlay').stop().fadeIn(800,
function(){
click = true;
});
navChange();
}
});
//閉じる
$('#close_modal').on('click',function(){
$('#overlay').stop().fadeOut(600);
});
ソースはGithabにアップしています。class化はブランチしました。
https://github.com/mugikomugi/my_slide/tree/class_ver
まだまだ需要がありそうなスライドショー。
お得意様からオーソドックスなスタイルを要求されることもあったので
そんな時のためにストックとして保存しておくことにしました。


