>  기사  >  웹 프론트엔드  >  Waterfall Flow 레이아웃의 jQuery 구현에 대한 자세한 설명(PC 및 모바일)_jquery

Waterfall Flow 레이아웃의 jQuery 구현에 대한 자세한 설명(PC 및 모바일)_jquery

WBOY
WBOY원래의
2016-05-16 15:34:301355검색

폭포 흐름 레이아웃은 오늘날 PC나 휴대폰과 같은 모바일 장치에서 이미지를 표시하는 매우 일반적인 방법이 되었습니다. 레이아웃 그림에는 대략 세 가지 스타일이 있습니다. 동일한 높이 및 동일한 너비, 동일한 너비 및 동일하지 않은 높이, 동일한 높이 및 동일하지 않은 너비 다음으로, 동일한 너비 및 동일하지 않은 높이의 가장 일반적인 형식을 예로 들어 보겠습니다.

Baidu 사진을 예로 사용합니다.

이것은 PC에서 흔히 사용되는 폭포 흐름 레이아웃 방법입니다. 다음으로 요소가 Baidu 사진에서 어떻게 레이아웃되어 있는지 살펴보겠습니다.

보시다시피 실제로는 너비가 같은 여러 개의 열 컨테이너가 포함되어 있으며 이미지는 계산을 통해 다른 컨테이너로 푸시됩니다. 본 글에서 소개하는 표시 방법은 위치 지정을 통한 최종 레이아웃 표시 방법은 다르지만 이전 알고리즘은 비교적 유사합니다.

살려보세요

먼저 다음 스타일의 여러 유닛을 본문에 작성하고 "상자"를 왼쪽에 띄웁니다.

<div class="box">
 <img class="img" src="./resource/images/1.jpg" />
 <div class="desc">Description</div>
</div>
<div class="box">
 <img class="img" src="./resource/images/2.jpg" />
 <div class="desc">Description</div>
</div>
<div class="box">
 <img class="img" src="./resource/images/3.jpg" />
 <div class="desc">Description</div>
</div>

다음 효과를 얻습니다:

다음:

var boxArr = $('.box'),
  num = Math.floor(document.body.clientWidth / boxArr.eq(0).outerWidth(true)),
 columnHeightArr = [];
columnHeightArr.length = num;
boxArr.each(function(index, item) {
 if (index < num) {
 columnHeightArr[index] = $(item).outerHeight(true);
 } else {
 var minHeight = Math.min.apply(null, columnHeightArr),
 minHeightIndex = $.inArray(minHeight,columnHeightArr);

 $(item).css({
 position: 'absolute',
 top: minHeight,
 left: boxArr.eq(minHeightIndex).position().left
 });
 }
}); 

위 코드는 대략 다음과 같습니다.

1. 먼저 브라우저에서 한 줄에 수용할 수 있는 이미지 수(num)를 계산합니다. 여기서는 externalWidth가 사용되며 여백, 패딩을 포함한 요소의 모든 상자 모델 속성 크기가 사용됩니다. , 테두리가 반환됩니다.

2. 각 열의 높이를 저장하는 배열(columnHeightArr)을 만듭니다. 배열의 길이는 num 값입니다.

3. 모든 사진을 탐색하고 첫 번째 행의 사진 높이를 열 높이 배열(columnHeightArr)에 저장합니다. 두 번째 행부터 시작하여 모든 열의 최소 높이(minHeight)를 계산합니다. (최소 높이 인덱스). 그런 다음 최소 높이의 열 아래 두 번째 행부터 이미지를 배치하면 효과는 다음과 같습니다.

사진을 올바른 위치에 배치했지만 모든 사진이 같은 위치에 배치된 것을 볼 수 있습니다. 이는 사진을 배치한 후 기둥의 높이를 늘려야 하기 때문입니다.

var boxArr = $('.box'),
 num = Math.floor(document.body.clientWidth / boxArr.eq(0).outerWidth(true)),
 columnHeightArr = [];
columnHeightArr.length = num;

boxArr.each(function(index, item) {
  if (index < num) {
    columnHeightArr[index] = $(item).outerHeight(true);
  } else {
    var minHeight = Math.min.apply(null, columnHeightArr),
 minHeightIndex = $.inArray(minHeight, columnHeightArr);

    $(item).css({
   position: 'absolute',
   top: minHeight,
   left: boxArr.eq(minHeightIndex).position().left
 });

    columnHeightArr[minHeightIndex] += $(item).outerHeight(true);
  }
});

결과가 정확합니다.

