瀑布流布局的实现(一)

        作为东北大学软件学院中少数选择非主流方向的我,第一次鼓起勇气写一篇属于我自己的前端相关的技术博客,其中不免用词不当、不专业之处,望各种博友见谅。

        这个问题其实是前段时间面试的时候一位考官问我的,当时没给出答案,后来想起这个问题,确实是一个很有趣的问题。

问题背景想必大家从标题上已经得知,是一个瀑布流的布局实现问题,瀑布流是什么请参考此链接点击打开链接,而我在下文的叙述中将初步的实现一下它的布局,在以后的文章中再逐渐的深入讨论。

既然上文提到是初步实现,那么我将做一些理想化实验的假设,其实本文更多的是算法上的讨论。语言将使用原生JS,浏览器采用CHROME,一切关于IE浏览器兼容性测试的问题,本文不予考虑,但保证在CHROME上测试通过。采用等宽不等高的图片源并假设浏览器宽度是图片宽的三倍。


首先我定义七个变量

var p = new Array(0,0);//最近加载的一张图片的左上角的坐标
var width = 300;//图片的宽度
var height; //图片的高度
var columnHeight = new Array(0,0,0);//第一个元素代表第一列的高度,以此类推
var iterColumn;//递归函数使用的变量,表示第iterColumn列目前待确认是否可用
var allColumn = 3;//总列数
var allImage = 7;//图片总数

在正式书写实现代码之前,我想有必要陈述一下我所理解的瀑布流布局规则,以免各位博友在浏览代码之时,产生不必要的异议。

1. 一般地,假设上一张图片加载在了第X列,那么判断第X列和第X-1列的高度差是否大于等于0。若结果为T那么将待加载图片加载在第X+1列,否则继续加载在第X列。

2. 特别地,当结果为T但X+1列与X列的高度差大于等于零时,那么将继续判断X+2列与X+1列的差,并按照一般地规律进行决策。

3. 特别地,当X列为最后一列且结果为T时,则比较第2列与第1列的高度差(注意是H2-H1)来判断是否适合将该图片加载到第1列,若不适合则以一般地规律判断第二列是否适合。

        要进行瀑布流布局,那么材料是必不可少的,我们的材料就是宽度相同,高度随机的一组图片,但是每张图片不可能做到尺寸相同,所以自然而然的想到了使用前台代码进行塑型控制。

        抛开代码来说,所有的图片想要呈瀑布流布局,理所应当的逻辑应该是一张一张图片去摆,那么问题来了,我们并不知道每一张图片的具体分辨率是多少,所以说在JS代码运行结束也就是开始加载HTML代码部分之前,我们并不可能在JS中通过DOM知晓图片的宽度和高度,但是我们在瀑布流逻辑中还偏偏就必须得知道图片的高度是多少。

        上一段也提到了,要想知道图片的高度,就必须得让图片先加载到浏览器页面中,OK,可能我们又会想当然的想到加载一次图片,处理一次逻辑将其融入瀑布流中然后再加载。那么问题又来了,因为我们在加载图片之后才可以知晓其高宽,所以因为我们必须将逻辑处理语句写在onload函数中,这就表示图片处理存在着一个队列流,想象一下,第二张图片要在第一张的逻辑处理之后才能加载,以此类推的话,我们需要将后一张图片的加载代码和逻辑处理代码都嵌套到前一张图片的onload函数中,但是我们有N张图片。大家此时此刻感受一下,这是个多么复杂而让人难受、闹心的一个问题(大牛可自动忽略此段话)。、

其实可能有高端语法(类似闭包等等)能解决我的上述问题,但是在这个时间点上我不想去钻牛角尖,而是另辟蹊径。我将所有图片都加载上去,然后再处理,问题马上就解决了。

说了这么多,想必大家对我的思路都了解了,那么闲话少叙最后以代码来给我的首篇技术博客画下完美的句号:

<!DOCTYPE html>
<html>
<body id="body">
 
<script>
var p = new Array(0,0);//最近加载的一张图片的左上角的坐标(top,left)
var width = 300;//图片的宽度
var height; //图片的高度
var columnHeight = new Array(0,0,0);//第一个元素代表第一列图片的高度总和,以此类推
var iterColumn;//递归函数使用的变量,表示第iterColumn列目前待确认是否可用
var allColumn = 3;//总列数
var allImage = 7;//图片总数
for(var i=1;i<allImage+1;i++){                         
    var img = document.createElement("img");   //DOM添加所有图片    
    img.src = i+".jpg";
    img.width = width;  //按照既定宽度约束图片
    img.id = i+".jpg";  //锁定ID
    document.getElementById("body").appendChild(img);  //待JS代码运行完毕加载到容器中
}

document.getElementById(allImage+".jpg").onload = function(){   //最后一张图片即全部图片都加载完毕后将运行瀑布流逻辑
    for(var i=1;i<allImage+1;i++){    //逐张进行摆放
        iterColumn = p[1]/width+1; //将其初始化为上一张图片的加载列数          
        var column = waterfall();  //运行瀑布流函数,返回该图片应该加载到的列数
        p[0] = columnHeight[column-1]; //更新变量
        p[1] = (column==1?0:(column==2?300:600)); //更新变量
        var img = document.getElementById(i+".jpg");//访问变量

	    columnHeight[column-1] += img.height;
	//以下三行将图片摆到合适位置
            img.style.position = "absolute";
    	    img.style.top = p[0]+"px";
    	    img.style.left = p[1]+"px";

    }
}

function waterfall(){	//逻辑函数 
    if(iterColumn == 1){  //当待确认列为第一列时,则比较第2列与第1列的高度差(注意是H2-H1)来判断是否适合将该图片加载到第1列,若不适合则让变量自增,递归运行函数判断下一列是否适合。 
	if(columnHeight[1]-columnHeight[0]>=0){
	    return 1;
	}
        else{
            iterColumn++;
            return waterfall();
	}
    }
    else if(iterColumn == allColumn){  //当待确认列为最后一列时,按照普通规则检验该行是否适合,若不适合,则将变量赋值为1,递归运行函数判断第一列是否适合
	if(columnHeight[allColumn-1]-columnHeight[allColumn-2]<0)
	    return allColumn;
        else{
            iterColumn=1;
            return waterfall();
	}
    }
    else{
	if(columnHeight[iterColumn-1]-columnHeight[iterColumn-2]<0){   //一般地规则,判断待确认列和上一列的高度差是否小于0。若结果为T则继续加载在该列,否则让变量自增,递归运行函数判断下一列

是否适合。
	    return iterColumn;
	}
        else{
            iterColumn++;
            return waterfall();
	}
    }
}
</script>

</body>
</html>


运行效果:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值