javascript - 怎么在瀑布流中内嵌一个元素,而且不影响布局
问题描述
想图片这样瀑布流中出现内嵌块
这有两种方法,
(注:下面的尺寸均为示例,仅用于理清逻辑,具体以实际案例为准)
方法1:用CSS实现的瀑布流
整个瀑布流区域,固定宽度(比如500px),每一小块,宽度,固定116px,小块之间的宽度固定为12px
这样,如果横向四小块的地方,是116px × 4 + 12px × 3 = 500px
大块,宽度为两小块的宽度加上一个横向间隔宽度,即116px × 2 + 12px = 244px
所以,如果横向2小块1大块,是116px × 2 + 244px × 1 + 12px × 2 = 500px
这样,只要让所有大块小块全左浮动或者右浮动,就可以堆成这样的瀑布流
但是,需要注意的是,实际上瀑布流区域在操作的时候,宽度是500px + 12px,具体原因见下图
其中,黄色部分为大框或小框的margin(假设所有的都设置上边距和左边距),那么4个小框其实是有4个宽度间距而并非是3个(有一个在这一排的左侧),所以总宽度应该加上一个12px。
方法2:用JS处理
这个不详细讲了,因为思路是和上面类似,只是是用JS动态去计算出那500px + 12px的值(但其实,只需要算500的值即可,由于瀑布块是绝对定位,不需要用边距将位置空开,空开的区域是通过计算后偏移所得)
对于每一个元素,都相对于瀑布流的外层元素绝对定位,用JS放到对应的位置上去
如果是大元素堆上去的时候,看一下是否处于第四列,如果处于第四列,就强制堆叠在第一列的位置(因为第四列向右只可以堆一列,大块占两列的宽度,所以会堆出屏幕区)
至于如何判断该堆在哪一列,可以用一个长度为4的数组,表示这四列最后一个元素的y值(相对于瀑布流外层元素而言,即绝对定位时的
top
加上outerHeight)下一个堆叠的上去的瀑布流元素,查找这个长度为4的数组中最小的一个值,进行附加,然后更新对应数组项的值
不过注意,如果你堆叠大块的时候,需要将下一个项的对应数组值也设置为大框的底部偏移量(假设在第一列插入大框,则需要更新第一列和第二列对应的Y偏移值)
另外,由于很难保证在堆叠大框的时候,大框上面对应两列的元素底边都在同一条上,所以,一般大框都是堆叠在瀑布流最开始的一行(因为这里能保证堆叠时两列的Y偏移量都为0)
此外还有一点要注意,堆叠大框时,判断从哪一列开始堆叠,由偏移量最小的值决定,但是同时也需要考虑到最小Y偏移量的列两侧的偏移,如下图:
此处,Y偏移量第二列虽然是最小,但是第二列周围两列(第一列和第三列),和第二列的落差太大了,但第三第四列反而比较平整,所以大块应该堆叠在第三第四列上
所以,堆叠大块优先判断顺序为:相邻两块的平整性(即下部Y偏移差绝对值最小)> 下部Y偏移最小
其他
大块定位和小块定位有些许区别:
不能从最后一列开始(因为右边没有第二列,强行定位就会偏离瀑布流)
优先要考虑上边缘尽量平整(
Math.abs(Y2 - Y1)
最小)在考虑平整的情况下,才考虑上边缘Y偏移最小(主要是为了让元素补全最短的一栏,但是大块需要优先考虑相邻两列平整)
需要同时更新两列的下边缘偏移量
当然,上面没有说的一点是,有个略复杂的办法,解决拼接上边缘不平整的情况,就是先不管边缘是否平整,直接拼接,然后遇到高度适合的小块,再将此小块拼到大块上面,补全缺口(下图中,绿色小块是后来补上去的,而并非在大块定位之前添加好的)。这个方法可能需要两组Y偏移量数据(大块和小块的底边偏移)
最后放上一个之前写的JS瀑布流方法(不含大块定位,但是思路类似,基于jQuery)
$(function(){
$('.tile-box').each(function(){
// 获取配置
var $offset = $(this).attr('offset') || '0,0', // 左上角偏移量(横向,纵向)
$blocks = $(this).attr('blocks') || '', // 宽度配置(宽度1|宽度2|宽度3|...)
$margin = $(this).attr('margin') || '0,0'; // 块与块之间的边距(横向,纵向);
// 解析变量
// 解析左上角偏移
$offset = $offset.split(',');
$offsetX = $offset[0] ? $offset[0] : 0;
$offsetY = $offset[1] ? $offset[1] : 0;
// 解析区块宽度配置
$blocks = $blocks.split('|');
// 解析边距配置
$margin = $margin.split(',');
$marginX = $margin[0] ? $margin[0] : 0;
$marginY = $margin[1] ? $margin[1] : 0;
// 初始化位移变量
var $posX = [], $posY = [];
for(var i = 0; i < $blocks.length; i++){
// X粥初始化
if($posX[i - 1] && $blocks[i - 1]){ // 第二列开始
$posX.push(parseFloat($posX[i - 1]) + parseFloat($blocks[i - 1]) + parseFloat($marginX));
}
else{ // 第一列
$posX.push(parseFloat($offsetX));
}
// Y轴初始化
$posY.push(parseFloat($offsetY) - parseFloat($marginY));
}
// 定义所需元素
var $tilebox = $(this), // 自身
$tiles = $(this).children(); // 用以排列的元素
// 给父元素加上相对定位(为了子元素可以实现绝对定位)
$tilebox.css('position', 'relative');
// 遍历需要排列的元素
$tiles.each(function(){
// 选择底部偏移最小的列
var $minIndex = 0;
for(var i = 1; i < $posY.length; i++){
if($posY[$minIndex]){
if($posY[$minIndex] > $posY[i]){
$minIndex = i;
}
}
}
// 设置当前排列块的定位
$(this).css({
'position': 'absolute',
'width': $blocks[$minIndex],
'top': $posY[$minIndex] + parseFloat($marginY),
'left': $posX[$minIndex]
});
// 设置新的纵向偏移
$posY[$minIndex] += ($(this).outerHeight(true) ? $(this).outerHeight(true) : $(this).height()) + parseFloat($marginY);
});
// 选择底部偏移最大的列
var $maxIndex = 0;
for(var i = 1; i < $posY.length; i++){
if($posY[$maxIndex]){
if($posY[$maxIndex] < $posY[i]){
$maxIndex = i;
}
}
}
// 设置父元素的高度
$tilebox.css('height', $posY[$maxIndex]);
});
});
这篇关于javascript - 怎么在瀑布流中内嵌一个元素,而且不影响布局的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!