1
1
import Vue from '../../utils/vue'
2
-
import { BImg } from './img'
3
2
import { getComponentConfig } from '../../utils/config'
4
-
import { getBCR, eventOn, eventOff } from '../../utils/dom'
5
3
import { hasIntersectionObserverSupport } from '../../utils/env'
4
+
import { VBVisible } from '../../directives/visible'
5
+
import { BImg } from './img'
6
6
7
7
const NAME = 'BImgLazy'
8
8
9
-
const THROTTLE = 100
10
-
const EVENT_OPTIONS = { passive: true, capture: false }
11
-
12
9
export const props = {
13
10
src: {
14
11
type: String,
@@ -81,24 +78,23 @@ export const props = {
81
78
default: false
82
79
},
83
80
offset: {
81
+
// Distance away from viewport (in pixels) before being
82
+
// considered "visible"
84
83
type: [Number, String],
85
84
default: 360
86
-
},
87
-
throttle: {
88
-
type: [Number, String],
89
-
default: THROTTLE
90
85
}
91
86
}
92
87
93
88
// @vue/component
94
89
export const BImgLazy = /*#__PURE__*/ Vue.extend({
95
90
name: NAME,
91
+
directives: {
92
+
bVisible: VBVisible
93
+
},
96
94
props,
97
95
data() {
98
96
return {
99
-
isShown: false,
100
-
scrollTimeout: null,
101
-
observer: null
97
+
isShown: this.show
102
98
}
103
99
},
104
100
computed: {
@@ -118,121 +114,66 @@ export const BImgLazy = /*#__PURE__*/ Vue.extend({
118
114
watch: {
119
115
show(newVal, oldVal) {
120
116
if (newVal !== oldVal) {
121
-
this.isShown = newVal
122
-
if (!newVal) {
123
-
// Make sure listeners are re-enabled if img is force set to blank
124
-
this.setListeners(true)
117
+
// If IntersectionObserver support is not available, image is always shown
118
+
const visible = hasIntersectionObserverSupport ? newVal : true
119
+
this.isShown = visible
120
+
if (visible !== newVal) {
121
+
// Ensure the show prop is synced (when no IntersectionObserver)
122
+
this.$nextTick(this.updateShowProp)
125
123
}
126
124
}
127
125
},
128
126
isShown(newVal, oldVal) {
129
127
if (newVal !== oldVal) {
130
128
// Update synched show prop
131
-
this.$emit('update:show', newVal)
129
+
this.updateShowProp()
132
130
}
133
131
}
134
132
},
135
-
created() {
136
-
this.isShown = this.show
137
-
},
138
133
mounted() {
139
-
if (this.isShown) {
140
-
this.setListeners(false)
141
-
} else {
142
-
this.setListeners(true)
143
-
}
144
-
},
145
-
activated() /* istanbul ignore next */ {
146
-
if (!this.isShown) {
147
-
this.setListeners(true)
148
-
}
149
-
},
150
-
deactivated() /* istanbul ignore next */ {
151
-
this.setListeners(false)
152
-
},
153
-
beforeDestroy() {
154
-
this.setListeners(false)
134
+
// If IntersectionObserver is not available, image is always shown
135
+
this.isShown = hasIntersectionObserverSupport ? this.show : true
155
136
},
156
137
methods: {
157
-
setListeners(on) {
158
-
if (this.scrollTimeout) {
159
-
clearTimeout(this.scrollTimeout)
160
-
this.scrollTimeout = null
161
-
}
162
-
/* istanbul ignore next: JSDOM doen't support IntersectionObserver */
163
-
if (this.observer) {
164
-
this.observer.unobserve(this.$el)
165
-
this.observer.disconnect()
166
-
this.observer = null
167
-
}
168
-
const winEvts = ['scroll', 'resize', 'orientationchange']
169
-
winEvts.forEach(evt => eventOff(window, evt, this.onScroll, EVENT_OPTIONS))
170
-
eventOff(this.$el, 'load', this.checkView, EVENT_OPTIONS)
171
-
eventOff(document, 'transitionend', this.onScroll, EVENT_OPTIONS)
172
-
if (on) {
173
-
/* istanbul ignore if: JSDOM doen't support IntersectionObserver */
174
-
if (hasIntersectionObserverSupport) {
175
-
this.observer = new IntersectionObserver(this.doShow, {
176
-
root: null, // viewport
177
-
rootMargin: `${parseInt(this.offset, 10) || 0}px`,
178
-
threshold: 0 // percent intersection
179
-
})
180
-
this.observer.observe(this.$el)
181
-
} else {
182
-
// Fallback to scroll/etc events
183
-
winEvts.forEach(evt => eventOn(window, evt, this.onScroll, EVENT_OPTIONS))
184
-
eventOn(this.$el, 'load', this.checkView, EVENT_OPTIONS)
185
-
eventOn(document, 'transitionend', this.onScroll, EVENT_OPTIONS)
186
-
}
187
-
}
138
+
updateShowProp() {
139
+
this.$emit('update:show', this.isShown)
188
140
},
189
-
doShow(entries) {
190
-
if (entries && (entries[0].isIntersecting || entries[0].intersectionRatio > 0.0)) {
141
+
doShow(visible) {
142
+
// If IntersectionObserver is not supported, the callback
143
+
// will be called with `null` rather than `true` or `false`
144
+
if ((visible || visible === null) && !this.isShown) {
191
145
this.isShown = true
192
-
this.setListeners(false)
193
-
}
194
-
},
195
-
checkView() {
196
-
// check bounding box + offset to see if we should show
197
-
/* istanbul ignore next: should rarely occur */
198
-
if (this.isShown) {
199
-
this.setListeners(false)
200
-
return
201
-
}
202
-
const offset = parseInt(this.offset, 10) || 0
203
-
const docElement = document.documentElement
204
-
const view = {
205
-
l: 0 - offset,
206
-
t: 0 - offset,
207
-
b: docElement.clientHeight + offset,
208
-
r: docElement.clientWidth + offset
209
-
}
210
-
// JSDOM Doesn't support BCR, but we fake it in the tests
211
-
const box = getBCR(this.$el)
212
-
if (box.right >= view.l && box.bottom >= view.t && box.left <= view.r && box.top <= view.b) {
213
-
// image is in view (or about to be in view)
214
-
this.doShow([{ isIntersecting: true }])
215
-
}
216
-
},
217
-
onScroll() {
218
-
/* istanbul ignore if: should rarely occur */
219
-
if (this.isShown) {
220
-
this.setListeners(false)
221
-
} else {
222
-
clearTimeout(this.scrollTimeout)
223
-
this.scrollTimeout = setTimeout(this.checkView, parseInt(this.throttle, 10) || THROTTLE)
224
146
}
225
147
}
226
148
},
227
149
render(h) {
150
+
const directives = []
151
+
if (!this.isShown) {
152
+
// We only add the visible directive if we are not shown
153
+
directives.push({
154
+
// Visible directive will silently do nothing if
155
+
// IntersectionObserver is not supported
156
+
name: 'b-visible',
157
+
// Value expects a callback (passed one arg of `visible` = `true` or `false`)
158
+
value: this.doShow,
159
+
modifiers: {
160
+
// Root margin from viewport
161
+
[`${parseInt(this.offset, 10) || 0}`]: true,
162
+
// Once the image is shown, stop observing
163
+
once: true
164
+
}
165
+
})
166
+
}
167
+
228
168
return h(BImg, {
169
+
directives,
229
170
props: {
230
171
// Computed value props
231
172
src: this.computedSrc,
232
173
blank: this.computedBlank,
233
174
width: this.computedWidth,
234
175
height: this.computedHeight,
235
-
// Passthough props
176
+
// Passthrough props
236
177
alt: this.alt,
237
178
blankColor: this.blankColor,
238
179
fluid: this.fluid,
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4