POPSブログ

jQuery レスポンシブ・スライダー、エンドレスタイプ

405

  Category:  jquery2017/02/12 pops 

前頁のレスポンシブ・スライダーは説明のために簡略しています。コントロールボタン付き、エンドレスタイプ、サムネール表示の切り替えが出来る様に機能を拡張してみました。通常のレスポンシブデザインHTML上でも動作します。その振る舞いなどを調べてみます。一言でいえば大変です。

 

responsive

 

TOP画像Slide処理エンドレスタイプのテスト

前頁のレスポンシブ・スライダーを、すこし機能拡張しただけで、原則的に構造など代わりはありません。
実際に色々作り、その差異を調べてみます。TOP画像としていますが画像幅を縮めれば他の場所でも利用できます。

 

max-widthを使用した形

ここより本題に入ります。レスポンシブにはmax-widthを使用すれば対処できます。下記に構造を示します。
通常「お利口」な方はプラグインなどを利用します。自作する方はほぼ居ないようです。

 

DEMO

通常のHTML5ページでみてみます。ブラウザ幅を変更して確認ください。

● 通常デモページ (http://pops-web.com/main/baserhtml/test002.html) : 通常ページでの表示デモ

● スマートフォン等の表示 : 確認ツールで見た場合の表示

 

構造と簡単な説明

図示すれば下記の様になる。(window内に収まっている場合)

zu

 

以下、デモの html css js と簡単な説明です。position設定の違いによる振る舞いに注意ください。
基本的な説明は「前ページ」をも参照ください。以下説明は「前ページ」説明と重複もあります。

【参照】当方の記事: jQuery レスポンシブスライダー処理


1. ul要素はliを横に並べるため、position:absolute です。カラム落ちしない様に長さが重要に成ります。
(100%で目一杯伸びるわけではない、動的に常に横幅を設定しなければなりません)
position:absolute設定でウインドウ外に空白が出来ますが、これは除去出来ます。
2. li要素は position:relative で float させます。ul要素の幅があれば、常に横並びになります。
(通常のHTMLと作り方が同じであるゆえ、反面問題がおき易いようです、li要素はをposition:absoluteで並べる方法もありますがこれは別途後日に説明します)
3. max-widthはli要素に設定します。(但し、不安定な場合は外側div要素にもmax-widthを設定する)
4. 但し、画像は常に大きさを修正しなければ成りません。自動計算表示では無い点に注意。
(li要素が position:absolute の場合では、画像の大きさは自動計算表示に成るので便利なとき有り)
5. 全体をスライド移動するには常に(window幅が変わった場合)、li要素(画像)の位置を計算しなければなりません。
6. エンドレスタイプに仕上げるため最初の画像を最後にappendして、アニメとCSSゴマカシで処理します。
7. サムネール表示のPagerは構造は同じで画像を入れただけです。
8. コントロールはspan要素で構成しました。(問題がおきにくいし簡素です)


● 対応ブラウザと検証

html5.js、css3-mediaqueries.jsを読み込んでいますので、仕方なく「IE」をも検証しました。
当方としては別段IEに対応する気は毛頭有りませんし、特にIE 9 8 7はもはや対応すべきではなく「排除」すべきです。


1. 一応、モダンブラウザおよび、IE 11 10 9 8 7まで表示可能です。(IEは問題ありすぎ...)
(IE11はモダンブラウザに属するが、CSS3、Canvas対応などでは他より劣るので余り対応したくない)
2. 検証はブラウザ付属ので「ペロッパーツール」および「確認ツール」で行いました。(一部、iphone6で確認)
3. デモページで使用のjQueryは jquery-1.11.3.min.js です。


HTML CSS JS

下記にHTML CSS JSを提示します。CSS JSの名前は自由です。一般的にリスト状に下記の様に書くのが好まれます。

HTML


<div id="sliderBox">
 <ul id="sliderBase">
  <li class="image-parts"><a href="#"><img src="/main/baserhtml/images/mainImage-01.jpg" /></a></li>
  <li class="image-parts"><a href="#"><img src="/main/baserhtml/images/mainImage-02.jpg" /></a></li>
  <li class="image-parts"><a href="#"><img src="/main/baserhtml/images/mainImage-03.jpg" /></a></li>
  <li class="image-parts"><a href="#"><img src="/main/baserhtml/images/mainImage-04.jpg" /></a></li>
  <li class="image-parts"><a href="#"><img src="/main/baserhtml/images/mainImage-05.jpg" /></a></li>
 </ul>
</div>

サンプルJS CSS

CSS


/* 重要 */
#wrappr{
/*overflow:hidden;*/
}