참고: 위 코드는 window.onload 메서드에서 실행되어야 합니다. 페이지의 모든 이미지 리소스가 로드될 때만 각 이미지의 높이가 변경되기 때문입니다. 효율적이다.

따라서 매우 심각한 문제가 발생할 수 있습니다. 네트워크가 좋지 않으면 사진이 완전히 로드되지 않고 사진이 불완전하게 표시되고 높이가 누락됩니다. 이는 모바일 측에서 명백합니다. 그리고 더 많이 로드하면 새로 추가된 이미지가 로드되었는지 판단하기가 더 어려워집니다.

실제 제작에서는 처음부터 HTML로 이미지를 작성할 수 없기 때문에 보통 아래와 같은 방법으로 합니다.

우선, 이미지 주소를 얻을 때 이미지의 너비와 높이도 가져와야 합니다. 이는 서버 백엔드에서는 어렵지 않습니다. 백엔드 형제에게 이미지의 너비와 높이 데이터 철자를 요청하면 됩니다. 이미지를 JSON으로 변환하여 전달하세요. ;

* 다음으로 매우 실용적인 작은 요령 을 소개하겠습니다. 요소의 크기가 어떻게 변하더라도 비율은 그대로 유지됩니다. 항상 일관성을 유지하세요. 이 기술은 요소가 반응형으로 만들기 위해 백분율을 사용하는 경우가 많기 때문에 모바일에서 특히 효과적입니다.

휴대폰 페이지에 이미지가 있는 경우 너비는 화면의 절반이어야 하며 가로세로 비율은 어떤 해상도에서도 휴대폰에서 변경되지 않고 유지되어야 합니다. 어떻게 하나요? 요소에 다음 특성을 설정합니다.

.box {
 width: 50%;
 height: 0;
 padding-bottom: 100%;
}

不设置高度,而是用padding“挤”出元素高度,而padding的百分比值都是基于父级容器的宽度。padding需要挤多少呢?就是宽度乘以高宽比(width和padding值均为百分比值),这就是我们为什么需要获得图片尺寸的原因。

效果:

可以看到在chrome手机模拟器中ipone4和肾6Plus的显示效果是完全一样的。在手机页面中宽是固定的,而高会随着页面内容的多少而变化,这个技巧利用元素padding百分比的值其实是基于其父级容器的宽,将高的值巧妙的转化成与宽相关。

说到现在可能有人终于忍不住要问了,讲了这么多和瀑布流有什么关系!简单就是一句话,我们要抛弃 img 标签,而采用背景图的方式。为了使用背景图,就得保持元素的比例永远与图片保持一致。

通过这种方式,可以不用判断图片都加载完毕,直接产生一些与图片同比例的div,再为其设置背景图,如下:

这里比如最外层的box宽度为220px,里面的img元素宽度就可以为100%,高度就可以通过padding挤出了。 

懒加载

使用背景图的方式还有好处那就是可以比较方便的实现懒加载。那什么是懒加载呢?就是当元素在我们的视野中时才展示图片,滚动时屏幕下方的图片并不展示,这可以很好的增加加载速度提升体验。

首先我们给最外层的box增加一个box-item类名(之后有用),将图片url并不设置给backgroundImage属性,而是赋给一个自定义属性:data-src

<div class="box box-item">
 <div class="img" data-src="./resource/images/1.jpg"></div>
 <div class="desc">Description</div>
</div>

接下来我们编写懒加载函数:

function lazyLoad() {
  var boxArr = $('.box-item');
  
  boxArr.each(function(index, item) {
    var viewTop = $(item).offset().top - $(window).scrollTop(),
 imgObj = $(item).find('.img');

 if ((viewTop < $(window).height()) && (($(item).offset().top + $(item).outerHeight(true)) > $(window).scrollTop())) {
   imgObj.css('backgroundImage','url('+imgObj.attr("data-src")+')').removeClass('data-src');
   $(item).removeClass('box-item');
 }
 })
}

首先我们获取所有拥有 .box-item 类名的元素,遍历。viewTop 为图片相对于浏览器窗口的相对高度,类似于position:fixed感觉。

通过条件进行判断,只有当该图片在浏览器窗口内(之上或之下都不行)时,将需要设置背景图元素的 data-src 值展示出来,并删除该属性。

之后将最外层元素的 box-item 删除,因为已经展示出来的图片不需要再进行这些判断,删除了该类名下一次滚动时就不会获取到已经展示过的元素,需要遍历的次数就会越来越少,这样能起到一个优化的作用。

该函数需要在你的元素已经append进页面时调用,以及在滚动时调用:

lazyLoad();
$(window).scroll(lazyLoad);

