ロゴ
HOME > jQuery・Javascript > ウィンドウ幅に合わせて画像をアニメーションで再配置する

ウィンドウ幅に合わせて画像をアニメーションで再配置する

2017年07月10日

海外のホームページを見ていたとき、ウィンドウ幅に応じて画像の羅列がカラム数を増減しつつアニメーション移動するというデザインを見かけました。思わず「かっけーー」と思い、思っただけでどう作っているのか等は考えなかったのですが、ここ最近で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を使わせて頂いています。

ウィンドウの幅を変えるってケースがいったいどれくらいあるのだろうか?また、そのときアニメーションさせる事で、どのような効果が得られるのか?という点には疑問を持ちますが、格好良いことには間違いありませんので、何かの参考になれば良いかなぁ。