#header,#main{
width:100%;
height:auto;
margin:0 auto;
text-align:center;
background-color:#eee;
}

/* 本体 */
#sliderBox{
position:relative;
width:880px;
height:380px;
margin:0 auto;
padding:0;
background-color:#000000;
overflow:hidden; /* MASK */
}
/* 上書き */
@media screen and (max-width:879px){
	#sliderBox{
		width:100%;
	}
}

#sliderBase{
position:absolute;
top:0;left:0;
width:100%;/*暫定*/
height:auto;
margin:0;
padding:0;
}
#sliderBase li{
list-style:none;
position:relative;
max-width:880px;
height:auto;
margin:0;
padding:0;
float:left;
}

#sliderBase li img{
width:100%;
height:auto;
margin:0;
padding:0;
}

/*span構成コントロール*/
#sliderBox #base-prev{
display:block;
position:absolute;
top:0;left:0;
width:20px;
height:100%;
margin:0;
padding:0;
background:url('/main/baserhtml/images/arrow_left_a3.png') no-repeat center center;
cursor:pointer;
z-index:100;
}
#sliderBox #base-next{
display:block;
position:absolute;
top:0;right:0;
width:20px;
height:100%;
margin:0;
padding:0;
background:url('/main/baserhtml/images/arrow_right_a3.png') no-repeat center center;
cursor:pointer;
z-index:101;
}

/* btn */
#btnBox{
width:100%;
height:auto;
margin:10px auto;
text-align:center;
z-index:1000;
}
#btnBox span{
width:100%;
height:auto;
margin:5px;
font-size:16px;
color:#000000;
cursor:pointer;
}
#btnBox span.active{
color:#ff0000;
}

#btnBox span img{
width:40px;/*40x18*/
height:18px;
border:#fff 1px solid;
}
#btnBox span img.active{
border:#f00 1px solid;
}

JS


//simple-slider4.js
//画像slider、通常処理形式エンドレスタイプ
//コントロールspan構成/a-linkつき
//IE7スクロールOK

