在Vue.js中使用JS 实现瀑布流

前言

  • 最近在学习瀑布流的实现方法,网上找了许多实现的方法,但发现与自己的需求不太符合,于是对代码进行了一些改动。
  • 个人需求:

    1. 横向加载
    2. 服务器返回的信息中没有图片尺寸
    3. 每加载完一张图片就设置它的位置,而不是等所有图片都显示完再布局
  • 性能肯定会比较慢,最好的方法还是在服务器返回图片尺寸大小或比例。
  • 为了方便操作,采用了在html中引用Vue.js的方法实现。

效果预览

  • 为了更直观的展示,这里设置了3G网络并禁用了缓存

  • 正常网速

主要改动地方

  • 根据盒子宽度设置列数 => 根据列数设置box的宽度
    改动原因是根据列数设置宽度我觉得会更直观方便,而且右侧不会留下空白
// 瀑布流布局
waterFall() {

    ...

    const columns = 3  // 列数
    const gap = 10;  // 间隔
    const itemWidth = ~~((this.getClient().width / columns - gap))

    ...
},
// 获取页面宽度
getClient() {
    const SCROLL_WIDTH = 20
    return {
        /*
            ** window.innerWidth - SCROLL_WIDTH: 页面宽度(包括滚动条)- 比滚动条多一点的宽度
            ** 不使用document.body.clientWidth 和 document.documentElement.clientWidth原因:
            ** 页面首次加载时的宽度没有被滚动条挤压的,这样会导致滚动加载时获取的页面宽度与首次的宽度不一致
            ** 一般浏览器滚动条宽度为17px,减去20是保守估计并为右侧留一定空间
            ** 移动端滚动条是悬浮在页面在上,不会造成挤压,因此在移动端中可以不减去滚动条宽度
        */
        width: window.innerWidth - SCROLL_WIDTH
        // width: window.innerWidth || document.body.clientWidth || document.documentElement.clientWidth
    }
},
// 对box进行布局
reflow(el, itemWidth, columns, gap) {
    
    el.style.width  = itemWidth + 'px'

    ...
}
  • HTML文档加载完毕后再统一布局 => 加载完一张图片就布局一次
    (window.onload => image.onload)
// 瀑布流布局
waterFall() {

    const columns = 3  // 列数
    const gap = 10;  // 间隔
    const itemWidth = ~~((this.getClient().width / columns - gap))

    const box = document.getElementById("big-box")
    const items = box.children

    for (let i = this.loadCount; i < items.length; i++, this.loadCount++) {

        // 获取图片元素
        const img = items[i].getElementsByTagName('img')[0]
        // 图片有缓存时直接布局(主要在窗口尺寸变化时调用)
        if(img.complete) {
            this.reflow(items[i], itemWidth, columns, gap)
        }
        // 图片无缓存时先对加载速度快的图片进行布局
        else {
            img.onload = () => {
                this.reflow(items[i], itemWidth, columns, gap)
            }
        }

    }
},
// 对box进行布局
reflow(el, itemWidth, columns, gap) {
    el.style.width  = itemWidth + 'px'

    // 第一行
    if (this.arr.length < columns) {
        el.style.top = 0;
        el.style.left = (itemWidth + gap) * this.arr.length + 'px'
        this.arr.push(el.offsetHeight)
    }
    // 其他行
    else {
        // 最小的列高度
        const minHeight = Math.min(...this.arr)
        // 当前高度最小的列下标
        const index = this.arr.indexOf(minHeight)

        el.style.top = minHeight + gap + 'px'
        el.style.left = (itemWidth + gap) * index + 'px'

        this.arr[index] = this.arr[index] + el.offsetHeight + gap
    }
}

完整代码





    
    
    
    

    JS 实现瀑布流(Vue)
    




    

存在的问题

  • 目前图片的尺寸获取和位置设置都是在image的onload方法中执行的,效率会比较慢
  • 窗口尺寸改变时会对所有的图片进行重新布局(可以考虑加个数组按图片加载顺序记录下标)
  • 图片未全部加载完就滚动到底部触发可能会导致数据缺失?(未验证)
    可以自行加个全部加载完才执行的判断。

参考文章

你可能感兴趣的:(vue.js,javascript,前端)