From b8f54f61f05bd84228050d7efd0919f5ababce24 Mon Sep 17 00:00:00 2001 From: Nicolas Gallagher Date: Sat, 17 Feb 2018 15:55:37 -0800 Subject: [PATCH] [change] Image uses decode() not requestIdleCallback() Chrome heavily throttles `requestIdleCallback` while scrolling, which causes delays in image loading. Instead, use the relatively new `decode()` API to decode the image off the main thread and prevent jank, without delaying loading itself. Fix #764 Ref #759 --- .../src/exports/Image/index.js | 22 ++++--------------- .../src/modules/ImageLoader/index.js | 11 +++++++++- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/packages/react-native-web/src/exports/Image/index.js b/packages/react-native-web/src/exports/Image/index.js index 9cc16f83b..7e0dea3a4 100644 --- a/packages/react-native-web/src/exports/Image/index.js +++ b/packages/react-native-web/src/exports/Image/index.js @@ -16,7 +16,6 @@ import ImageResizeMode from './ImageResizeMode'; import ImageSourcePropType from './ImageSourcePropType'; import ImageStylePropTypes from './ImageStylePropTypes'; import ImageUriCache from './ImageUriCache'; -import requestIdleCallback, { cancelIdleCallback } from '../../modules/requestIdleCallback'; import StyleSheet from '../StyleSheet'; import StyleSheetPropType from '../../modules/StyleSheetPropType'; import View from '../View'; @@ -78,9 +77,6 @@ type State = { shouldDisplaySource: boolean }; -const getAssetTimeout = source => - typeof source === 'object' && source.timeout ? source.timeout : 1000; - class Image extends Component<*, State> { static displayName = 'Image'; @@ -126,7 +122,6 @@ class Image extends Component<*, State> { _imageRequestId = null; _imageState = null; _isMounted = false; - _loadRequest = null; constructor(props, context) { super(props, context); @@ -216,6 +211,7 @@ class Image extends Component<*, State> { const hiddenImage = displayImage ? createElement('img', { alt: accessibilityLabel || '', + decode: 'async', draggable, ref: this._setImageRef, src: displayImage, @@ -252,22 +248,12 @@ class Image extends Component<*, State> { _createImageLoader() { const { source } = this.props; this._destroyImageLoader(); - this._loadRequest = requestIdleCallback( - () => { - const uri = resolveAssetUri(source); - this._imageRequestId = ImageLoader.load(uri, this._onLoad, this._onError); - this._onLoadStart(); - }, - { timeout: getAssetTimeout(source) } - ); + const uri = resolveAssetUri(source); + this._imageRequestId = ImageLoader.load(uri, this._onLoad, this._onError); + this._onLoadStart(); } _destroyImageLoader() { - if (this._loadRequest) { - cancelIdleCallback(this._loadRequest); - this._loadRequest = null; - } - if (this._imageRequestId) { ImageLoader.abort(this._imageRequestId); this._imageRequestId = null; diff --git a/packages/react-native-web/src/modules/ImageLoader/index.js b/packages/react-native-web/src/modules/ImageLoader/index.js index 314a2ba95..ff6f7a16b 100644 --- a/packages/react-native-web/src/modules/ImageLoader/index.js +++ b/packages/react-native-web/src/modules/ImageLoader/index.js @@ -50,7 +50,16 @@ const ImageLoader = { id += 1; const image = new window.Image(); image.onerror = onError; - image.onload = onLoad; + image.onload = e => { + // avoid blocking the main thread + if (typeof image.decode === 'function') { + image.decode().then(() => { onLoad(e) }); + } else { + setTimeout(() => { + onLoad(e); + }, 0); + } + }; image.src = uri; requests[`${id}`] = image; return id;