美文网首页VUE
浅谈瀑布流原理及Vue实现

浅谈瀑布流原理及Vue实现

作者: salt_fash | 来源:发表于2019-07-27 16:38 被阅读0次

    瀑布流,又称瀑布流式布局。是比较流行的一种网站页面布局,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部。最早采用此布局的网站是Pinterest,逐渐在国内流行开来。国内大多数清新站基本为这类风格。

    瀑布流布局效果

    在这里插入图片描述

    既多行等宽元素排列,等宽不等高,后面的元素依次排列在上一个元素后面

    那么瀑布流的规则是什么哪?下面将用图解的方式分析一下瀑布流的算法

    图解瀑布流

    第一排图片的顶部会处于同一个高度,依次排列在顶端,哪第一排排满之后,后面的图片,也就是第6张图片应该怎样排列?

    在这里插入图片描述
    是这样吗?
    在这里插入图片描述
    可能有人认为会是这种方式进行排列,然而这样排列的方式是错误的,这样排列容易导致某一列可能会过长,而其他的列会过短,导致列之间高度相差过大的情况出现。那么怎样取解决这个问题那?答案就是利用定位在最短的一列下面进行排列,就像下图
    在这里插入图片描述
    看到这里,基本上已经理解了瀑布流的基本原理,接下来就是怎样去实现了!

    瀑布流的实现

    下面以Vue为例来展示怎样实现瀑布流效果

    1.基本的布局

    布局就比较简单的写一下哈~ 反正主要讲原理~

    .v-waterfall-content{
        width: 100%;
        height: 100%;
        position: relative;
    }
    .v-waterfall-item{
        position: absolute;
    }
    .v-waterfall-item img{
        width: 100%;
        height: 100%;
    }
    

    2.瀑布流核心实现

    首先在data中定义一些基本的数据

      waterfallList:[],//存放计算好的数据
      waterfallImgWidth:200,//每一列的宽度
      waterfallImgCol:5,//多少列
      waterfallImgRight:10,//右边距
      waterfallImgBottom:10,//下边距
      waterfallDeviationHeight:[],//存放瀑布流各个列的高度
      imgList:[]
    

    然后给图片数组填充图片数据:

        let imgArr = [
          require('../assets/100x70.png'),
          require('../assets/100x80.png'),
          require('../assets/100x90.png'),
          require('../assets/100x100.png'),
          require('../assets/100x120.png'),
          require('../assets/100x150.png'),
          require('../assets/100x210.png'),
          require('../assets/100x230.png'),
          require('../assets/100x250.png')
        ];
        for (let i = 0;i < 100;i++){
          this.imgList.push(this.imgArr[Math.round(Math.random() * 8)]);
        }
    

    这里要注意,在js中存放静态文件链接的时候要用require,不然不会显示:

        <div class="v-waterfall-content">
            <div v-for="img in imgList"
                 class="v-waterfall-item">
                <img :src="img" alt="">
            </div>
        </div>
    
    在这里插入图片描述

    可以看到,所有图片都挤到一起,根本没有流,下面谈一谈怎样让图片流起来

    根据上面解释的瀑布流的基本原理,就是需要找到图片列里面高度最低的那一个,要找到最低的就需要记录没一列的高度,下面是记录列高度的实现:

    //图片预加载,获取图片宽和高
    imgPreloading(){
        for (let i = 0;i < this.imgList.length;i++){
            let aImg = new Image();
            aImg.src = this.imgList[i];
            aImg.onload = aImg.onerror = (e)=>{
                let imgData = {};
                //根据设定的列宽度求出图片的高度
                imgData.height = this.waterfallImgWidth/aImg.width*aImg.height;
                imgData.src = this.imgList[i];
                this.waterfallList.push(imgData);
                //调用图片位置计算方法
                this.rankImg(imgData);
            }
        }
    }
    //计算图片偏移量
    rankImg(imgData){
        let {waterfallImgWidth,waterfallImgRight,waterfallImgBottom,waterfallDeviationHeight,waterfallImgCol} = this;
        //找出当前最短列的索引
        let minIndex = this.waterfallDeviationHeight.indexOf(Math.min.apply(null, this.waterfallDeviationHeight))
        //获取最短列底部高度,既下一张图片的顶部高度
        imgData.top = waterfallDeviationHeight[minIndex];
        //计算左侧偏移,最短列索引*(右边距+列宽度)
        imgData.left = minIndex*(waterfallImgRight+waterfallImgWidth);
        //改变当前列高度
        waterfallDeviationHeight[minIndex] += imgData.height + waterfallImgBottom;
    }
    

    这两个方法就是瀑布流的核心代码了,修改一下html代码,在刚才写的给图片数组填充完数据之后调用一下imgPreloading方法,刷新浏览器就可以看到效果啦!

    <div class="v-waterfall-content" id="v-waterfall">
        <div v-for="img in waterfallList"
             class="v-waterfall-item"
            :style="{top:img.top+'px',left:img.left+'px',width:waterfallImgWidth+'px',height:img.height}">
            <img :src="img.src" alt="">
        </div>
    </div>
    
    在这里插入图片描述

    到这里基本上瀑布流的原理和核心思路大致明了了。瀑布流最重要的就是记录流的位置和计算图片的高度,
    第一次写技术型博客,有写的不好的地方,还请多多指正,放上完整代码

    <template>
        <div class="v-waterfall-content" id="v-waterfall">
            <div v-for="img in waterfallList"
                 class="v-waterfall-item"
                :style="{top:img.top+'px',left:img.left+'px',width:waterfallImgWidth+'px',height:img.height}">
                <img :src="img.src" alt="">
            </div>
        </div>
    </template>
    
    <script>
        export default {
            name: "v-waterfall",
            data(){
                return {
                    waterfallList:[],
                    imgArr:[
                        require('../assets/100x70.png'),
                        require('../assets/100x80.png'),
                        require('../assets/100x90.png'),
                        require('../assets/100x100.png'),
                        require('../assets/100x120.png'),
                        require('../assets/100x150.png'),
                        require('../assets/100x210.png'),
                        require('../assets/100x230.png'),
                        require('../assets/100x250.png')
                    ],
                    waterfallImgWidth:100,
                    waterfallImgCol:5,
                    waterfallImgRight:10,
                    waterfallImgBottom:10,
                    waterfallDeviationHeight:[],
                    imgList:[]
                }
            },
            created() {
                for (let i = 0;i < 100;i++){
                    this.imgList.push(this.imgArr[Math.round(Math.random() * 8)]);
                }
            },
            mounted(){
                this.calculationWidth();
            },
            methods:{
                //计算每个图片的宽度或者是列数
                calculationWidth(){
                    let domWidth = document.getElementById("v-waterfall").offsetWidth;
                    if (!this.waterfallImgWidth && this.waterfallImgCol){
                        this.waterfallImgWidth = (domWidth-this.waterfallImgRight*this.waterfallImgCol)/this.waterfallImgCol;
                    }else if(this.waterfallImgWidth && !this.waterfallImgCol){
                        this.waterfallImgCol = Math.floor(domWidth/(this.waterfallImgWidth+this.waterfallImgRight))
                    }
                    //初始化偏移高度数组
                    this.waterfallDeviationHeight = new Array(this.waterfallImgCol);
                    for (let i = 0;i < this.waterfallDeviationHeight.length;i++){
                        this.waterfallDeviationHeight[i] = 0;
                    }
                    this.imgPreloading()
                },
                //图片预加载
                imgPreloading(){
                    for (let i = 0;i < this.imgList.length;i++){
                        let aImg = new Image();
                        aImg.src = this.imgList[i];
                        aImg.onload = aImg.onerror = (e)=>{
                            let imgData = {};
                            imgData.height = this.waterfallImgWidth/aImg.width*aImg.height;
                            imgData.src = this.imgList[i];
                            this.waterfallList.push(imgData);
                            this.rankImg(imgData);
                        }
                    }
                },
                //瀑布流布局
                rankImg(imgData){
                    let {waterfallImgWidth,waterfallImgRight,waterfallImgBottom,waterfallDeviationHeight,waterfallImgCol} = this;
                    //for (let i = 0;i < this.waterfallList.length;i++){
                    let minIndex = this.filterMin();
                    imgData.top = waterfallDeviationHeight[minIndex];
                    imgData.left = minIndex*(waterfallImgRight+waterfallImgWidth);
                    waterfallDeviationHeight[minIndex] += imgData.height + waterfallImgBottom;
                    //}
                    console.log(imgData);
                },
                /**
                 * 找到最短的列并返回下标
                 * @returns {number} 下标
                 */
                filterMin(){
                    const min = Math.min.apply(null, this.waterfallDeviationHeight);
                    return this.waterfallDeviationHeight.indexOf(min);
                }
            }
        }
    </script>
    
    <style scoped>
    .v-waterfall-content{
        width: 100%;
        height: 100%;
        position: relative;
    }
    .v-waterfall-item{
        float: left;
        position: absolute;
    }
    .v-waterfall-item img{
        width: auto;
        height: auto;
    }
    </style>
    
    

    相关文章

      网友评论

        本文标题:浅谈瀑布流原理及Vue实现

        本文链接:https://www.haomeiwen.com/subject/urperctx.html