diff --git a/package.json b/package.json
index fd356bbff..1a396271b 100644
--- a/package.json
+++ b/package.json
@@ -99,6 +99,7 @@
"react-test-renderer": "16.2.0",
"react-tooltip": "3.8.0",
"react-virtualized": "9.20.1",
+ "react-visibility-sensor": "5.0.2",
"redux": "3.7.2",
"redux-mock-store": "^1.2.3",
"redux-throttle": "0.1.1",
diff --git a/src/components/scratch-image/scratch-image.jsx b/src/components/scratch-image/scratch-image.jsx
index 4cf0c5d95..bd3a6c8eb 100644
--- a/src/components/scratch-image/scratch-image.jsx
+++ b/src/components/scratch-image/scratch-image.jsx
@@ -1,12 +1,57 @@
import PropTypes from 'prop-types';
import React from 'react';
+import VisibilitySensor from 'react-visibility-sensor';
import storage from '../../lib/storage';
class ScratchImage extends React.PureComponent {
+ static get pendingImages () {
+ return this._pendingImages || (this._pendingImages = new Set());
+ }
+ static get maxParallelism () {
+ return this._maxParallelism > 0 ? this._maxParallelism : (this._maxParallelism = 6);
+ }
+
+ static loadPendingImages () {
+ if (this._currentJobs >= this.maxParallelism) {
+ // already busy
+ return;
+ }
+
+ // Find the first visible image. If there aren't any, find the first non-visible image.
+ let nextImage;
+ for (const image of this.pendingImages) {
+ if (image.isVisible) {
+ nextImage = image;
+ break;
+ } else {
+ nextImage = nextImage || image;
+ }
+ }
+
+ // If we found an image to load:
+ // 1) Remove it from the queue
+ // 2) Load the image
+ // 3) Pump the queue again
+ if (nextImage) {
+ this.pendingImages.delete(nextImage);
+ const imageSource = nextImage.props.imageSource;
+ ++this._currentJobs;
+ storage
+ .load(imageSource.assetType, imageSource.assetId)
+ .then(asset => {
+ const dataURI = asset.encodeDataURI();
+ nextImage.setState({
+ imageURI: dataURI
+ });
+ --this._currentJobs;
+ this.loadPendingImages();
+ });
+ }
+ }
+
constructor (props) {
super(props);
-
this.state = {};
Object.assign(this.state, this._loadImageSource(props.imageSource));
}
@@ -24,18 +69,14 @@ class ScratchImage extends React.PureComponent {
*/
_loadImageSource (imageSource) {
if (imageSource.uri) {
+ ScratchImage.pendingImages.delete(this);
return {
imageURI: imageSource.uri,
lastRequestedAsset: null
};
}
if (this.state.lastRequestedAsset !== imageSource.assetId) {
- storage.load(imageSource.assetType, imageSource.assetId)
- .then(asset => {
- this.setState({
- imageURI: asset.encodeDataURI()
- });
- });
+ ScratchImage.pendingImages.add(this);
return {
lastRequestedAsset: imageSource.assetId
};
@@ -49,10 +90,29 @@ class ScratchImage extends React.PureComponent {
imageSource: _imageSource,
...imgProps
} = this.props;
- return (
);
+ return (
+
+ {
+ ({isVisible}) => {
+ this.isVisible = isVisible;
+ if (isVisible) {
+ ScratchImage.loadPendingImages();
+ }
+ return (
+
+ );
+ }
+ }
+
+ );
}
}