@ -2,16 +2,19 @@ import { logger, LogLevels } from "../lib/logger";
import VisibilityIntersector , { OnVisibilityChange } from "./visibilityIntersector" ;
import VisibilityIntersector , { OnVisibilityChange } from "./visibilityIntersector" ;
type LazyLoadElementBase = {
type LazyLoadElementBase = {
div : HTMLDivElement ,
load : ( ) = > Promise < any >
load : ( target? : HTMLDivElement ) = > Promise < any >
} ;
} ;
type LazyLoadElement = LazyLoadElementBase & {
type LazyLoadElement = Omit < LazyLoadElementBase , ' load ' > & {
wasSeen? : boolean
load : ( target? : HTMLElement ) = > Promise < any > ,
div : HTMLElement
wasSeen? : boolean ,
} ;
} ;
const PARALLEL_LIMIT = 5 ;
export class LazyLoadQueueBase {
export class LazyLoadQueueBase {
protected lazyLoadMedia : Array < LazyLoadElementBase > = [ ] ;
protected queue : Array < LazyLoadElementBase > = [ ] ;
protected inProcess : Set < LazyLoadElementBase > = new Set ( ) ;
protected inProcess : Set < LazyLoadElementBase > = new Set ( ) ;
protected lockPromise : Promise < void > = null ;
protected lockPromise : Promise < void > = null ;
@ -19,13 +22,13 @@ export class LazyLoadQueueBase {
protected log = logger ( 'LL' , LogLevels . error ) ;
protected log = logger ( 'LL' , LogLevels . error ) ;
constructor ( protected parallelLimit = 5 ) {
constructor ( protected parallelLimit = PARALLEL_LIMIT ) {
}
}
public clear() {
public clear() {
this . inProcess . clear ( ) ; // ацтеки забьются, будет плохо
this . inProcess . clear ( ) ; // ацтеки забьются, будет плохо
this . lazyLoadMedia . length = 0 ;
this . queue . length = 0 ;
// unreachable code
// unreachable code
/ * f o r ( l e t i t e m o f t h i s . i n P r o c e s s ) {
/ * f o r ( l e t i t e m o f t h i s . i n P r o c e s s ) {
this . lazyLoadMedia . push ( item ) ;
this . lazyLoadMedia . push ( item ) ;
@ -34,34 +37,40 @@ export class LazyLoadQueueBase {
public lock() {
public lock() {
if ( this . lockPromise ) return ;
if ( this . lockPromise ) return ;
const perf = performance . now ( ) ;
this . lockPromise = new Promise ( ( resolve , reject ) = > {
this . lockPromise = new Promise ( ( resolve , reject ) = > {
this . unlockResolve = resolve ;
this . unlockResolve = resolve ;
} ) ;
} ) ;
this . lockPromise . then ( ( ) = > {
this . log ( 'was locked for:' , performance . now ( ) - perf ) ;
} ) ;
}
}
public unlock() {
public unlock() {
if ( ! this . unlockResolve ) return ;
if ( ! this . unlockResolve ) return ;
this . lockPromise = null ;
this . unlockResolve ( ) ;
this . unlockResolve ( ) ;
this . unlockResolve = null ;
this . unlockResolve = this . lockPromise = null ;
this . processQueue ( ) ;
}
}
public async processItem ( item : LazyLoadElementBase ) {
public async processItem ( item : LazyLoadElementBase ) {
if ( this . lockPromise ) {
return ;
}
this . inProcess . add ( item ) ;
this . inProcess . add ( item ) ;
this . log ( 'will load media' , this . lockPromise , item ) ;
this . log ( 'will load media' , this . lockPromise , item ) ;
try {
try {
if ( this . lockPromise /* && false */ ) {
const perf = performance . now ( ) ;
await this . lockPromise ;
this . log ( 'waited lock:' , performance . now ( ) - perf ) ;
}
//await new Promise((resolve) => setTimeout(resolve, 2e3));
//await new Promise((resolve) => setTimeout(resolve, 2e3));
//await new Promise((resolve, reject) => window.requestAnimationFrame(() => window.requestAnimationFrame(resolve)));
//await new Promise((resolve, reject) => window.requestAnimationFrame(() => window.requestAnimationFrame(resolve)));
await item . load ( item . div ) ;
//await item.load(item.div);
await this . loadItem ( item ) ;
} catch ( err ) {
} catch ( err ) {
this . log . error ( 'loadMediaQueue error:' , err /* , item */ ) ;
this . log . error ( 'loadMediaQueue error:' , err /* , item */ ) ;
}
}
@ -70,25 +79,28 @@ export class LazyLoadQueueBase {
this . log ( 'loaded media' , item ) ;
this . log ( 'loaded media' , item ) ;
if ( this . lazyLoadMedia . length ) {
this . processQueue ( ) ;
this . processQueue ( ) ;
}
}
protected loadItem ( item : LazyLoadElementBase ) {
return item . load ( ) ;
}
}
protected getItem() {
protected getItem() {
return this . lazyLoadMedia . shift ( ) ;
return this . queue . shift ( ) ;
}
}
protected addElement ( el : LazyLoadElementBase ) {
protected addElement ( method : 'push' | 'unshift' , el : LazyLoadElementBase ) {
this . processQueue ( el ) ;
this . queue [ method ] ( el ) ;
this . processQueue ( ) ;
}
}
public async processQueue ( item? : LazyLoadElementBase ) {
public async processQueue ( item? : LazyLoadElementBase ) {
if ( this . parallelLimit > 0 && this . inProcess . size >= this . parallelLimit ) return ;
if ( ! this . queue . length || this . lockPromise || ( this . parallelLimit > 0 && this . inProcess . size >= this . parallelLimit ) ) return ;
do {
do {
if ( item ) {
if ( item ) {
this . lazyLoadMedia . findAndSplice ( i = > i == item ) ;
this . queue . findAndSplice ( i = > i == item ) ;
} else {
} else {
item = this . getItem ( ) ;
item = this . getItem ( ) ;
}
}
@ -100,25 +112,26 @@ export class LazyLoadQueueBase {
}
}
item = null ;
item = null ;
} while ( this . inProcess . size < this . parallelLimit && this . lazyLoadMedia . length ) ;
} while ( this . inProcess . size < this . parallelLimit && this . queue . length ) ;
}
}
public push ( el : LazyLoadElementBase ) {
public push ( el : LazyLoadElementBase ) {
this . lazyLoadMedia . push ( el ) ;
this . addElement ( 'push' , el ) ;
this . addElement ( el ) ;
}
}
public unshift ( el : LazyLoadElementBase ) {
public unshift ( el : LazyLoadElementBase ) {
this . lazyLoadMedia . unshift ( el ) ;
this . addElement ( 'unshift' , el ) ;
this . addElement ( el ) ;
}
}
}
}
export class LazyLoadQueueIntersector extends LazyLoadQueueBase {
export class LazyLoadQueueIntersector extends LazyLoadQueueBase {
protected queue : Array < LazyLoadElement > = [ ] ;
protected inProcess : Set < LazyLoadElement > = new Set ( ) ;
public intersector : VisibilityIntersector ;
public intersector : VisibilityIntersector ;
protected intersectorTimeout : number ;
protected intersectorTimeout : number ;
constructor ( protected parallelLimit = 5 ) {
constructor ( protected parallelLimit = PARALLEL_LIMIT ) {
super ( parallelLimit ) ;
super ( parallelLimit ) ;
}
}
@ -146,6 +159,26 @@ export class LazyLoadQueueIntersector extends LazyLoadQueueBase {
this . intersector . refresh ( ) ;
this . intersector . refresh ( ) ;
}
}
protected loadItem ( item : LazyLoadElement ) {
return item . load ( item . div ) ;
}
protected addElement ( method : 'push' | 'unshift' , el : LazyLoadElement ) {
const item = this . queue . find ( i = > i . div == el . div ) ;
if ( item ) {
return false ;
} else {
for ( const item of this . inProcess ) {
if ( item . div == el . div ) {
return false ;
}
}
}
this . queue [ method ] ( el ) ;
return true ;
}
protected setProcessQueueTimeout() {
protected setProcessQueueTimeout() {
if ( ! this . intersectorTimeout ) {
if ( ! this . intersectorTimeout ) {
this . intersectorTimeout = window . setTimeout ( ( ) = > {
this . intersectorTimeout = window . setTimeout ( ( ) = > {
@ -154,13 +187,18 @@ export class LazyLoadQueueIntersector extends LazyLoadQueueBase {
} , 0 ) ;
} , 0 ) ;
}
}
}
}
public push ( el : LazyLoadElement ) {
super . push ( el ) ;
}
}
export default class LazyLoadQueue extends LazyLoadQueueIntersector {
public unshift ( el : LazyLoadElement ) {
protected lazyLoadMedia : Array < LazyLoadElement > = [ ] ;
super . unshift ( el ) ;
protected inProcess : Set < LazyLoadElement > = new Set ( ) ;
}
}
constructor ( protected parallelLimit = 5 ) {
export default class LazyLoadQueue extends LazyLoadQueueIntersector {
constructor ( protected parallelLimit = PARALLEL_LIMIT ) {
super ( parallelLimit ) ;
super ( parallelLimit ) ;
this . intersector = new VisibilityIntersector ( this . onVisibilityChange ) ;
this . intersector = new VisibilityIntersector ( this . onVisibilityChange ) ;
@ -171,10 +209,10 @@ export default class LazyLoadQueue extends LazyLoadQueueIntersector {
this . log ( 'isIntersecting' , target ) ;
this . log ( 'isIntersecting' , target ) ;
// need for set element first if scrolled
// need for set element first if scrolled
const item = this . lazyLoadMedia . findAndSplice ( i = > i . div == target ) ;
const item = this . queue . findAndSplice ( i = > i . div == target ) ;
if ( item ) {
if ( item ) {
item . wasSeen = true ;
item . wasSeen = true ;
this . lazyLoadMedia . unshift ( item ) ;
this . queue . unshift ( item ) ;
//this.processQueue(item);
//this.processQueue(item);
}
}
@ -183,7 +221,7 @@ export default class LazyLoadQueue extends LazyLoadQueueIntersector {
} ;
} ;
protected getItem() {
protected getItem() {
return this . lazyLoadMedia . findAndSplice ( item = > item . wasSeen ) ;
return this . queue . findAndSplice ( item = > item . wasSeen ) ;
}
}
public async processItem ( item : LazyLoadElement ) {
public async processItem ( item : LazyLoadElement ) {
@ -191,37 +229,34 @@ export default class LazyLoadQueue extends LazyLoadQueueIntersector {
this . intersector . unobserve ( item . div ) ;
this . intersector . unobserve ( item . div ) ;
}
}
protected addElement ( el : LazyLoadElement ) {
protected addElement ( method : 'push' | 'unshift' , el : LazyLoadElement ) {
//super.addElement(el);
const inserted = super . addElement ( method , el ) ;
if ( ! inserted ) return false ;
this . intersector . observe ( el . div ) ;
if ( el . wasSeen ) {
if ( el . wasSeen ) {
super . processQueue ( el ) ;
thi s. processQueue ( el ) ;
} else {
} else if ( ! el . hasOwnProperty ( 'wasSeen' ) ) {
el . wasSeen = false ;
el . wasSeen = false ;
this . intersector . observe ( el . div ) ;
}
}
}
public push ( el : LazyLoadElement ) {
return true ;
super . push ( el ) ;
}
public unshift ( el : LazyLoadElement ) {
super . unshift ( el ) ;
}
}
}
}
export class LazyLoadQueueRepeat extends LazyLoadQueueIntersector {
export class LazyLoadQueueRepeat extends LazyLoadQueueIntersector {
private _lazyLoadMedia : Map < HTMLElement , LazyLoadElementBase > = new Map ( ) ;
private _queue : Map < HTMLElement , LazyLoadElement > = new Map ( ) ;
constructor ( protected parallelLimit = 5 , protected onVisibilityChange? : OnVisibilityChange ) {
constructor ( protected parallelLimit = PARALLEL_LIMIT , protected onVisibilityChange? : OnVisibilityChange ) {
super ( parallelLimit ) ;
super ( parallelLimit ) ;
this . intersector = new VisibilityIntersector ( ( target , visible ) = > {
this . intersector = new VisibilityIntersector ( ( target , visible ) = > {
if ( visible ) {
if ( visible ) {
const item = this . lazyLoadMedia . findAndSplice ( i = > i . div == target ) ;
const item = this . queue . findAndSplice ( i = > i . div == target ) ;
this . lazyLoadMedia . unshift ( item || this . _lazyLoadMedia . get ( target ) ) ;
this . queue . unshift ( item || this . _queue . get ( target ) ) ;
} else {
} else {
this . lazyLoadMedia . findAndSplice ( i = > i . div == target ) ;
this . queue . findAndSplice ( i = > i . div == target ) ;
}
}
this . onVisibilityChange && this . onVisibilityChange ( target , visible ) ;
this . onVisibilityChange && this . onVisibilityChange ( target , visible ) ;
@ -229,6 +264,11 @@ export class LazyLoadQueueRepeat extends LazyLoadQueueIntersector {
} ) ;
} ) ;
}
}
public clear() {
super . clear ( ) ;
this . _queue . clear ( ) ;
}
/ * p u b l i c a s y n c p r o c e s s I t e m ( i t e m : L a z y L o a d E l e m e n t ) {
/ * p u b l i c a s y n c p r o c e s s I t e m ( i t e m : L a z y L o a d E l e m e n t ) {
//await super.processItem(item);
//await super.processItem(item);
await LazyLoadQueueBase . prototype . processItem . call ( this , item ) ;
await LazyLoadQueueBase . prototype . processItem . call ( this , item ) ;
@ -238,8 +278,28 @@ export class LazyLoadQueueRepeat extends LazyLoadQueueIntersector {
}
}
} * /
} * /
public observe ( el : LazyLoadElementBase ) {
public observe ( el : LazyLoadElement ) {
this . _lazyLoadMedia . set ( el . div , el ) ;
this . _queue . set ( el . div , el ) ;
this . intersector . observe ( el . div ) ;
this . intersector . observe ( el . div ) ;
}
}
}
}
export class LazyLoadQueueRepeat2 extends LazyLoadQueueIntersector {
constructor ( protected parallelLimit = PARALLEL_LIMIT , protected onVisibilityChange? : OnVisibilityChange ) {
super ( parallelLimit ) ;
this . intersector = new VisibilityIntersector ( ( target , visible ) = > {
const item = this . queue . findAndSplice ( i = > i . div == target ) ;
if ( visible && item ) {
this . queue . unshift ( item ) ;
}
this . onVisibilityChange && this . onVisibilityChange ( target , visible ) ;
this . setProcessQueueTimeout ( ) ;
} ) ;
}
public observe ( el : HTMLElement ) {
this . intersector . observe ( el ) ;
}
}