(function($){

  $(function(){

    function init(){

	//画像の大きさ
	var image_w=880;
	var image_h=380;

	//時間
	var cycle_time=8000;
	var slide_time=800;

	//コントロールBTNの使用
	var ctrlbtn_use=true;
	//エンドレスタイプtrue
	var endless=true;
	//BTNグループの使用true、不使用false
	var hitbtn_use=true;
	//サムネール使用true、ボタンはfalse
	var thumb_use=true;
	//サムネールの大きさはCSSで設定

	//オブジェクト
	var box_elm_wrap=$('#sliderBox');
	var box_elm=$('#sliderBase');
	var img_elm=$('#sliderBase li');

	//変数、未使用あり
	var image_max=img_elm.length;
	var timerID=null;
	var image_no=0;
	var keep_no=0;
	var image_urls=[];
	var slide_pos=[];
	var slideelm=[];//オブジェクト保存
	var item_w;
	var cross_url="";
	var animeflag=false;
	var slideflag=true;//左右
	var endless_elm;
	var key_name="";

	//BTNオブジェクト
	var hit_btns=[];

	var width_v=image_w;
	var height_v=image_h;
	var keep_w=image_w;

	//liエレメント追加
	if(endless){
		box_elm.append('<li id="endless" class="image-parts"><a href="javascript:void(0);"><img /></a></li>');
		endless_elm=$('#endless');
		var url=img_elm.eq(0).find("a").children("img").attr('src');
		endless_elm.find("a").children("img").attr('src',url);
		img_elm=box_elm.children("li");//再認識させる
	}

	//URL取得位置計算
	box_elm.find('li').each(function(i){
		//仮位置
		var posxs=width_v*i;
		slide_pos[i]=posxs;//位置保存
		slideelm[i]=img_elm.eq(i);//オブジェクト保存
		image_urls[i]=$(this).find("a").children("img").attr('src');//URL保存
	});

	//コントロールBTN/span構成
	if(ctrlbtn_use){

		//append #sliderBase
		box_elm_wrap.append('<span id="base-prev"></span>');
		box_elm_wrap.append('<span id="base-next"></span>');

		//オブジェクト
		var next_elm=$('#base-next');
		var prev_elm=$('#base-prev');

		//click-action
		next_elm.click(function(){
			//アニメ中はキャンセル
			if(animeflag){return false}
			//タイマーclear
			clearTimeout(timerID);
			next_set();
			return false
		});
		prev_elm.click(function(){
			//アニメ中はキャンセル
			if(animeflag){return false}
			//タイマーclear
			clearTimeout(timerID);
			prev_set();
			return false
		});

	}
	//BTNグループaction-set
	if(hitbtn_use){

		//後ろにappendする
		box_elm_wrap.after('<div id="btnBox"></div>');
		var mark="&#9679;";
		//make-btn
		var btnhtml="";
		for (var i=0; i < image_max; i++) {

			//ID名を付ける
			var name="hitbtn"+i;
			if(thumb_use){
				thumb='<img class="thumb" src="'+ image_urls[i] +'" />';
				btnhtml += '<span id="' + name +'">' + thumb + '</span>';
			}else{
				btnhtml += '<span id="' + name +'">' + mark + '</span>';
			}

		}
		//append #btnBox
 		$('#btnBox').append(btnhtml);
		btnhtml="";

		//action
		$('#btnBox').find('span').each(function(i){
			$(this).click(function(){hitno_set(i);});
		});
		//オブジェクト保存
		for (var i=0; i < image_max; i++) {
			hit_btns[i]=$('#hitbtn'+i);
		}

		//最初をactiveに
		if(hitbtn_use){
			if(thumb_use){
				hit_btns[0].children("img").addClass('active');
			}else{
				hit_btns[0].addClass('active');
			}
		}

	}

	//初期化、寸法きめ
	image_no=0;
	resizeFunc();

	//スタート遅延なし
	set_timer();

	//エフェクト処理
	function effectImage(){

		animeflag=true;
		//位置情報
		var pos_x=width_v;
		var pos_y=height_v;

		if(hitbtn_use){
			if(thumb_use){
				hit_btns[keep_no].children("img").removeClass('active');
			}else{
				hit_btns[keep_no].removeClass('active');
			}
		}

		var pos_x=width_v*image_no;

		//endless事前処理
		if(endless){
			var endless_pos=slide_pos[image_max];
			if(key_name=='next'){pos_x=endless_pos;}
			if(key_name=='prev'){pos_x=width_v*image_no;box_elm.css({'left':-endless_pos});}
		}

		//エフェクト
		box_elm.animate({'left':-pos_x},slide_time,function(){

			//endless
			if(endless){
				if(key_name=='next'){box_elm.css({'left':0});}
			}

			//処理番号保存
			keep_no=image_no;
			animeflag=false;
			slideflag=true;
			key_name="";

			if(hitbtn_use){
				if(thumb_use){
					hit_btns[image_no].children("img").addClass('active');
				}else{
					hit_btns[image_no].addClass('active');
				}
			}

			set_timer();

		});

	}

	//次ぎの開く要素を計算
	function next_set() {
		if(image_no==image_max-1){key_name="next";}
		slideflag=true;
		//次ぎの番号
		image_no +=1;
		if (image_no > (image_max-1)) {image_no=0;}
		effectImage();
	}
	//戻る要素を計算
	function prev_set() {
		if(image_no==0){key_name="prev";}
		slideflag=false;
		//戻る番号
		image_no -=1;
		if (image_no < 0) {image_no=image_max-1;}
		effectImage();
	}
	//指定番号で計算
	function hitno_set(no) {

		//表示番号ならキャンセル
		if(no==keep_no){return false}
		//アニメ中はキャンセル
		if(animeflag){return false}
		//タイマーclear
		clearTimeout(timerID);
		//番号
		image_no=no;
		effectImage();
		return false
	}

	//タイマーSET
	function set_timer(){

		//一旦切ってからセット
		clearTimeout(timerID);
		timerID=setTimeout(next_set,cycle_time);

	}

	//window-resize
	$(window).resize(resizeFunc);

	//スライドのためBOXの大きさ設定
	function resizeFunc(){

		window_width=$(window).width();

		//BOX、画像修正
		if(window_width<image_w){

			var w=window_width;
			var ratio=window_width/image_w;
			var h=image_h*ratio;
			var ul_w=w*image_max;
			if(endless){ul_w=w*(image_max+1);}

			//現在の大きさ
			width_v=w;
			height_v=h;

			box_elm_wrap.css({'width':w,'height':h});
			box_elm.css({'width':ul_w,'height':h});

			//画像幅位置修正
			chg_pos(width_v);

		}else{

			var ul_w=image_w*image_max;
			if(endless){ul_w=image_w*(image_max+1);}
			//現在の大きさ
			width_v=image_w;
			height_v=image_h;

			box_elm_wrap.css({'width':image_w,'height':image_h});
			box_elm.css({'width':ul_w,'height':image_h});

			//画像幅位置修正
			chg_pos(width_v);

		}

	}

	//画像幅位置修正
	function chg_pos(w_v){
		//幅変更がなければ実行しない
		if(keep_w==w_v){return}
		//処理
		var maxlen=image_max;
		if(endless){maxlen=image_max+1;}
		//移動位置再計算
		for (var i=0; i < maxlen; i++) {
			slide_pos[i]=w_v*i;
			slideelm[i].find("img").css({'width':w_v});//画像幅
		}
		//現在番号位置にBaseをセット、動いた場合の修正
		var psx_v=slide_pos[keep_no]*-1;
		box_elm.css({'left':psx_v});
		keep_w=w_v;
	}

    }//init end

    //--------------------------

    //遅延/IE7
    setTimeout(function() {
      init();
    },10);

  });

})(jQuery);


