import { loadImageAsync , noop , ImageCache } from './util' ; import { VueLazyloadOptions } from '../types/lazyload' ; export default class ReactiveListener { el : HTMLElement | null ; src : string ; error : string | null ; loading : string ; bindType : string | null ; $parent : Element | null ; options : VueLazyloadOptions ; cors : string ; elRenderer : Function ; attempt : number ; naturalHeight : number ; naturalWidth : number ; performanceData : { init : number ; loadStart : number ; loadEnd : number ; } ; state ! : { loading : boolean ; error : boolean ; loaded : boolean ; rendered : boolean ; } ; rect : DOMRect ; _imageCache : ImageCache ; constructor ( el , src , error , loading , bindType , $parent , options , cors , elRenderer , imageCache ) { this . el = el ; this . src = src ; this . error = error ; this . loading = loading ; this . bindType = bindType ; this . attempt = 0 ; this . cors = cors ; this . naturalHeight = 0 ; this . naturalWidth = 0 ; this . options = options ; this . rect = { } as DOMRect ; this . $parent = $parent ; this . elRenderer = elRenderer ; this . _imageCache = imageCache ; this . performanceData = { init : Date . now ( ) , loadStart : 0 , loadEnd : 0 } ; this . filter ( ) ; this . initState ( ) ; this . render ( 'loading' , false ) ; } initState ( ) { if ( 'dataset' in this . el ! ) { this . el . dataset . src = this . src ; } else { this . el ! . setAttribute ( 'data-src' , this . src ) ; } this . state = { loading : false , error : false , loaded : false , rendered : false } ; } record ( event ) { this . performanceData [ event ] = Date . now ( ) ; } update ( option ) { const oldSrc = this . src ; this . src = option . src ; this . loading = option . loading ; this . error = option . error ; this . filter ( ) ; if ( oldSrc !== this . src ) { this . attempt = 0 ; this . initState ( ) ; } } getRect ( ) { this . rect = this . el ! . getBoundingClientRect ( ) ; } checkInView ( ) { this . getRect ( ) ; return this . rect . top < window . innerHeight * this . options . preLoad ! && this . rect . bottom > this . options . preLoadTop ! && this . rect . left < window . innerWidth * this . options . preLoad ! && this . rect . right > 0 ; } filter ( ) { for ( const key in this . options . filter ) { this . options . filter [ key ] ( this , this . options ) ; } } renderLoading ( cb ) { this . state . loading = true ; loadImageAsync ( { src : this . loading , cors : this . cors } , ( ) => { this . render ( 'loading' , false ) ; this . state . loading = false ; cb ( ) ; } , ( ) => { cb ( ) ; this . state . loading = false ; if ( ! this . options . silent ) console . warn ( `VueLazyload log: load failed with loading image(${ this . loading })` ) ; } ) ; } load ( onFinish = noop ) { if ( ( this . attempt > this . options . attempt ! - 1 ) && this . state . error ) { if ( ! this . options . silent ) console . log ( `VueLazyload log: ${ this . src } tried too more than ${ this . options . attempt } times` ) ; onFinish ( ) ; return ; } if ( this . state . rendered && this . state . loaded ) return ; if ( this . _imageCache . has ( this . src as string ) ) { this . state . loaded = true ; this . render ( 'loaded' , true ) ; this . state . rendered = true ; return onFinish ( ) ; } this . renderLoading ( ( ) => { this . attempt ++ ; this . options . adapter . beforeLoad && this . options . adapter . beforeLoad ( this , this . options ) ; this . record ( 'loadStart' ) ; loadImageAsync ( { src : this . src , cors : this . cors } , ( data ) => { this . naturalHeight = data . naturalHeight ; this . naturalWidth = data . naturalWidth ; this . state . loaded = true ; this . state . error = false ; this . record ( 'loadEnd' ) ; this . render ( 'loaded' , false ) ; this . state . rendered = true ; this . _imageCache . add ( this . src ) ; onFinish ( ) ; } , ( err ) => { ! this . options . silent && console . error ( err ) ; this . state . error = true ; this . state . loaded = false ; this . render ( 'error' , false ) ; } ) ; } ) ; } render ( state , cache ) { this . elRenderer ( this , state , cache ) ; } performance ( ) { let state = 'loading' ; let time = 0 ; if ( this . state . loaded ) { state = 'loaded' ; time = ( this . performanceData . loadEnd - this . performanceData . loadStart ) / 1000 ; } if ( this . state . error ) state = 'error' ; return { src : this . src , state , time } ; } $destroy ( ) { this . el = null ; this . src = '' ; this . error = null ; this . loading = '' ; this . bindType = null ; this . attempt = 0 ; } }