原生JavaScript 瀑布流 实现 zx-waterfall

栏目: JavaScript · 发布时间: 5年前

内容简介:源码地址:InstallES6+

源码地址: https://github.com/capricornc...

使用说明

Install zx-waterfall using npm

npm i --save zx-waterfall

ES6+

import ZxWaterfall from 'zx-waterfall'

const waterfall = new ZxWaterfall({
  // HTMLElement, waterfall items's outer container
  container: document.getElementById('zxWaterfall'),
  // children item selector, eg. '.item-container'
  itemSelector: '.item-wrapper',
  // item's spacing, unit px
  gutter: 20,
  // item's width
  itemWidth: 300
})
// reset
waterfall.reset()
// loaMedias
waterfall.loadMedia(['http://xx.com/aaa.jpg'])
// change
waterfall.change()

browser

<script src="path/zx-waterfall.min.js"></script>

demo

https://capricorncd.github.io...

注意

  • container 's style must be style.position=relative|absolute|fixed

参数options

  • container: HTMLElement

    瀑布流外容器.

  • containerWidth: Number

    瀑布流宽度,如果初始化时外容器未隐藏状态则需使用该参数.

    默认获取外容器宽度.

  • itemSelector: String

    子元素选择器,比如 样式名'.item-container'.

  • gutter: Number

    元素间的间隔.

  • verticalGutter: Number

    元素间垂直方向间隔,默认使用gutter值.

  • itemWidth: Number

    元素宽度, 默认 300 , 会根据容器宽度自动调整.

  • forceItemWidth: Boolean

    强制元素宽度,即使用itemWidth作为宽度值,默认 false .

  • align: String , Optional value left|center|right

    强制宽度时,元素显示靠边位置,默认 center .

源码

/**
 * preload image
 * @param url
 * @param handler
 */
function loadImage (url, handler) {
  let $el = document.createElement('img')
  $el.src = url
  $el.onload = handler
  $el.onerror = handler
  $el = null
}

/**
 * to int
 * @param m
 * @returns {number}
 */
function int (m) {
  let n = parseInt(m)
  return isNaN(n) ? 0 : n
}

/**
 * convert pseudoArray to array
 * @param pseudoArray
 * @param index
 * @returns {T[]}
 */
function slice (pseudoArray, index) {
  return Array.prototype.slice.call(pseudoArray, int(index))
}

// default options
const DEF_OPTIONS = {
  // HTMLElement, waterfall items's outer container
  container: null,
  // container's width, container are hidden when initialized
  // default get container offsetWidth when it's visible
  containerWidth: 0,
  // children item selector, eg. '.item-container'
  itemSelector: '',
  // item's spacing, unit px
  gutter: 20,
  // item's vertical spacing, default use gutter's value
  verticalGutter: 0,
  // item's width
  itemWidth: 300,
  // force item width
  forceItemWidth: false,
  // Horizontal align when forceItemWidth is true
  align: 'center'
}

/**
 * ZxWaterfall
 */
class ZxWaterfall {
  /**
   * constructor
   * @param opts
   */
  constructor (opts) {
    opts = Object.assign({}, DEF_OPTIONS, opts)
    // check container
    if (!opts.container || opts.container.nodeType !== 1) {
      throw new TypeError(`Instancing parameter 'container' is not HTMLElement.`)
    }
    // check itemSelector
    if (!opts.itemSelector || typeof opts.itemSelector !== 'string') {
      throw new TypeError(`Instancing parameter 'itemSelector' is null or is't a string.`)
    }
    // check verticalGutter
    if (!opts.verticalGutter) {
      opts.verticalGutter = opts.gutter
    }
    // item number
    this.count = 0
    this.opts = opts
    this._init()
    // clone this.reset
    this._resetClone = this.reset.bind(this)
    window.addEventListener('resize', this._resetClone)
  }

  /**
   * initialization
   * @private
   */
  _init () {
    let opts = this.opts
    // container width
    let containerWidth = int(opts.containerWidth) || opts.container.offsetWidth
    // column number
    let columnNum = Math.floor(containerWidth / (opts.itemWidth + opts.gutter))
    // opts.itemWidth when opts.forceItemWidth = true
    // else use compute new width
    this.itemWidth = opts.forceItemWidth
      ? opts.itemWidth
      : (containerWidth - (columnNum + 1) * opts.gutter) / columnNum
    // column current height array
    this.columns = Array(columnNum)
    this.columns.fill(0, 0)
    // offset left when forceItemWidth=true
    this.offsetLeft = 0
    if (opts.forceItemWidth) {
      let residualSpaceWidth = containerWidth - (this.itemWidth + opts.gutter) * columnNum - opts.gutter
      switch (opts.align) {
        case 'center':
          this.offsetLeft = residualSpaceWidth / 2
          break
        case 'right':
          this.offsetLeft = residualSpaceWidth
          break
      }
    }
  }

  /**
   * set items position
   * @private
   */
  _setPosition () {
    let opts = this.opts
    // get new item elements
    let $childs = slice(opts.container.querySelectorAll(opts.itemSelector), this.count)
    // console.log(this.count, $childs)
    let len = $childs.length
    // reset this.count value
    this.count += len
    // handle new $item
    let i, $item
    for (i = 0; i < len; i++) {
      $item = $childs[i]
      if (!$item) continue
      $item.style.position = 'absolute'
      $item.style.width = this.itemWidth + 'px'
      $item.style.display = 'inline-block'
      // get columns min value
      let min = Math.min.apply(null, this.columns)
      let index = this.columns.findIndex(val => val === min)
      // set $item position
      $item.style.top = `${min + opts.verticalGutter}px`
      $item.style.left = `${this.offsetLeft + (this.itemWidth + opts.gutter) * index + opts.gutter}px`
      // reset waterfall current column height value
      let itemHeight = $item.offsetHeight
      this.columns[index] = min + itemHeight + opts.verticalGutter
      // update container new min height style
      // opts.container.style.minHeight = Math.max.apply(null, this.columns) + opts.verticalGutter + 'px'
    }
  }

  /**
   * container's items number change
   */
  change () {
    // reset postion, when new item element append to container, or remove
    this._setPosition()
  }

  /**
   * reset
   */
  reset () {
    this.count = 0
    this._init()
    this._setPosition()
  }

  /**
   * preload media items
   * @param arr media source urls array
   * @returns {Promise<any>}
   */
  loadMedia (arr) {
    return new Promise(resolve => {
      if (Array.isArray(arr) && arr.length) {
        let len = arr.length
        let count = 0
        /* eslint-disable */
        arr.forEach(url => {
          loadImage(url, () => {
            count++
            if (len === count) resolve()
          })
        })
      } else {
        resolve()
      }
    })
  }

  /**
   * destroy
   * removeEventListener window resize
   */
  destroy () {
    window.removeEventListener('resize', this._resetClone)
  }
}

export default ZxWaterfall

demo: https://github.com/capricornc...


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Python Algorithms

Python Algorithms

Magnus Lie Hetland / Apress / 2010-11-24 / USD 49.99

Python Algorithms explains the Python approach to algorithm analysis and design. Written by Magnus Lie Hetland, author of Beginning Python, this book is sharply focused on classical algorithms, but it......一起来看看 《Python Algorithms》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具