HTML5でのテスト表示結果

通常デモページ (http://pops-web.com/main/baserhtml/test001.html) のHTML構成を違わせてテストした結果、モダンブラウザおよび IE7-11 まで表示結果はOKでした。

重要、スライダー部分、右外側に「空白」が出来る場合がありますが除去の処理です。body直下の全体をラップした要素に overflow:hidden 設定すれば除去出来ます。「空白」の出ない処理方法もありますが...


#wrappr{
overflow:hidden;
}

つまり、max-width をそのまま単独で使用すれば、構造によっては右外側に「空白」が出来る。

media screen設定を上書きすれば、直りますが...

@media screen設定の場合。

スライダーにmedia screen設定の場合は下記の様になるが、余りこのような設定で使用する事は無いと思う。
この場合、max-widthは media screen に設定されますので要素の幅は width:880px となる。

また個別にmedia screen 設定されましたので右に空白は出ません。心配な方はそのままでも結構です。HTML表示に影響は有りません。


● max-width設定の場合


max-width設定の場合、空白がでる

#sliderBox{
position:relative;
max-width:880px;
height:380px;
margin:0 auto;
padding:0;
background-color:#000000;
overflow:hidden; /* MASK */
}

空白除去のため必要です
#wrappr{
overflow:hidden;
}

