/
img.ts
87 lines (77 loc) 路 2.63 KB
/
img.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import { Component } from '../component.ts'
import { h, strToHash } from '../core.ts'
interface Props {
[key: string]: any
src: string
height?: number | string
width?: number | string
lazy?: boolean
placeholder?: any
}
/**
* A useful Image component
* Add <Img lazy ..., to lazy load the img source
* Add <Img width="100" height="100" ..., to specify img element's size.
* Add <Img placeholder="src or element" ...., to prepare placeholder for img.
*/
export class Img extends Component<Props> {
constructor(props: Props) {
super(props)
const { src, key } = props
// id has to be unique
this.id = `${strToHash(src)}-${strToHash(JSON.stringify(props))}`
if (key) this.id += `key-${key}`
// this could also be done in willMount()
if (!this.state) this.setState({ isLoaded: false, image: undefined })
}
didMount() {
const { lazy = true, placeholder, children, key, ref, ...rest } = this.props
if (typeof lazy === 'boolean' && lazy === false) return
const observer = new IntersectionObserver(
(entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
observer.disconnect()
this.state.image = h('img', { ...rest }) as HTMLImageElement
if (this.state.image.complete) {
this.state.isLoaded = true
this.update()
} else {
this.state.image.onload = () => {
this.state.isLoaded = true
this.update()
}
}
}
})
},
{ threshold: [0, 1] }
)
observer.observe(this.elements[0])
}
render() {
const { src, placeholder, children, lazy = true, key, ref, ...rest } = this.props
// return the img tag if not lazy loaded
if (typeof lazy === 'boolean' && lazy === false) {
this.state.image = h('img', { src, ...rest }) as HTMLImageElement
return this.state.image
}
// if it is visible and loaded, show the image
if (this.state.isLoaded) {
return this.state.image
// if the placeholder is an image src
} else if (placeholder && typeof placeholder === 'string') {
return h('img', { src: placeholder, ...rest })
// if the placeholder is an JSX element
} else if (placeholder && typeof placeholder === 'function') {
return placeholder()
} else {
// render a simple box
const style: Record<string, any> = {}
if (rest.width) style.width = `${rest.width}px`
if (rest.height) style.height = `${rest.height}px`
const { width, height, ...others } = rest
return h('div', { style, ...others })
}
}
}