mirror of
https://github.com/the1812/Bilibili-Evolved.git
synced 2025-11-04 21:22:45 +08:00
Add magic grid
This commit is contained in:
parent
43f4aa9c41
commit
1ee0eaa5ab
15
src/utils/magic-grid.d.ts
vendored
Normal file
15
src/utils/magic-grid.d.ts
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
export interface MagicGridConfig {
|
||||
container: string | HTMLElement // Required. Can be a class, id, or an HTMLElement
|
||||
static: boolean // Required for static content. Default: false.
|
||||
items: number, // Required for dynamic content. Initial number of items in the container.
|
||||
gutter?: number, // Optional. Space between items. Default: 25(px).
|
||||
maxColumns?: number, // Optional. Maximum number of columns. Default: Infinite.
|
||||
useMin?: boolean, // Optional. Prioritize shorter columns when positioning items. Default: false.
|
||||
useTransform?: boolean, // Optional. Position items using CSS transform. Default: True.
|
||||
animate?: boolean, // Optional. Animate item positioning. Default: false.
|
||||
}
|
||||
export class MagicGrid {
|
||||
constructor(config: MagicGridConfig)
|
||||
listen(): void
|
||||
positionItems(): void
|
||||
}
|
||||
269
src/utils/magic-grid.js
Normal file
269
src/utils/magic-grid.js
Normal file
@ -0,0 +1,269 @@
|
||||
/*
|
||||
This is an ESM version of [Magic Grid](https://github.com/e-oj/Magic-Grid)
|
||||
Modified by Grant Howard
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author emmanuelolaojo
|
||||
* @since 11/11/18
|
||||
*/
|
||||
|
||||
/**
|
||||
* Validates the configuration object.
|
||||
*
|
||||
* @param config - configuration object
|
||||
*/
|
||||
const checkParams = function (config) {
|
||||
const DEFAULT_GUTTER = 25
|
||||
|
||||
if (!config) {
|
||||
throw new Error('No config object has been provided.')
|
||||
}
|
||||
|
||||
if (typeof config.useTransform !== 'boolean') {
|
||||
config.useTransform = true
|
||||
}
|
||||
|
||||
if (typeof config.gutter !== 'number') {
|
||||
config.gutter = DEFAULT_GUTTER
|
||||
}
|
||||
|
||||
if (!config.container) { error('container') }
|
||||
if (!config.items && !config.static) { error('items or static') }
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles invalid configuration object
|
||||
* errors.
|
||||
*
|
||||
* @param prop - a property with a missing value
|
||||
*/
|
||||
const error = function (prop) {
|
||||
throw new Error(("Missing property '" + prop + "' in MagicGrid config"))
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the shortest column in
|
||||
* a column list.
|
||||
*
|
||||
* @param cols - list of columns
|
||||
*
|
||||
* @return shortest column
|
||||
*/
|
||||
const getMin = function (cols) {
|
||||
let min = cols[0]
|
||||
|
||||
for (const col of cols) {
|
||||
if (col.height < min.height) { min = col }
|
||||
}
|
||||
|
||||
return min
|
||||
}
|
||||
|
||||
/**
|
||||
* @author emmanuelolaojo
|
||||
* @since 11/10/18
|
||||
*
|
||||
* The MagicGrid class is an
|
||||
* implementation of a flexible
|
||||
* grid layout.
|
||||
*/
|
||||
|
||||
export class MagicGrid {
|
||||
constructor (config) {
|
||||
checkParams(config)
|
||||
|
||||
if (config.container instanceof HTMLElement) {
|
||||
this.container = config.container
|
||||
this.containerClass = config.container.className
|
||||
} else {
|
||||
this.containerClass = config.container
|
||||
this.container = document.querySelector(config.container)
|
||||
}
|
||||
|
||||
this.items = this.container.children
|
||||
this.static = config.static || false
|
||||
this.size = config.items
|
||||
this.gutter = config.gutter
|
||||
this.maxColumns = config.maxColumns || false
|
||||
this.useMin = config.useMin || false
|
||||
this.useTransform = config.useTransform
|
||||
this.animate = config.animate || false
|
||||
this.started = false
|
||||
|
||||
this.init()
|
||||
}
|
||||
/**
|
||||
* Initializes styles
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
init () {
|
||||
if (!this.ready() || this.started) { return }
|
||||
|
||||
this.container.style.position = 'relative'
|
||||
|
||||
for (let i = 0; i < this.items.length; i++) {
|
||||
const style = this.items[i].style
|
||||
|
||||
style.position = 'absolute'
|
||||
|
||||
if (this.animate) {
|
||||
style.transition = (this.useTransform ? 'transform' : 'top, left') + ' 0.2s ease'
|
||||
}
|
||||
}
|
||||
|
||||
this.started = true
|
||||
}
|
||||
/**
|
||||
* Calculates the width of a column.
|
||||
*
|
||||
* @return width of a column in the grid
|
||||
* @private
|
||||
*/
|
||||
colWidth () {
|
||||
return this.items[0].getBoundingClientRect().width + this.gutter
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes an array of empty columns
|
||||
* and calculates the leftover whitespace.
|
||||
*
|
||||
* @return {{cols: Array, wSpace: number}}
|
||||
* @private
|
||||
*/
|
||||
setup () {
|
||||
const width = this.container.getBoundingClientRect().width
|
||||
const colWidth = this.colWidth()
|
||||
let numCols = Math.floor(width / colWidth) || 1
|
||||
const cols = []
|
||||
|
||||
if (this.maxColumns && numCols > this.maxColumns) {
|
||||
numCols = this.maxColumns
|
||||
}
|
||||
|
||||
for (let i = 0; i < numCols; i++) {
|
||||
cols[i] = { height: 0, index: i }
|
||||
}
|
||||
|
||||
const wSpace = width - numCols * colWidth + this.gutter
|
||||
|
||||
return { cols, wSpace }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next available column.
|
||||
*
|
||||
* @param cols list of columns
|
||||
* @param i index of dom element
|
||||
*
|
||||
* @return {*} next available column
|
||||
* @private
|
||||
*/
|
||||
nextCol (cols, i) {
|
||||
if (this.useMin) {
|
||||
return getMin(cols)
|
||||
}
|
||||
|
||||
return cols[i % cols.length]
|
||||
}
|
||||
|
||||
/**
|
||||
* Positions each item in the grid, based
|
||||
* on their corresponding column's height
|
||||
* and index then stretches the container to
|
||||
* the height of the grid.
|
||||
*/
|
||||
positionItems () {
|
||||
const ref = this.setup()
|
||||
const cols = ref.cols
|
||||
let wSpace = ref.wSpace
|
||||
let maxHeight = 0
|
||||
const colWidth = this.colWidth()
|
||||
|
||||
wSpace = Math.floor(wSpace / 2)
|
||||
|
||||
for (let i = 0; i < this.items.length; i++) {
|
||||
const col = this.nextCol(cols, i)
|
||||
const item = this.items[i]
|
||||
const topGutter = col.height ? this.gutter : 0
|
||||
const left = col.index * colWidth + wSpace + 'px'
|
||||
const top = col.height + topGutter + 'px'
|
||||
|
||||
if (this.useTransform) {
|
||||
item.style.transform = 'translate(' + left + ', ' + top + ')'
|
||||
} else {
|
||||
item.style.top = top
|
||||
item.style.left = left
|
||||
}
|
||||
|
||||
col.height += item.getBoundingClientRect().height + topGutter
|
||||
|
||||
if (col.height > maxHeight) {
|
||||
maxHeight = col.height
|
||||
}
|
||||
}
|
||||
|
||||
this.container.style.height = maxHeight + 'px'
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if every item has been loaded
|
||||
* in the dom.
|
||||
*
|
||||
* @return {Boolean} true if every item is present
|
||||
*/
|
||||
ready () {
|
||||
if (this.static) { return true }
|
||||
return this.items.length >= this.size
|
||||
}
|
||||
|
||||
/**
|
||||
* Periodically checks that all items
|
||||
* have been loaded in the dom. Calls
|
||||
* this.listen() once all the items are
|
||||
* present.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
getReady () {
|
||||
const interval = setInterval(() => {
|
||||
this.container = document.querySelector(this.containerClass)
|
||||
this.items = this.container.children
|
||||
|
||||
if (this.ready()) {
|
||||
clearInterval(interval)
|
||||
|
||||
this.init()
|
||||
this.listen()
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
|
||||
/**
|
||||
* Positions all the items and
|
||||
* repositions them whenever the
|
||||
* window size changes.
|
||||
*/
|
||||
listen () {
|
||||
if (this.ready()) {
|
||||
let timeout
|
||||
window.addEventListener('resize', () => {
|
||||
if (!timeout) {
|
||||
timeout = setTimeout(() => {
|
||||
this.positionItems()
|
||||
timeout = null
|
||||
}, 200)
|
||||
}
|
||||
})
|
||||
|
||||
this.positionItems()
|
||||
} else { this.getReady() }
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
export: {
|
||||
MagicGrid
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user