implement lazy images

This commit is contained in:
Nolan Lawson 2018-03-13 23:07:30 -07:00
parent 9d779a203c
commit 436c8c05aa
3 changed files with 86 additions and 37 deletions

View file

@ -0,0 +1,47 @@
<div class="lazy-image"
style="width: {{width}}px; height: {{height}}px; background: {{background}};">
{{#if displaySrc}}
<img
class="{{hidden ? 'hidden' : ''}} {{className || ''}}"
aria-hidden="{{ariaHidden || ''}}"
alt="{{alt || ''}}"
src="{{displaySrc}}"
:width
:height
/>
{{/if}}
</div>
<style>
.lazy-image {
overflow: hidden;
}
.lazy-image img {
transition: opacity 0.2s linear;
}
</style>
<script>
export default {
oncreate() {
requestAnimationFrame(() => {
let img = new Image()
let src = this.get('src')
let fallback = this.get('fallback')
img.onload = () => {
requestAnimationFrame(() => {
this.set({
displaySrc: src,
hidden: true
})
requestAnimationFrame(() => {
this.set({hidden: false})
})
})
}
img.onerror = () => {
this.set({displaySrc: fallback})
}
img.src = src
})
}
}
</script>

View file

@ -17,14 +17,15 @@
playsinline playsinline
/> />
{{else}} {{else}}
<img class="{{class}} {{imageError ? 'image-error' : ''}}" <LazyImage
alt="{{label}}" alt="{{label}}"
src="{{imageError ? oneTransparentPixel : staticSrc}}" src="{{staticSrc}}"
width="{{width}}" fallback="{{oneTransparentPixel}}"
height="{{height}}" :width
on:imgLoad="set({imageLoaded: true})" :height
on:imgLoadError="set({imageError: true})" background="var(--loading-bg)"
/> className="{{class}}"
/>
{{/if}} {{/if}}
<PlayVideoIcon className="{{playing ? 'hidden' : ''}}"/> <PlayVideoIcon className="{{playing ? 'hidden' : ''}}"/>
</div> </div>
@ -39,9 +40,9 @@
</style> </style>
<script> <script>
import { mouseover } from '../_utils/events' import { mouseover } from '../_utils/events'
import { imgLoad, imgLoadError } from '../_utils/events'
import PlayVideoIcon from './PlayVideoIcon.html' import PlayVideoIcon from './PlayVideoIcon.html'
import { ONE_TRANSPARENT_PIXEL } from '../_static/media' import { ONE_TRANSPARENT_PIXEL } from '../_static/media'
import LazyImage from './LazyImage.html'
export default { export default {
methods: { methods: {
@ -50,15 +51,14 @@
} }
}, },
events: { events: {
mouseover, mouseover
imgLoad,
imgLoadError
}, },
data: () => ({ data: () => ({
oneTransparentPixel: ONE_TRANSPARENT_PIXEL oneTransparentPixel: ONE_TRANSPARENT_PIXEL
}), }),
components: { components: {
PlayVideoIcon PlayVideoIcon,
LazyImage
} }
} }
</script> </script>

View file

