update: remove $Layout.computeLayout()
update: remove threshold property
update: rewrite scrollCompute() for better performance
This commit is contained in:
defaultkavy 2024-10-01 20:09:37 +08:00
parent e15cd5bac4
commit d06c668adb
Signed by: defaultkavy
GPG Key ID: DFBB22C4E69D7826
2 changed files with 14 additions and 24 deletions

View File

@ -10,7 +10,7 @@ export class $Layout extends $Container<HTMLElement> {
COLUNM: 1, COLUNM: 1,
TYPE: 'justified' as $LayoutType, TYPE: 'justified' as $LayoutType,
ROOT: null as null | $Container, ROOT: null as null | $Container,
THRESHOLD: null as null | number ITEM_PROPERTIES: new Map<$Element, $LayoutItemProperties>()
} }
constructor(options?: $ContainerOptions) { constructor(options?: $ContainerOptions) {
super('layout', options); super('layout', options);
@ -61,21 +61,8 @@ export class $Layout extends $Container<HTMLElement> {
root(root: $Container): this root(root: $Container): this
root(root?: $Container) { return $.fluent(this, arguments, () => this._property.ROOT ?? $(document), () => $.set(this._property, 'ROOT', root)) } root(root?: $Container) { return $.fluent(this, arguments, () => this._property.ROOT ?? $(document), () => $.set(this._property, 'ROOT', root)) }
/**
* The top and bottom of display element area, depend by window.innerHeight. Default to the `innerHeight / 2` at everytime scroll.
* Using `resize` event and $State value to change threshold dynamically.
*/
threshold(): number;
threshold(threshold: $StateArgument<number | null> | undefined): this;
threshold(threshold?: $StateArgument<number | null> | undefined) { return $.fluent(this, arguments, () => this._property.THRESHOLD ?? innerHeight, () => $.set(this._property, 'THRESHOLD', threshold)) }
protected get COL_WIDTH() { return (this.offsetWidth - this._property.GAP * (this._property.COLUNM - 1)) / (this._property.COLUNM); } protected get COL_WIDTH() { return (this.offsetWidth - this._property.GAP * (this._property.COLUNM - 1)) / (this._property.COLUNM); }
protected computeLayout() {
if (this._property.TYPE === 'justified') return this.justifiedCompute();
else return this.justifiedCompute();
}
protected justifiedCompute() { protected justifiedCompute() {
const ROW_LIST: Row[] = []; const ROW_LIST: Row[] = [];
const LAYOUT_WIDTH = this.offsetWidth; const LAYOUT_WIDTH = this.offsetWidth;
@ -124,7 +111,8 @@ export class $Layout extends $Container<HTMLElement> {
} }
render() { render() {
if (!this.inDOM()) return; if (!this.inDOM()) return this;
this._property.ITEM_PROPERTIES.clear();
if (this._property.TYPE === 'justified') { if (this._property.TYPE === 'justified') {
const ROW_LIST = this.justifiedCompute(); const ROW_LIST = this.justifiedCompute();
let ROW_POSITION_Y = 0; let ROW_POSITION_Y = 0;
@ -141,6 +129,7 @@ export class $Layout extends $Container<HTMLElement> {
left: `${ITEM_POSITION_X}px`, left: `${ITEM_POSITION_X}px`,
}) })
item.$node.attribute('layout-item-ratio', item.ratio); item.$node.attribute('layout-item-ratio', item.ratio);
this._property.ITEM_PROPERTIES.set(item.$node, {height: ROW.height, width: ITEM_WIDTH, top: ROW_POSITION_Y, left: ITEM_POSITION_X, ratio: item.ratio, $node: item.$node})
ITEM_POSITION_X += (ROW.height * item.ratio) + this._property.GAP; ITEM_POSITION_X += (ROW.height * item.ratio) + this._property.GAP;
} }
ROW_POSITION_Y += ROW.height + this._property.GAP; ROW_POSITION_Y += ROW.height + this._property.GAP;
@ -164,6 +153,7 @@ export class $Layout extends $Container<HTMLElement> {
left: `${COL_POSITION_X}px` left: `${COL_POSITION_X}px`
}) })
item.$node.attribute('layout-item-ratio', item.ratio); item.$node.attribute('layout-item-ratio', item.ratio);
this._property.ITEM_PROPERTIES.set(item.$node, {height: ITEM_HEIGHT, width: COL_WIDTH, top: ITEM_POSITION_Y, left: COL_POSITION_X, ratio: item.ratio, $node: item.$node})
ITEM_POSITION_Y += ITEM_HEIGHT + this._property.GAP; ITEM_POSITION_Y += ITEM_HEIGHT + this._property.GAP;
} }
COL_POSITION_X += COL_WIDTH + this._property.GAP; COL_POSITION_X += COL_WIDTH + this._property.GAP;
@ -179,16 +169,16 @@ export class $Layout extends $Container<HTMLElement> {
protected scrollCompute() { protected scrollCompute() {
if (this.inDOM() === false) return; if (this.inDOM() === false) return;
const threshold = this.threshold(); const scrollTopForLayout = document.documentElement.scrollTop - this.dom.offsetTop
this.children.array.forEach(child => { this._property.ITEM_PROPERTIES.forEach((properties, $node) => {
if (!child.isElement()) return; const itemTop = properties.top
const rect = child.domRect(); const itemBottom = properties.top + properties.height;
if (rect.bottom < -threshold) child.hide(true, false); if (itemBottom > scrollTopForLayout && itemTop < scrollTopForLayout + innerHeight + properties.height) $node.hide(false, false);
else if (rect.top > innerHeight + threshold) child.hide(true, false); else $node.hide(true, false);
else child.hide(false, false);
}) })
this.children.render(); this.children.render();
} }
} }
export type $LayoutType = 'justified' | 'waterfall' export type $LayoutType = 'justified' | 'waterfall';
export type $LayoutItemProperties = {height: number, width: number, top: number, left: number, ratio: number, $node: $Element};

View File

@ -1,7 +1,7 @@
{ {
"name": "@elexis/layout", "name": "@elexis/layout",
"description": "A simple justified/waterfall layout for ElexisJS", "description": "A simple justified/waterfall layout for ElexisJS",
"version": "0.1.2", "version": "0.1.3",
"author": { "author": {
"name": "defaultkavy", "name": "defaultkavy",
"email": "defaultkavy@gmail.com", "email": "defaultkavy@gmail.com",