ウィンドウ幅に合わせて画像をアニメーションで再配置する
海外のホームページを見ていたとき、ウィンドウ幅に応じて画像の羅列がカラム数を増減しつつアニメーション移動するというデザインを見かけました。思わず「かっけーー」と思い、思っただけでどう作っているのか等は考えなかったのですが、ここ最近で3件程、このようなデザインのご相談が有りました。
こういうのです。
どうせプラグインが有るんだろうとか、ソースコードをパクっちまえ、とかは自分の力にならないので、悩んでみる事にします。
- アニメーションするのだから、画像は絶対位置で配置し、指定した位置へアニメーションで移動させれば良いのだろう
- 画像をラップしている枠の幅を1列に配置したい画像枚数で割ればleftからの絶対位置は計算出来る
- 全体の枚数を1列の枚数で割れば行数が解るので、topからの絶対位置は計算出来る
- 案件によって表示する幅やカラム数が変わるだろうから、それは自由に指定したい
- とりあえず画像は真四角にして計算し易いようにしよう
- レスポンシブデザインに連携するようにしなくてはダメだ
- なら@mediaの幅をtriggerとしてアニメーションさせるようにするか
こんな感じで考えてみました。
/* * imagesLoaded() jquery plugin * https://gist.github.com/paulirish/268257 */ $.fn.imagesLoaded = function(callback){ var elems = this.filter('img'), len = elems.length, blank = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="; elems.bind('load.imgloaded',function(){ if (--len <= 0 && this.src !== blank){ elems.unbind('load.imgloaded'); callback.call(elems,this); } }).each(function(){ if (this.complete || this.complete === undefined){ var src = this.src; this.src = blank; this.src = src; } }); return this; }; (function($){ $.fn.moving = function(options) { var settings = $.extend( { media1: 5, media2: 4, media3: 2, trigger1:959, trigger2:768, trigger3:500, }, options); var imageRow = new Array; var animete = false; var onLoad = function(obj){ //画像データを配列化しておく var move = $("#"+obj.attr('id')+' img'); for(var i=0; i<move.length; i++){ imageRow[i] = move[i].src; } checkWindow(obj,null); } function checkWindow(obj,resize){ var move = "#"+obj.attr('id'); var movingWidth = $(move).width() * 1; //ブラウザの幅に応じて分割数をセット var row = settings.media1; if(movingWidth < settings.trigger1) //959px row = settings.media1; if(movingWidth < settings.trigger2) //768px row = settings.media2; if(movingWidth < settings.trigger3) //500px row = settings.media3; var imgSize = Math.floor(movingWidth / row); //movingの幅を分割数で割り、画像一枚の幅を計算 var imgLength = $(move+' img').length; //画像枚数 $("#container").height( Math.ceil((imgLength / row)) * imgSize); //総枚数を分割数で割った値に画像高さ(=幅)を掛けてmoving高さを計算 //初回表示の場合はいったん空にして後から再配置 if(!resize){ $(move+' li').remove(); } //画像配列を1列の枚数で分割 *array_chunkっぽいの var chunks = [], i = 0, n = imageRow.length; while (i < n) { chunks.push(imageRow.slice(i, i += row)); } var imgTop = 0; //縦の絶対位置 var dummy = 0; for(var i=0; i<chunks.length; i++){ for(var j=0; j<chunks[i].length; j++){ var posLeft = Math.floor(imgSize*j); //左からの位置 var posTop = Math.floor(imgTop); var dummyId = 'id'+dummy; //初期表示の場合はアニメーション無 if(!resize){ //初回はid振っておく $('<li id="'+dummyId+'"></li>').html('<img src="'+chunks[i][j]+'">').appendTo(move).css({"width":imgSize,"height":imgSize,"top":posTop,"left":posLeft}); } //ウインド幅変更時はアニメーションさせる else{ animete = true; $('#'+dummyId).css({"width":imgSize,"height":imgSize}); $('#'+dummyId).animate({ top: posTop,left: posLeft },{ complete: function(){ animete = false; $('#'+dummyId).stop(); $('#'+dummyId).queue([]); } }); } dummy++; } imgTop = imgTop+imgSize; } } var obj = $(this); var timer = false; $(window).resize(function(){ if(animete == true) return; //アニメーション中は実行しない //ウインドサイズ変更が終わったときに実行 if (timer !== false) { clearTimeout(timer); } timer = setTimeout(function() { checkWindow(obj,true); }, 200); }); $("body img").imagesLoaded(function(){ onLoad( obj );//初回実行 }); }; })(jQuery);
ちなみにCSSは以下の程度で。
ul { margin: 0px; padding: 0px; } ul li { position: absolute; margin: 0px; padding: 0px; list-style: none; } ul li img { width: 100%; height: auto; vertical-align: bottom; } #container { width: 1200px; position: relative; overflow: hidden; margin: 0px auto; } @media screen and (max-width: 959px) { #container { width: 96%; } }
HTMLの方で設定を行います。
$(function(){ $('#moving').moving({ media1: 5, media2: 3, media3: 2, trigger1:959, trigger2:768, trigger3:500, }); });
trigger*は移動アニメーションを実行するタイミングで、media*はその時の横列数となります。
短いソースコードなので解説はしませんが、このくらいシンプルなのを書いておけば応用が効きそうです。あと、画像枚数が多かったり重たいと読込時にみっともないので、imagesLoaded() jquery pluginを使わせて頂いています。
ウィンドウの幅を変えるってケースがいったいどれくらいあるのだろうか?また、そのときアニメーションさせる事で、どのような効果が得られるのか?という点には疑問を持ちますが、格好良いことには間違いありませんので、何かの参考になれば良いかなぁ。