实现瀑布流页面的两种方案(纯css3/js)

瀑布流页面设计最初我是在“花瓣”这个产品上看到的,最近一个电商网站也运用了这一设计方式,可能会显得产品更有特色吧。今天我们就讨论下怎么实现一个图片的瀑布流。

思路1:使用JS

通常数据以分页的形式从后端获取,前端加载图片,一行显示两个图片,图片高度要按比例显示。如果左侧图片高就把下一张图片放到右侧,相反如果右侧图片高,就把下一张图片放到左侧,这样可以保持整个页面的布局和谐。同时,在页面滚动到距下方50px时触发滚动加载下一页数据。

具体实现


<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>瀑布流title>
    <style>
        html, body{
            margin: 0;
            padding: 0;
            width: 100%;
        }
        ul{
            margin: 0;
            padding: 0;
            list-style: none;
            float: left;
        }
        li{
            float: left;
        }
    style>
head>

<body>
    <div>
        <ul id="left">ul>
        <ul id="right">ul>
    div>
    <script>
    	// src需要替换成实际的图片地址
        const data = [
            [{
                src: 'xxx',
                name: 'test1'
            }, {
                src: 'xxx',
                name: 'test2'
            }, {
                src: 'xxx',
                name: 'test3'
            }, {
                src: 'xxx',
                name: 'test4'
            }, {
                src: 'xxx',
                name: 'test5'
            }, {
                src: 'xxx',
                name: 'test6'
            }],
            [{
                src: 'xxx',
                name: 'test21'
            }, {
                src: 'xxx',
                name: 'test22'
            }, {
                src: 'xxx',
                name: 'test23'
            }, {
                src: 'xxx',
                name: 'test24'
            }, {
                src: 'xxx',
                name: 'test25'
            }, {
                src: 'xxx',
                name: 'test26'
            }],
            [{
                src: 'xxx',
                name: 'test31'
            }, {
                src: 'xxx',
                name: 'test32'
            }, {
                src: 'xxx',
                name: 'test33'
            }, {
                src: 'xxx',
                name: 'test34'
            }, {
                src: 'xxx',
                name: 'test35'
            }, {
                src: 'xxx',
                name: 'test36'
            }]
        ];
        //模拟远程加载数据
        const mockQuery = function (currentPage) {
            if (currentPage < 3) {
                return {
                    totalPages: data.length,
                    content: data[currentPage]
                }
            }
            return {
                totalPages: data.length,
                content: []
            }
        }

        const width = Math.floor((window.innerWidth - 30) / 2);
        const imagesLeft = document.getElementById('left');
        const imagesRight = document.getElementById('right');
        imagesLeft.style = 'width: ' + width + 'px; margin: 0 5px 10px 10px';
        imagesRight.style = 'width: ' + width + 'px; margin: 0 10px 10px 5px';

		//动态插入图片到左侧ul或右侧ul中
        const insertImages = function (data, width) {
            let leftHeight = 0;
            let rightHeight = 0;
            for(let image of data.content){
                const li = document.createElement("li");
                const img = document.createElement('img');
                img.src = image.src;
                img.style = 'width: 100%';
                img.onload = function(){
                    if(leftHeight <= rightHeight){
                        imagesLeft.appendChild(li);
                        leftHeight += this.height * width / this.width;
                    }else{
                        imagesRight.appendChild(li);
                        rightHeight += this.height * width / this.width;
                    }
                }
                li.appendChild(img);
            }
        }
		//判断是否滑动到页面底部
        function isScrollToPageBottom(){
            //文档高度
            var documentHeight = document.documentElement.offsetHeight;
            var viewPortHeight = window.innerHeight;
            var scrollHeight = window.pageYOffset ||
                    document.documentElement.scrollTop ||
                    document.body.scrollTop || 0;

            return documentHeight - viewPortHeight - scrollHeight < 50;
        }

        //监听页面滚动事件
        document.addEventListener('scroll', function (event) {
            if(isScrollToPageBottom()){
                if(currentPage < 2){
                    currentPage++;
                    insertImages(mockQuery(currentPage), width);
                }
           }
        });
        let currentPage = 0;
        insertImages(mockQuery(currentPage), width);
    script>
body>
html>

这里面需要注意的是图片是远程加载的,加载的顺序不定,所以图片的排序是按图片网络加载的顺序来显示的。在实际项目中图片通常是需要按创建时间来显示,我们会在下一次文章中做讨论。

思路2: 使用CSS3

上面是使用js来实现瀑布流,当然现在css3技术也很成熟,我们可以只用css3就能实现瀑布流效果。下面要介绍的是Multi-columns布局。关于Multi-columns如果不了解可以查看文末资料介绍。这种实现代码简单很多。

具体实现


<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>瀑布流title>
    <style>
        html, body{
            margin: 0;
            padding: 0;
            width: 100%;
        }
    
        ul{
            margin: 0;
            padding: 0;
            list-style: none;
            column-count: 2; 
            column-gap: 0;
            column-gap: 10px;
        }
        img{
            margin-bottom: 10px;
        }
    style>
head>

<body>
    <ul id="images">ul>
    <script>
        const data = [
            [{
                src: '',
                name: 'test1'
            }, {
                src: '',
                name: 'test2'
            }, {
                src: '',
                name: 'test3'
            }, {
                src: '',
                name: 'test4'
            }, {
                src: '',
                name: 'test5'
            }, {
                src: '',
                name: 'test6'
            }],
            [{
                src: '',
                name: 'test21'
            }, {
                src: '',
                name: 'test22'
            }, {
                src: '',
                name: 'test23'
            }, {
                src: '',
                name: 'test24'
            }, {
                src: '',
                name: 'test25'
            }, {
                src: '',
                name: 'test26'
            }],
            [{
                src: '',
                name: 'test31'
            }, {
                src: '',
                name: 'test32'
            }, {
                src: '',
                name: 'test33'
            }, {
                src: '',
                name: 'test34'
            }, {
                src: '',
                name: 'test35'
            }, {
                src: '',
                name: 'test36'
            }]
        ];
        const mockQuery = function (currentPage) {
            if (currentPage < 3) {
                return {
                    totalPages: data.length,
                    content: data[currentPage]
                }
            }
            return {
                totalPages: data.length,
                content: []
            }
        }

        const images = document.getElementById('images');

        const insertImages = function (data) {
            for(let image of data.content){
                const li = document.createElement("li");
                const img = document.createElement('img');
                img.src = image.src;
                img.style = 'width: 100%';
                li.appendChild(img);
                images.appendChild(li);
            }
        }

        function isScrollToPageBottom(){
            //文档高度
            var documentHeight = document.documentElement.offsetHeight;
            var viewPortHeight = window.innerHeight;
            var scrollHeight = window.pageYOffset ||
                    document.documentElement.scrollTop ||
                    document.body.scrollTop || 0;

            return documentHeight - viewPortHeight - scrollHeight < 50;
        }

        //listner document scroll
        document.addEventListener('scroll', function (event) {
            if(isScrollToPageBottom()){
                if(currentPage < 2){
                    currentPage++;
                    insertImages(mockQuery(currentPage));
                }
           }
        });
        let currentPage = 0;
        insertImages(mockQuery(currentPage));
    script>
body>

html>

更多资料

  1. 有个瀑布流插件很好用, 大家可以试用一下: masonry.desandro.com
  2. 使用CSS多列布局: developer.mozilla.org/zh-CN/docs/Web/Guide/CSS/Using_multi-column_layouts

你可能感兴趣的:(前端技术)