● @media screen設定の場合 (デモはこちらを設定しました)


@media screen設定の場合、空白が出ない

#sliderBox{
position:relative;
width:880px;
height:380px;
margin:0 auto;
padding:0;
background-color:#000000;
overflow:hidden; /* MASK */
}
/* 上書き */
@media screen and (max-width:879px){
	#sliderBox{
		width:100%;
	}
}

スライダーに@media screen設定のため
下記の空白除去は原則不要です
#wrappr{
overflow:hidden;
}

エンドレスタイプの処理

横に連続に画像を並べた場合に最後の画像から、最初の画像に移動する時全画像をスライドすることになります。
これはこれで面白みがありますが、ちょっとセワシゲです。
最初の画像を最後の次に配置しておけばエンドレスの様にアニメ出来ます。


1. 初期時にli要素の最後に、li要素をappendして、最初の画像を入れます。
2. 最後の画像になったら、追加画像にスライドして、CSSで最初の画像に戻ります。
3. prevはその反対になるようにアニメとで処理します。
4. サムネールボタンのアクションは従来と同じです。


処理方法などは色々ありますが、下記の様に処理。urlはこの後に入れても良い、次の処理でimage_urls[0]に入っている。
一瞬の表示用画像で有るので「a要素」は空です。


//liエレメント追加
if(endless){
	box_elm.append('<li id="endless" class="image-parts"><a href="javascript:void(0);"><img /></a></li>');
	endless_elm=$('#endless');
	var url=img_elm.eq(0).find("a").children("img").attr('src');
	endless_elm.find("a").children("img").attr('src',url);
	img_elm=box_elm.children("li");//再認識させる
}

clone()でコピーして利用する場合は下記のようになる、振る舞いは同じ。a-linkは、1番目と同じになります。


if(endless){
	var clone=img_elm.eq(0).clone(true);
	box_elm.append(clone);
	img_elm=box_elm.children("li");//再認識させる
	img_elm.eq(image_max).attr('id','endless');//ID
	endless_elm=$('#endless');
}

コントロールボタンの処理

NEXT PREV、つまり「次に」「戻る」ボタンです。


1. レスポンシブ・スライダーでのNEXT PREVボタンはほとんどが「画像の上に配置」されています。
2.「画像の外側に配置」すると処理が大変になるから手抜きしているのです。
3.「矢印」は背景に常時、透過PNG画像を中央表示している例が多い。
(スマートフォンはHOVERアクションが機能しないなどの理由で常時表示が無難)
4.「矢印」収納のBOXを「div要素」で作ると高さをその都度調整しなければならない場合が多い。
(マスクが効かない場合がある、下のPagerを被いボタンが機能しないことがあるので注意)
5. 「span要素」で作ると高さの調整がいらないので、こちらで作成しました。


何ら変哲のない作り方です。アニメスライド実行中は、機能しないようにしている。


//コントロールBTN/span構成
if(ctrlbtn_use){

	//append #sliderBase
	box_elm_wrap.append('<span id="base-prev"></span>');
	box_elm_wrap.append('<span id="base-next"></span>');

	//オブジェクト
	var next_elm=$('#base-next');
	var prev_elm=$('#base-prev');

	//click-action
	next_elm.click(function(){
		//アニメ中はキャンセル
		if(animeflag){return false}
		//タイマーclear
		clearTimeout(timerID);
		next_set();
		return false
	});
	prev_elm.click(function(){
		//アニメ中はキャンセル
		if(animeflag){return false}
		//タイマーclear
		clearTimeout(timerID);
		prev_set();
		return false
	});

}

● HOVERアクションについての考察