滚动加载

说完了懒加载,再说说滚动加载。所谓滚动加载就是当页面滚动到底部附近时加载新的图片。我这里选择的是滚动到高度最小的列底部时加载新的数据,你也可以根据自己的喜好来做判断。

function scrollLoad() {
  var viewHeight = $(window).scrollTop() + $(window).height(),
 minHeight = Math.min.apply(null, columnHeightArr);

  if (viewHeight >= minHeight) {
   //loadMore...
  }
}

滚动加载也是在window的滚动事件中进行监听,可以与懒加载一起进行:

$(window).scroll(function() {
 scrollLoad();
 lazyLoad(); 
});

说完了PC端,我们来说下手机端。其实原理是一样的,只是从多列变成固定的两列了。

var boxArr = $('.box'),
 columnHeightArr = [];
columnHeightArr.length = 2;

boxArr.each(function(index, item) {
  if (index < 2) {
    columnHeightArr[index] = $(item).outerHeight(true);
  } else {
    var minHeight = Math.min.apply(null, columnHeightArr),
 minHeightIndex = $.inArray(minHeight, columnHeightArr);

    $(item).css({
   position: 'absolute',
   top: minHeight,
   left: boxArr.eq(minHeightIndex).position().left
 });

    columnHeightArr[minHeightIndex] += $(item).outerHeight(true);
  }
});

不同的是为了适应不同屏幕的手机,最外层的box容器宽度和边距要设置成百分比的形式。

 最后有一点要注意,因为我们没有像百度一样用一个个列盒子去装,而是用定位的方式。导致的问题是图片元素的父级没法自适应高度,如果你有相关的需求我们可以计算出所有列中最长的长度,并将这个值赋值给父容器的min-height属性:

$('body').css('minHeight',Math.max.apply(null, columnHeightArr)); 

整理下完整的代码,瀑布流的全套服务就到这了

var dataArr = [
 {picUrl:'./resource/images/1.jpg',width:522,height:783},
 {picUrl:'./resource/images/2.jpg',width:550,height:786},
 {picUrl:'./resource/images/3.jpg',width:535,height:800},
 {picUrl:'./resource/images/4.jpg',width:578,height:504},
 {picUrl:'./resource/images/5.jpg',width:1440,height:900}
 ];

 $.each(dataArr, function(index, item) {
 $("body").append('<div class="box box-item">' +
  '<div class="img" style="height:0;padding-bottom:'+cRate(item) * 100 + "%"+'" data-src="'+item.picUrl+'"></div>' +
  '<div class="desc">Description</div>' +
  '</div>');
 });

 var boxArr = $('.box'),
 num = Math.floor(document.body.clientWidth / boxArr.eq(0).outerWidth(true)),
 columnHeightArr = [];
 columnHeightArr.length = num;
 
 arrangement();

  $('body').css('minHeight',Math.max.apply(null, columnHeightArr));

 lazyLoad();

 function arrangement() {
 boxArr.each(function(index, item) {
 if (index < num) {
 columnHeightArr[index] = $(item).outerHeight(true);
 } else {
 var minHeight = Math.min.apply(null, columnHeightArr),
  minHeightIndex = $.inArray(minHeight, columnHeightArr);
 $(item).css({
  position: 'absolute',
  top: minHeight,
  left: boxArr.eq(minHeightIndex).position().left
 });
 columnHeightArr[minHeightIndex] += $(item).outerHeight(true);
 }
 });
 }

 function lazyLoad() {
 var boxArr = $('.box-item');
 boxArr.each(function(index, item) {
 var viewTop = $(item).offset().top - $(window).scrollTop(),
 imgObj = $(item).find('.img');
 if ((viewTop < $(window).height()) && ($(item).offset().top + $(item).outerHeight(true) > $(window).scrollTop())) {
// console.log($(item).attr('data-src'));
 imgObj.css('backgroundImage','url('+imgObj.attr("data-src")+')').removeClass('data-src');
 $(item).removeClass('box-item');
 }
 })
 }

 function cRate(obj) {
 return obj.height / obj.width;
 }

 function scrollLoad() {
 var viewHeight = $(window).scrollTop() + $(window).height(),
 minHeight = Math.min.apply(null, columnHeightArr);
 if (viewHeight >= minHeight) {
 //loadMore...
 }
 }

 $(window).scroll(function() {
 lazyLoad();
 scrollLoad();
 });

以上就是为大家分享的关于jQuery瀑布流布局,内容很丰富,需要大家一点点的理解消化,真正的做到学以致用,希望能够帮助到大家。

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.