@ -6,13 +6,14 @@
style="width: {{inlineWidth}}px; height: {{inlineHeight}}px;" style="width: {{inlineWidth}}px; height: {{inlineHeight}}px;"
> >
<PlayVideoIcon /> <PlayVideoIcon />
<img alt="{{media.description || ''}}" <LazyImage
src="{{imageError ? oneTransparentPixel : media.preview_url}}" alt="{{media.description}}"
width="{{inlineWidth}}" src="{{media.preview_url}}"
height="{{inlineHeight}}" fallback="{{oneTransparentPixel}}"
class="{{noNativeWidthHeight ? 'no-native-width-height' : ''}} {{imageError ? 'image-error' : ''}}" width="{{inlineWidth}}"
on:imgLoad="set({imageLoaded: true})" height="{{inlineHeight}}"
on:imgLoadError="set({imageError: true})" background="var(--loading-bg)"
className="{{noNativeWidthHeight ? 'no-native-width-height' : ''}}"
/> />
</button> </button>
{{else}} {{else}}
@ -48,25 +49,23 @@
playing="{{mouseover}}" playing="{{mouseover}}"
/> />
{{else}} {{else}}
<img class="{{!imageLoaded ? 'image-loading' : ''}} {{imageError ? 'image-error' : ''}} {{noNativeWidthHeight ? 'no-native-width-height' : ''}}" <LazyImage
on:imgLoad="set({imageLoaded: true})" alt="{{media.description}}"
on:imgLoadError="set({imageError: true})" src="{{media.preview_url}}"
alt="{{media.description || ''}}" fallback="{{oneTransparentPixel}}"
src="{{imageError ? oneTransparentPixel : media.preview_url}}" width="{{inlineWidth}}"
width="{{inlineWidth}}" height="{{inlineHeight}}"
height="{{inlineHeight}}" background="var(--loading-bg)"
className="{{noNativeWidthHeight ? 'no-native-width-height' : ''}}"
/> />
{{/if}} {{/if}}
</button> </button>
{{/if}} {{/if}}
<style> <style>
:global(.status-media img.image-loading, .status-media img.image-error) {
background: var(--loading-bg);
}
:global(.status-media video, .status-media img) { :global(.status-media video, .status-media img) {
object-fit: cover; object-fit: cover;
} }
.no-native-width-height { :global(.no-native-width-height) {
background-color: var(--mask-bg); background-color: var(--mask-bg);
} }
.play-video-button { .play-video-button {
@ -86,24 +85,28 @@
cursor: zoom-in; cursor: zoom-in;
} }
:global(.status-media video, .status-media img) { :global(.status-media video, .status-media img, .status-media .lazy-image,
.status-media .show-image-button, .status-media .non-autoplay-gifv,
.status-media .play-video-button) {
max-width: calc(100vw - 40px); max-width: calc(100vw - 40px);
} }
@media (max-width: 767px) { @media (max-width: 767px) {
:global(.status-media video, .status-media img) { :global(.status-media video, .status-media img, .status-media .lazy-image,
.status-media .show-image-button, .status-media .non-autoplay-gifv,
.status-media .play-video-button) {
max-width: calc(100vw - 20px); max-width: calc(100vw - 20px);
} }
} }
</style> </style>
<script> <script>
import { DEFAULT_MEDIA_WIDTH, DEFAULT_MEDIA_HEIGHT } from '../../_static/media' import { DEFAULT_MEDIA_WIDTH, DEFAULT_MEDIA_HEIGHT } from '../../_static/media'
import { imgLoad, imgLoadError } from '../../_utils/events'
import { importDialogs } from '../../_utils/asyncModules' import { importDialogs } from '../../_utils/asyncModules'
import { mouseover } from '../../_utils/events' import { mouseover } from '../../_utils/events'
import NonAutoplayGifv from '../NonAutoplayGifv.html' import NonAutoplayGifv from '../NonAutoplayGifv.html'
import PlayVideoIcon from '../PlayVideoIcon.html' import PlayVideoIcon from '../PlayVideoIcon.html'
import { ONE_TRANSPARENT_PIXEL } from '../../_static/media' import { ONE_TRANSPARENT_PIXEL } from '../../_static/media'
import { store } from '../../_store/store' import { store } from '../../_store/store'
import LazyImage from '../LazyImage.html'
export default { export default {
computed: { computed: {
@ -143,13 +146,12 @@
}), }),
store: () => store, store: () => store,
events: { events: {
imgLoad,
imgLoadError,
mouseover mouseover
}, },
components: { components: {
NonAutoplayGifv, NonAutoplayGifv,
PlayVideoIcon PlayVideoIcon,
LazyImage
} }
} }
</script> </script>