python实现3d waterfall_原生JavaScript 瀑布流 实现 zx-waterfall

使用说明

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']).then(_ => {

// change

waterfall.change()

})

browser

demo

注意

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.

方法

reset()

重置瀑布流初始值

loadMeida(array)

预加载列表数据中的image媒体元素.

array: ['http://a.com/1.jpg', 'http://a.com/2.jpg']

@return promise

change()

列表数据改变后后,通知瀑布流更新元素位置.

源码

/**

* 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}

*/

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值