Use img.decode() (#473)
* remove will-change:transform from container * WIP: use img.decode() * more work on img.decode
This commit is contained in:
parent
d10f924620
commit
8949b36873
|
@ -1,13 +1,15 @@
|
|||
{#if error}
|
||||
<svg class={computedClass} aria-hidden="true">
|
||||
<svg class={computedClass} style={svgStyle} aria-hidden="true">
|
||||
<use xlink:href="#fa-user" />
|
||||
</svg>
|
||||
{:elseif $autoplayGifs}
|
||||
<img
|
||||
class={computedClass}
|
||||
aria-hidden="true"
|
||||
<LazyImage
|
||||
className={computedClass}
|
||||
ariaHidden="true"
|
||||
alt=""
|
||||
src={account.avatar}
|
||||
{width}
|
||||
{height}
|
||||
on:imgLoad="set({loaded: true})"
|
||||
on:imgLoadError="set({error: true})" />
|
||||
{:else}
|
||||
|
@ -17,6 +19,8 @@
|
|||
alt=""
|
||||
src={account.avatar}
|
||||
staticSrc={account.avatar_static}
|
||||
{width}
|
||||
{height}
|
||||
{isLink}
|
||||
on:imgLoad="set({loaded: true})"
|
||||
on:imgLoadError="set({error: true})"
|
||||
|
@ -32,48 +36,17 @@
|
|||
background: none;
|
||||
}
|
||||
|
||||
:global(.avatar.size-extra-small) {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
:global(.avatar.size-small) {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
:global(.avatar.size-medium) {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
:global(.avatar.size-big) {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
:global(.avatar.size-big) {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
svg.avatar {
|
||||
fill: var(--deemphasized-text-color);
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import { imgLoad, imgLoadError } from '../_utils/events'
|
||||
import { store } from '../_store/store'
|
||||
import NonAutoplayImg from './NonAutoplayImg.html'
|
||||
import { classname } from '../_utils/classname'
|
||||
import LazyImage from './LazyImage.html'
|
||||
|
||||
export default {
|
||||
events: {
|
||||
imgLoad,
|
||||
imgLoadError
|
||||
},
|
||||
data: () => ({
|
||||
className: void 0,
|
||||
loaded: false,
|
||||
|
@ -82,15 +55,30 @@
|
|||
}),
|
||||
store: () => store,
|
||||
computed: {
|
||||
computedClass: ({ className, loaded, size }) => (classname(
|
||||
computedClass: ({ className, loaded }) => (classname(
|
||||
'avatar',
|
||||
className,
|
||||
loaded && 'loaded',
|
||||
`size-${size}`
|
||||
))
|
||||
loaded && 'loaded'
|
||||
)),
|
||||
width: ({ size, $isMobileSize }) => {
|
||||
switch (size) {
|
||||
case 'extra-small':
|
||||
return 24
|
||||
case 'small':
|
||||
return 48
|
||||
case 'big':
|
||||
return $isMobileSize ? 80 : 100
|
||||
case 'medium':
|
||||
default:
|
||||
return 64
|
||||
}
|
||||
},
|
||||
height: ({ width }) => width,
|
||||
svgStyle: ({ width, height }) => `width: ${width}px; height: ${height}px;`
|
||||
},
|
||||
components: {
|
||||
NonAutoplayImg
|
||||
NonAutoplayImg,
|
||||
LazyImage
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,14 +1,13 @@
|
|||
<div class="lazy-image"
|
||||
style="width: {width}px; height: {height}px; background: {background};">
|
||||
<div class="lazy-image" style={computedStyle} >
|
||||
{#if displaySrc}
|
||||
<img
|
||||
class="{hidden ? 'hidden' : ''} {className || ''}"
|
||||
aria-hidden={ariaHidden || ''}
|
||||
alt={alt || ''}
|
||||
title={alt || ''}
|
||||
src={displaySrc}
|
||||
class={className}
|
||||
aria-hidden={ariaHidden}
|
||||
{alt}
|
||||
{title}
|
||||
{width}
|
||||
{height}
|
||||
src={displaySrc}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -16,40 +15,45 @@
|
|||
.lazy-image {
|
||||
overflow: hidden;
|
||||
}
|
||||
.lazy-image img {
|
||||
transition: opacity 0.2s linear;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import { mark, stop } from '../_utils/marks'
|
||||
import { decodeImage } from '../_utils/decodeImage'
|
||||
|
||||
export default {
|
||||
oncreate () {
|
||||
async oncreate () {
|
||||
mark('LazyImage oncreate()')
|
||||
let img = new Image()
|
||||
let { src } = this.get()
|
||||
let { fallback } = this.get()
|
||||
img.onload = () => {
|
||||
requestAnimationFrame(() => {
|
||||
this.set({
|
||||
displaySrc: src,
|
||||
hidden: true
|
||||
})
|
||||
requestAnimationFrame(() => {
|
||||
this.set({hidden: false})
|
||||
})
|
||||
})
|
||||
}
|
||||
img.onerror = () => {
|
||||
let { src, fallback } = this.get()
|
||||
try {
|
||||
await decodeImage(src)
|
||||
this.set({ displaySrc: src })
|
||||
} catch (e) {
|
||||
if (fallback) {
|
||||
this.set({displaySrc: fallback})
|
||||
}
|
||||
img.src = src
|
||||
}
|
||||
stop('LazyImage oncreate()')
|
||||
},
|
||||
data: () => ({
|
||||
displaySrc: void 0,
|
||||
hidden: false,
|
||||
ariaHidden: false
|
||||
})
|
||||
fallback: void 0,
|
||||
background: '',
|
||||
width: void 0,
|
||||
height: void 0,
|
||||
className: '',
|
||||
ariaHidden: '',
|
||||
alt: '',
|
||||
title: ''
|
||||
}),
|
||||
computed: {
|
||||
computedStyle: ({width, height, background}) => {
|
||||
return [
|
||||
width && `width: ${width}px;`,
|
||||
height && `height: ${height}px;`,
|
||||
background && `background: ${background};`
|
||||
].filter(Boolean).join('')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,22 +1,13 @@
|
|||
{#if staticSrc === src}
|
||||
<img class={className || ''}
|
||||
<img
|
||||
class={computedClass}
|
||||
aria-hidden={ariaHidden}
|
||||
alt={alt || ''}
|
||||
title={alt || ''}
|
||||
{src}
|
||||
on:imgLoad
|
||||
on:imgLoadError />
|
||||
{:else}
|
||||
<img class="{className || ''} non-autoplay-zoom-in {isLink ? 'is-link' : ''}"
|
||||
aria-hidden={ariaHidden}
|
||||
alt={alt || ''}
|
||||
title={alt || ''}
|
||||
src={staticSrc}
|
||||
on:imgLoad
|
||||
on:imgLoadError
|
||||
{alt}
|
||||
{title}
|
||||
{width}
|
||||
{height}
|
||||
src={displaySrc}
|
||||
on:mouseover="onMouseOver(event)"
|
||||
ref:node />
|
||||
{/if}
|
||||
/>
|
||||
<style>
|
||||
.non-autoplay-zoom-in {
|
||||
cursor: zoom-in;
|
||||
|
@ -26,18 +17,44 @@
|
|||
}
|
||||
</style>
|
||||
<script>
|
||||
import { imgLoad, imgLoadError, mouseover } from '../_utils/events'
|
||||
import { mouseover } from '../_utils/events'
|
||||
import { decodeImage } from '../_utils/decodeImage'
|
||||
import { ONE_TRANSPARENT_PIXEL } from '../_static/media'
|
||||
import { classname } from '../_utils/classname'
|
||||
|
||||
export default {
|
||||
async oncreate () {
|
||||
let { staticSrc } = this.get()
|
||||
try {
|
||||
await decodeImage(staticSrc)
|
||||
this.set({ displaySrc: staticSrc })
|
||||
this.fire('imgLoad')
|
||||
} catch (e) {
|
||||
this.fire('imgLoadError', e)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onMouseOver (mouseOver) {
|
||||
let { src, staticSrc } = this.get()
|
||||
this.refs.node.src = mouseOver ? src : staticSrc
|
||||
let { src, staticSrc, displaySrc } = this.get()
|
||||
if (displaySrc !== ONE_TRANSPARENT_PIXEL) {
|
||||
this.set({ displaySrc: mouseOver ? src : staticSrc })
|
||||
}
|
||||
}
|
||||
},
|
||||
events: {
|
||||
imgLoad,
|
||||
imgLoadError,
|
||||
mouseover
|
||||
},
|
||||
data: () => ({
|
||||
displaySrc: ONE_TRANSPARENT_PIXEL,
|
||||
alt: '',
|
||||
title: ''
|
||||
}),
|
||||
computed: {
|
||||
computedClass: ({className, src, staticSrc, isLink}) => (classname(
|
||||
className,
|
||||
src !== staticSrc && 'non-autoplay-zoom-in',
|
||||
isLink && 'is-link'
|
||||
))
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -4,7 +4,9 @@
|
|||
{#if verifyCredentials}
|
||||
<h2>Logged in as:</h2>
|
||||
<div class="acct-current-user">
|
||||
<Avatar account={verifyCredentials} className="acct-avatar" size="big"/>
|
||||
<div class="acct-avatar">
|
||||
<Avatar account={verifyCredentials} size="big"/>
|
||||
</div>
|
||||
<ExternalLink className="acct-handle"
|
||||
href={verifyCredentials.url}>
|
||||
{'@' + verifyCredentials.acct}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { onlineObservers } from './onlineObservers'
|
|||
import { navObservers } from './navObservers'
|
||||
import { autosuggestObservers } from './autosuggestObservers'
|
||||
import { pageVisibilityObservers } from './pageVisibilityObservers'
|
||||
import { resizeObservers } from './resizeObservers'
|
||||
|
||||
export function observers (store) {
|
||||
instanceObservers(store)
|
||||
|
@ -14,4 +15,5 @@ export function observers (store) {
|
|||
navObservers(store)
|
||||
autosuggestObservers(store)
|
||||
pageVisibilityObservers(store)
|
||||
resizeObservers(store)
|
||||
}
|
||||
|
|
14
routes/_store/observers/resizeObservers.js
Normal file
14
routes/_store/observers/resizeObservers.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { registerResizeListener } from '../../_utils/resize'
|
||||
|
||||
export function resizeObservers (store) {
|
||||
if (!process.browser) {
|
||||
return
|
||||
}
|
||||
|
||||
const recalculateIsMobileSize = () => {
|
||||
store.set({isMobileSize: window.matchMedia('(max-width: 767px)').matches})
|
||||
}
|
||||
|
||||
registerResizeListener(recalculateIsMobileSize)
|
||||
recalculateIsMobileSize()
|
||||
}
|
14
routes/_utils/decodeImage.js
Normal file
14
routes/_utils/decodeImage.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
export function decodeImage (src) {
|
||||
if (typeof Image.prototype.decode === 'function') {
|
||||
let img = new Image()
|
||||
img.src = src
|
||||
return img.decode()
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let img = new Image()
|
||||
img.src = src
|
||||
img.onload = resolve
|
||||
img.onerror = reject
|
||||
})
|
||||
}
|
|
@ -1,23 +1,3 @@
|
|||
export function imgLoadError (node, callback) {
|
||||
node.addEventListener('error', callback)
|
||||
|
||||
return {
|
||||
destroy () {
|
||||
node.removeEventListener('error', callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function imgLoad (node, callback) {
|
||||
node.addEventListener('load', callback)
|
||||
|
||||
return {
|
||||
destroy () {
|
||||
node.removeEventListener('load', callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function mouseover (node, callback) {
|
||||
function onMouseEnter () {
|
||||
callback(true) // eslint-disable-line
|
||||
|
|
Loading…
Reference in a new issue