スマートフォンでは、HOVERアクションは機能しませんので使用しては居ませんが、仮にPC向けに必要な場合は、上のコントロールボタンの様に、div要素、span要素などで挿入します。


1. 透過BOX(背景が透明)の場合、IE系ではHOVERアクションを認識しない場合が多い。
2. 背景に透過PNG画像などを入れると、全てのブラウザでHOVERアクションを認識する。
3. 上記のNEXT等のBOX全体が透過PNG画像があるだけでアクションを認識しているのと同じです。
4. 画像の上に配置すれば、当然画像のa-linkは機能しません。
5. span要素で作った方が、処理し易い。



//append #sliderBase
box_elm_wrap.append('<span id="hover-box"></span>');

//オブジェクト
var hover_elm=$('#hover-box');

//hover-action
hover_elm.hover(
	function(){
		//処理
	},
	function(){
		//処理
	}
);

----------------------------------------
SPAN CSS例

#hover-box{
display:block;
position:absolute;
top:0;left:0;
width:100%;
height:100%;
background:url('透過PNG画像のURL') repeat;
z-index:99;
}

Pagerボタンの作成

Pagerボタンの作成は「span」要素で行っている。floatせずに横に並ぶので便利である。スマートフォンを考慮してほとんどが画像数最大5-6個で使用されるので「span」要素構成が効率的と思う。
「active」クラス処理のため「span」要素にIDを付与している。この方が処理が早くなる。

注意、Pagerボタンはスライダー本体の外側に配置されています。表示形態はJS先頭で設定可能です。
サムネールの大きさ間隔などはCSSで設定します。

現在、Pagerボタンは「●」を表示している。四角なら「■」、&#9632; である。

画像サムネール表示なら「img」要素を入れればよい。あとで事前に保存している「画像URL」をattr()すれば表示出来る。



if(hitbtn_use){

	//後ろにappendする
	box_elm_wrap.after('<div id="btnBox"></div>');
	var mark="&#9679;";
	//make-btn
	var btnhtml="";
	for (var i=0; i < image_max; i++) {

		//ID名を付ける
		var name="hitbtn"+i;
		if(thumb_use){
			thumb='<img class="thumb" src="'+ image_urls[i] +'" />';
			btnhtml += '<span id="' + name +'">' + thumb + '</span>';
		}else{
			btnhtml += '<span id="' + name +'">' + mark + '</span>';
		}

	}
	//append #btnBox
 	$('#btnBox').append(btnhtml);

	省略

}

● サムネールのCSS

サムネールとボタンではCSSが違います。サムネールの大きさは表示を確認して決めてください。
activeの場合は下記の様に簡単に設定しています。変更は自由。


#btnBox span img{
width:40px;/*40x18*/
height:18px;
border:#fff 1px solid;
}
#btnBox span img.active{
border:#f00 1px solid;
}

JS最後の遅延処理

主にIE系ですが、画像を読み込んでからでなければ、上手く動作しない場合など1秒位遅延させると良い場合があります。その対策で付けていますが、今回は必要ではありませんでした。


//遅延/IE7
setTimeout(function() {
 init();
},10);

 

使用画像

原則、使用者が用意します。(880x380サイズ) : サンプルでの使用画像


透過PNG画像、下に来る画像の色合いによっては認識しづらい場合が有ります。下は自作品で、品質は良くない。
別画像の必要なかたは自作するか、ネット上でプラグイン付属画像をパクッテください。

透過画像 透過画像 透過画像 透過画像 透過画像 透過画像 透過画像 20x20透過画像
---


基本的な説明は「前ページ」を参照ください。

【参照】当方の記事: jQuery レスポンシブスライダー処理


 

モット効率的な方法もあるかも知れませんが、まだ判りません。誰か考えてください。
今回はコントロールボタンを画像内側に表示しました。簡単ですが、以上です。

 


[ この記事のURL ]


タグ:html5 , css3 , Slider , jquery

 

ブログ記事一覧

年別アーカイブ一覧



[1]