diff --git a/examples/src/components/app.js b/examples/src/components/app.js
new file mode 100644
index 0000000..2cfa054
--- /dev/null
+++ b/examples/src/components/app.js
@@ -0,0 +1,590 @@
+import React, { useEffect, useState } from 'react';
+import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
+import { darcula } from 'react-syntax-highlighter/dist/esm/styles/prism';
+import { Img, BackgroundImg } from '../../../src';
+import { images } from '../mock';
+import ContainerBox from './container-box';
+import '../style.css';
+
+
+function App (){
+ const [innerWidth, setInnerWidth] = useState(window.innerWidth);
+
+ useEffect(() => {
+ window.addEventListener('resize', () => {
+ setInnerWidth(window.innerWidth);
+ });
+ }, []);
+
+ return(
+
+
+
+
+ React Cloudimage Responsive
+
+
(Blur Hash version)
+
+
Responsive images , now easier than ever.
+
+ Make your existing images responsive without creating new
+ images. Upload one
+ high quality original image and the plugin will resize, compress and accelerate images
+ across
+ the World in your site for all devices. The plugin supports lazy load with blur
+ hash placeholder.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Features
+
+
+ Resize large images to the size needed by your design and
+ generate multiple images for different device screen size
+
+ Strip all unnecessary metadata and optimize JPEG, PNG and GIF compression
+ Efficiently lazy load images to speed initial page load and save bandwidth
+ Very compact representation of a placeholder for an image using blur-hash string
+ Hold the image position so your page doesn't jump while images load
+
+
+
+
+
+
+ How it works
+
+ The plugin detects the width of image's container and pixel ratio{' '}
+ density of{' '}
+ your device to load the exact image size you need. It processes images via{' '}
+ Cloudimage.io service which offers
+ comprehensive automated{' '}
+ image optimization solutions.
+
+
+ When an image is first loaded on your website or mobile app, Cloudimage's resizing servers will{' '}
+ download your origin image from your origin server, resize it and{' '}
+ deliver to your user via lightning-fast Content Delivery Networks (CDNs). Once the image
+ is{' '}
+ resized in the format of your choice, Cloudimage will send it to a Content Delivery Network, which will in
+ turn{' '}
+ deliver it rocket fast to your visitors, responsively across various screen sizes .
+
+
+ Read the following article to learn more about Cloudimage.io service.
+
+
+
+
+
+ In numbers
+
+
+ We have original image stored via CDN with 6240×4160 px resolution and{' '}
+ 8.7 mb size :{' '}
+ https://cdn.scaleflex.it/demo/redcharlie.jpg{' '}
+ link {' '}
+ In the table below we can see what size and resolution will be loaded depending on the image's container.
+
+
+
+
+
+ container size
+ pixel ratio density
+ calculated width
+ result: dimantion | size | link
+
+
+
+
+
+
+ 400px
+
+ 1
+ 400px
+ 400×267 | 18.7 kb | link
+
+
+
+ 2
+ 800px
+ 800×533 | 58.1 kb | link
+
+
+
+
+
+ 570px
+
+ 1
+ 600px
+ 600×400 | 35.4 kb | link
+
+
+
+ 2
+ 1200px
+ 1200x800 | 119 kb | link
+
+
+
+
+
+
+ 720px
+
+ 1
+ 800px
+ 800×533 | 58.1 kb | link
+
+
+
+ 2
+ 1600px
+ 1600px×1066 | 200 kb | link
+
+
+
+
+
+ 1170px
+
+ 1
+ 1200px
+ 1200x800 | 119 kb | link
+
+
+
+ 2
+ 2400px
+ 2400x1600 | 405 kb | link
+
+
+
+
+
+
+
+
+ * The plugin rounds container width to next possible value which can be divided by
+ 100{' '}
+ without the remainder. It's done for cache reasons so that we cache not all images
+ different by{' '}
+ 1px, but only 100px, 200px, 300px …
+
+
+
+
+
+
+
Gallery demo
+
+
Change the size of your browser's window and reload the page to see how the Cloudimage Responsive
+ plugin{' '}
+ will deliver an optimized image for the screen size.
+
+
+
+
+
+
+
+
+
+
+
+
+ {images.slice(1, 7).map((image, index) => (
+
+
+
+
+ original:
{image.original_size} link
+
+
+ ))}
+
+
+
+
+
+
+ original: {images[17].original_size} link
+
+
+
+
You can control your image size/ratio/crop with media query breakpoints
+
Resize your browser window to see how it works
+
+ {` `}
+
+
+
+
+
+
+
+
+
Crop your images
+
+
+
+
Original Image
+
+
+
+ container 200 x200 px
+
+
+
+
+
+
+
+
+
{`
+
+
`}
+
+
+
+
Crop
+
+
+
+ container 200 x200 px
+
+
+
+
+
+
+
+
+ {`
+
+
`}
+
+
+
+
Auto crop
+
+
+
+ container 200 x200 px
+
+
+
+
+
+
+
+
{`
+
+
`}
+
+
+
+
+
+
+
+
Use with background images
+
+ {`... `}
+
+
+
+
+
+
What is Lorem Ipsum?
+
+ Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
+
+
+
+
+
+
+
+
+ Ready to get started?
+ To use the plugin, you will need a Cloudimage token. Don't worry, it only takes seconds to get one by
+ registering{' '}
+ here . Once your token is created, you can
+ configure{' '}
+ it as described below.{' '}
+ This token allows you to use 25GB of image cache and 25GB of worldwide CDN traffic per month for free.
+
+
+
+
+
+
+
+
Install using npm
+
+ npm install --save react-cloudimage-responsive-blur-hash
+
+
+
+
+
+
+ initialize it with your token and the baseURL of your image
+ storage{' '}
+ using CloudimageProvider
+
+
{`import React from 'react';
+ import { render } from 'react-dom';
+ import Img, { CloudimageProvider } from 'react-cloudimage-responsive-blur-hash';
+
+ const cloudimageConfig = {
+ token: 'demo',
+ baseURL: 'https://jolipage.airstore.io/'
+ };
+
+ const cloudimageConfigWithCustomCNAMEDomain = {
+ token: 'demo',
+ baseURL: 'https://jolipage.airstore.io/',
+ customDomain: 'images.airstore.io'
+ };
+
+
+ const App = () => {
+ return (
+
+ Simple demo of react-cloudimage-responsive-blur-hash
+
+
+ );
+ };
+
+ render( , document.body);`}
+
+ Get your Cloudimage tokens here .
+
+
+
+
+
+
+ Implement it, just using the Img component:
+
+
+ {` `}
+
+
+ NOTE: "ratio" is recommended to prevent page layout jumping.{' '}
+ The parameter is used to calculate image height to hold the image position while image is loading.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Filerobot UI family:
+
+
+
+
+
+
+
+
+
+
+
Made with ❤ in 2019 in Paris, Munich and Sofia by the Scaleflex team, the guys behind
Cloudimage.io .
+
+
+
+
+
+
+
+
+
+
+
Your device pixel ratio:
{(window.devicePixelRatio || 1).toFixed(1)}
+
+
Your device width:
+
{innerWidth} px
+
+
+ );
+}
+
+export default App;
\ No newline at end of file
diff --git a/examples/src/components/container-box.js b/examples/src/components/container-box.js
index d7785c5..58d92a8 100644
--- a/examples/src/components/container-box.js
+++ b/examples/src/components/container-box.js
@@ -1,46 +1,45 @@
-import React, { Component } from 'react';
+import React, {
+ useEffect, useRef, useState, useCallback,
+} from 'react';
import { debounce } from 'throttle-debounce'
-class ContainerBox extends Component {
- constructor() {
- super();
+function ContainerBox({ isHeight }) {
+ const [width, setWidth] = useState('---');
+ const [height, setHeight] = useState('---');
- this.box = React.createRef()
- this.state = {
- width: '---',
- height: '---'
- };
- }
+ const box = useRef();
+
+ const setContainerWidthAndHeight = useCallback(() => {
+ setWidth(box.current.parentNode.offsetWidth);
+ setHeight(box.current.parentNode.offsetHeight);
+ }, [box]);
- componentDidMount() {
- this.setState({
- width: this.box.current.parentNode.offsetWidth,
- height: this.box.current.parentNode.offsetHeight
- });
+ useEffect(() => {
+ setContainerWidthAndHeight();
window.addEventListener('resize', debounce(400, () => {
- this.setState({
- width: this.box.current.parentNode.offsetWidth,
- height: this.box.current.parentNode.offsetHeight
- });
- })
- );
- }
-
- render() {
- const { width, height } = this.state;
- const { isHeight } = this.props;
-
- return (
-
- container {isHeight ? '' : 'width:'} {width} {isHeight ? `x ${height}` : ''} px
-
- )
- }
+ setContainerWidthAndHeight();
+ }));
+
+ return () => {
+ window.removeEventListener('resize', debounce);
+ };
+ },[]);
+
+ return(
+
+ container
+ {' '}
+ {isHeight ? '' : 'width:'}
+ {' '}
+ {width}
+ {' '}
+ {isHeight ? `x ${height}` : ''}
+ {' '}
+ px
+
+ );
}
export default ContainerBox;
\ No newline at end of file
diff --git a/examples/src/index.js b/examples/src/index.js
index eff28a0..0dc654b 100644
--- a/examples/src/index.js
+++ b/examples/src/index.js
@@ -1,12 +1,8 @@
-import React, { Component } from 'react';
-import { render } from 'react-dom';
-import Img, { CloudimageProvider, BackgroundImg } from '../../src';
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+import { CloudimageProvider } from '../../src';
+import App from './components/app';
import './style.css';
-import { images } from './mock';
-import ContainerBox from './components/container-box';
-import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
-import { darcula } from 'react-syntax-highlighter/dist/esm/styles/prism';
-
const cloudimageConfig = {
token: 'demo',
@@ -17,592 +13,9 @@ const cloudimageConfig = {
limitFactor: 10
};
-
-class App extends Component {
- state = {
- innerWidth: window.innerWidth
- };
-
- componentDidMount() {
- const self = this;
-
- window.addEventListener('resize', () => {
- self.setState({ innerWidth: window.innerWidth });
- })
- }
-
- render() {
- return (
-
-
-
-
- React Cloudimage Responsive
-
-
(Blur Hash version)
-
-
Responsive images , now easier than ever.
-
- Make your existing images responsive without creating new
- images. Upload one
- high quality original image and the plugin will resize, compress and accelerate images
- across
- the World in your site for all devices. The plugin supports lazy load with blur
- hash placeholder.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Features
-
-
- Resize large images to the size needed by your design and
- generate multiple images for different device screen size
-
- Strip all unnecessary metadata and optimize JPEG, PNG and GIF compression
- Efficiently lazy load images to speed initial page load and save bandwidth
- Very compact representation of a placeholder for an image using blur-hash string
- Hold the image position so your page doesn't jump while images load
-
-
-
-
-
-
- How it works
-
- The plugin detects the width of image's container and pixel ratio{' '}
- density of{' '}
- your device to load the exact image size you need. It processes images via{' '}
- Cloudimage.io service which offers
- comprehensive automated{' '}
- image optimization solutions.
-
-
- When an image is first loaded on your website or mobile app, Cloudimage's resizing servers will{' '}
- download your origin image from your origin server, resize it and{' '}
- deliver to your user via lightning-fast Content Delivery Networks (CDNs). Once the image
- is{' '}
- resized in the format of your choice, Cloudimage will send it to a Content Delivery Network, which will in
- turn{' '}
- deliver it rocket fast to your visitors, responsively across various screen sizes .
-
-
- Read the following article to learn more about Cloudimage.io service.
-
-
-
-
-
- In numbers
-
-
- We have original image stored via CDN with 6240×4160 px resolution and{' '}
- 8.7 mb size :{' '}
- https://cdn.scaleflex.it/demo/redcharlie.jpg{' '}
- link {' '}
- In the table below we can see what size and resolution will be loaded depending on the image's container.
-
-
-
-
-
- container size
- pixel ratio density
- calculated width
- result: dimantion | size | link
-
-
-
-
-
-
- 400px
-
- 1
- 400px
- 400×267 | 18.7 kb | link
-
-
-
- 2
- 800px
- 800×533 | 58.1 kb | link
-
-
-
-
-
- 570px
-
- 1
- 600px
- 600×400 | 35.4 kb | link
-
-
-
- 2
- 1200px
- 1200x800 | 119 kb | link
-
-
-
-
-
-
- 720px
-
- 1
- 800px
- 800×533 | 58.1 kb | link
-
-
-
- 2
- 1600px
- 1600px×1066 | 200 kb | link
-
-
-
-
-
- 1170px
-
- 1
- 1200px
- 1200x800 | 119 kb | link
-
-
-
- 2
- 2400px
- 2400x1600 | 405 kb | link
-
-
-
-
-
-
-
-
- * The plugin rounds container width to next possible value which can be divided by
- 100{' '}
- without the remainder. It's done for cache reasons so that we cache not all images
- different by{' '}
- 1px, but only 100px, 200px, 300px …
-
-
-
-
-
-
-
Gallery demo
-
-
Change the size of your browser's window and reload the page to see how the Cloudimage Responsive
- plugin{' '}
- will deliver an optimized image for the screen size.
-
-
-
-
-
-
-
-
-
-
-
-
- {images.slice(1, 7).map((image, index) => (
-
-
-
-
- original:
{image.original_size} link
-
-
- ))}
-
-
-
-
-
-
- original: {images[17].original_size} link
-
-
-
-
You can control your image size/ratio/crop with media query breakpoints
-
Resize your browser window to see how it works
-
- {` `}
-
-
-
-
-
-
-
-
-
Crop your images
-
-
-
-
Original Image
-
-
-
- container 200 x200 px
-
-
-
-
-
-
-
-
-
{`
-
-
`}
-
-
-
-
Crop
-
-
-
- container 200 x200 px
-
-
-
-
-
-
-
-
- {`
-
-
`}
-
-
-
-
Auto crop
-
-
-
- container 200 x200 px
-
-
-
-
-
-
-
-
{`
-
-
`}
-
-
-
-
-
-
-
-
Use with background images
-
- {`... `}
-
-
-
-
-
-
What is Lorem Ipsum?
-
- Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
-
-
-
-
-
-
-
-
- Ready to get started?
- To use the plugin, you will need a Cloudimage token. Don't worry, it only takes seconds to get one by
- registering{' '}
- here . Once your token is created, you can
- configure{' '}
- it as described below.{' '}
- This token allows you to use 25GB of image cache and 25GB of worldwide CDN traffic per month for free.
-
-
-
-
-
-
-
-
Install using npm
-
- npm install --save react-cloudimage-responsive-blur-hash
-
-
-
-
-
-
- initialize it with your token and the baseURL of your image
- storage{' '}
- using CloudimageProvider
-
-
{`import React from 'react';
-import { render } from 'react-dom';
-import Img, { CloudimageProvider } from 'react-cloudimage-responsive-blur-hash';
-
-const cloudimageConfig = {
- token: 'demo',
- baseURL: 'https://jolipage.airstore.io/'
-};
-
-const cloudimageConfigWithCustomCNAMEDomain = {
- token: 'demo',
- baseURL: 'https://jolipage.airstore.io/',
- customDomain: 'images.airstore.io'
-};
-
-
-const App = () => {
- return (
-
- Simple demo of react-cloudimage-responsive-blur-hash
-
-
- );
-};
-
-render( , document.body);`}
-
- Get your Cloudimage tokens here .
-
-
-
-
-
-
- Implement it, just using the Img component:
-
-
- {` `}
-
-
- NOTE: "ratio" is recommended to prevent page layout jumping.{' '}
- The parameter is used to calculate image height to hold the image position while image is loading.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Filerobot UI family:
-
-
-
-
-
-
-
-
-
-
-
Made with ❤ in 2019 in Paris, Munich and Sofia by the Scaleflex team, the guys behind
Cloudimage.io .
-
-
-
-
-
-
-
-
-
-
-
Your device pixel ratio:
{(window.devicePixelRatio || 1).toFixed(1)}
-
-
Your device width:
-
{this.state.innerWidth} px
-
-
-
- )
- }
-}
-
-render( , document.getElementById("app"));
+const root = ReactDOM.createRoot(document.getElementById("app"));
+root.render(
+
+
+
+);
diff --git a/package.json b/package.json
index 7ead717..c6bf03f 100644
--- a/package.json
+++ b/package.json
@@ -40,14 +40,9 @@
},
"dependencies": {
"cloudimage-responsive-utils": "^2.4.9",
- "core-js": "^3.0.0",
"react-lazyload": "^2.6.7",
"throttle-debounce": "^2.0.1"
},
- "peerDependencies": {
- "react": "^16.3.0",
- "react-dom": "^16.3.0"
- },
"devDependencies": {
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
@@ -64,10 +59,11 @@
"@babel/preset-react": "^7.0.0",
"babel-loader": "^8.0.0",
"css-loader": "^2.0.0",
+ "core-js": "^3.0.0",
"gh-pages": "^2.0.1",
"html-webpack-plugin": "^3.2.0",
- "react": "^16.13.1",
- "react-dom": "^16.13.1",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
"react-syntax-highlighter": "^10.2.0",
"style-loader": "^0.23.1",
"throttle-debounce": "^2.0.1",
diff --git a/src/background-inner.js b/src/background-inner.js
new file mode 100644
index 0000000..d81e9a0
--- /dev/null
+++ b/src/background-inner.js
@@ -0,0 +1,64 @@
+import React, { useState, useEffect } from 'react';
+import { backgroundStyles as styles } from 'cloudimage-responsive-utils';
+import Canvas from './canvas';
+
+function BackgroundInner(props){
+ const {
+ cloudimgURL,
+ className,
+ style,
+ children,
+ blurhash,
+ _ref,
+ config,
+ ...otherProps
+ } = props;
+
+ const [loaded, setLoaded] = useState(false);
+
+ const { delay } = config;
+
+ const onImgLoad = () => {
+ setLoaded(true);
+ }
+
+ const preLoadImg = () => {
+ const img = new Image();
+
+ img.onload = onImgLoad();
+ img.src = cloudimgURL;
+ }
+
+ const containerClassName = [
+ className,
+ 'cloudimage-background',
+ loaded ? 'loaded' : 'loading',
+ ].join(' ').trim();
+
+ useEffect(() => {
+ if (typeof delay !== 'undefined') {
+ setTimeout(() => {
+ preLoadImg();
+ }, delay);
+ } else {
+ preLoadImg();
+ }
+ }, []);
+
+ return(
+
+ {blurhash &&
}
+
+
+ {children}
+
+
+ );
+}
+
+export default BackgroundInner;
diff --git a/src/background.js b/src/background.js
index 5bb248f..58d9731 100644
--- a/src/background.js
+++ b/src/background.js
@@ -1,109 +1,101 @@
-import React, { Component } from 'react';
-import { findDOMNode } from 'react-dom';
+import React, { useEffect, useRef, useState, useMemo } from 'react';
import { isServer, processReactNode } from 'cloudimage-responsive-utils';
import { getFilteredBgProps } from './utils.js';
-import LazyLoad from 'react-lazyload';
-import { backgroundStyles as styles } from 'cloudimage-responsive-utils';
-import Canvas from './canvas';
+// import LazyLoad from 'react-lazyload';
+import BackgroundInner from './background-inner';
+import usePrevious from './hooks/usePrevious';
-class BackgroundImg extends Component {
- constructor(props) {
- super(props);
+function BackgroundImg (props){
+ const { config = {}, src } = props;
- this.server = isServer();
- this.state = { cloudimgURL: '', processed: false };
- }
+ const { innerWidth } = config;
- componentDidMount() {
- if (this.server) return;
+ const [data, setData] = useState({});
- this.processBg();
- }
+ const bgNode = useRef();
+ const server = useMemo(() => isServer(), []);
- componentDidUpdate(prevProps) {
- if (this.server) return;
+ const {
+ height, cloudimgURL, processed,
+ } = data;
- const { config: { innerWidth }, src } = this.props;
+ const processBg = (update, windowScreenBecomesBigger) => {
+ const bgData = processReactNode(
+ props,
+ bgNode.current || bgNode.current.ref,
+ update,
+ windowScreenBecomesBigger,
+ false,
+ );
- if (prevProps.config.innerWidth !== innerWidth) {
- this.processBg(true, innerWidth > prevProps.config.innerWidth);
- }
-
- if (src !== prevProps.src) {
- this.processBg();
+ if (bgData) {
+ setData(bgData);
}
}
- processBg = (update, windowScreenBecomesBigger) => {
- const bgNode = findDOMNode(this);
- const data = processReactNode(this.props, bgNode, update, windowScreenBecomesBigger, false);
-
- if (!data) {
- return;
+ const {
+ alt,
+ className,
+ style,
+ lazyLoadConfig,
+ // lazyLoading = config.lazyLoading,
+ lazyLoading = false,
+ children,
+ blurhash,
+ innerRef,
+ doNotReplaceURL,
+ ...otherProps
+ } = getFilteredBgProps(props);
+
+ const containerProps = {
+ cloudimgURL,
+ className,
+ style,
+ children,
+ blurhash,
+ ...otherProps,
+ };
+
+ const previousProps = usePrevious({ innerWidth: config.innerWidth, src });
+
+ useEffect(() => {
+ if (server || !(bgNode.current || bgNode.current?.ref)) return;
+
+ processBg();
+
+ innerRef.current = bgNode.current || bgNode.current.ref;
+ }, []);
+
+ useEffect(() => {
+ if (!previousProps) return;
+
+ if (previousProps.innerWidth !== innerWidth) {
+ processBg(true, innerWidth > previousProps.innerWidth);
}
- this.setState(data);
- }
-
- render() {
- if (this.server) return {this.props.children}
;
-
- const { height, processed, cloudimgURL } = this.state;
- const {
- alt, className, config, style, lazyLoadConfig,
- lazyLoading = config.lazyLoading,
- children, blurhash, doNotReplaceURL, ...otherProps
- } = getFilteredBgProps(this.props);
-
- if (!processed) return {children}
;
-
- const Container = ;
-
- return lazyLoading ? (
-
- {Container}
-
- ) : Container;
- }
-}
-
-class BackgroundInner extends Component {
- state = { loaded: false };
-
- componentDidMount() {
- this.preLoadImg(this.props.cloudimgURL);
- }
+ if (src !== previousProps.src) {
+ processBg();
+ }
- preLoadImg = (cloudimgURL) => {
- const img = new Image();
+ }, [innerWidth, src]);
- img.onload = this.onImgLoad;
- img.src = cloudimgURL;
- }
+ const Container = ;
+
+ if (server) return {children}
;
- onImgLoad = () => {
- this.setState({ loaded: true });
- }
+ if (!processed && bgNode.current) return {children}
;
- render() {
- const { loaded } = this.state;
- const { cloudimgURL, className, style, children, blurhash, otherProps } = this.props;
-
- return (
-
- {blurhash &&
}
-
-
- {children}
-
-
- )
- }
+ return lazyLoading ? (
+
+ {Container}
+
+ ) : Container;
}
-export default BackgroundImg;
\ No newline at end of file
+export default BackgroundImg;
diff --git a/src/blurhash.js b/src/blurhash.js
index 95c51cf..7762bd0 100644
--- a/src/blurhash.js
+++ b/src/blurhash.js
@@ -1,22 +1,26 @@
-import React, { Component } from 'react';
+import React from 'react';
import {blurHashImgStyes as styles} from "cloudimage-responsive-utils";
import { getFilteredProps } from './utils.js';
import Canvas from './canvas';
-class BlurHash extends Component {
- render() {
- const { blurhash, ratio = 1 } = this.props;
- const { className, preserveSize, imgNodeWidth, imgNodeHeight } = getFilteredProps(this.props);
- return (
-
{blurhash && }
- )
- }
+ );
}
export default BlurHash;
\ No newline at end of file
diff --git a/src/canvas.js b/src/canvas.js
index 01e273c..9a870a6 100644
--- a/src/canvas.js
+++ b/src/canvas.js
@@ -1,25 +1,25 @@
-import React, { Component } from 'react';
+import React, { useEffect, useRef } from 'react';
import {blurHashImgStyes as styles} from "cloudimage-responsive-utils";
import { blurhash } from 'cloudimage-responsive-utils';
-class Canvas extends Component {
- componentDidMount() {
- const pixels = blurhash.decode(this.props.blurhash, 32, 32);
- const canvas = this.refs.canvas;
+function Canvas (props){
+ const { loaded } = props;
+
+ const canvasRef = useRef();
+
+ useEffect(() => {
+ const pixels = blurhash.decode(props.blurhash, 32, 32);
+ const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
const imageData = ctx.getImageData(0, 0, 32, 32);
imageData.data.set(pixels);
ctx.putImageData(imageData, 0, 0);
- }
-
- render() {
- const { loaded } = this.props;
+ });
- return(
-
- )
- }
+ return (
+
+ );
}
export default Canvas;
\ No newline at end of file
diff --git a/src/gatsby/index.js b/src/gatsby/index.js
index 6132a70..a6c2006 100644
--- a/src/gatsby/index.js
+++ b/src/gatsby/index.js
@@ -1,24 +1,38 @@
-import React from 'react';
+import {
+ useContext, forwardRef, useCallback,
+} from 'react';
import ImgComponent from '../img';
import BackgroundImgComponent from '../background';
import CloudimageProvider, { CloudimageContext } from '../provider';
-const Img = (props = {}) => {
+const Img = forwardRef((props, ref) => {
+ const cloudImageContext = useContext(CloudimageContext);
+
+ const callbackRef = useCallback((node) => {
+ if (node && ref) {
+ ref.current = node;
+ }
+ }, []);
+
return (
-
- {(context = {}) => }
-
- )
-}
+
+ );
+});
+
+const BackgroundImg = forwardRef((props, ref) => {
+ const cloudImageContext = useContext(CloudimageContext);
+
+ const callbackRef = useCallback((node) => {
+ if (node && ref) {
+ ref.current = node;
+ }
+ }, []);
-const BackgroundImg = (props = {}) => {
return (
-
- {(context = {}) => }
-
- )
-}
+
+ );
+});
export default Img;
diff --git a/src/hooks/usePrevious.js b/src/hooks/usePrevious.js
new file mode 100644
index 0000000..580c3c3
--- /dev/null
+++ b/src/hooks/usePrevious.js
@@ -0,0 +1,14 @@
+import { useRef, useEffect } from 'react';
+
+
+const usePrevious = (value) => {
+ const ref = useRef();
+
+ useEffect(() => {
+ ref.current = value;
+ });
+
+ return ref.current;
+};
+
+export default usePrevious;
diff --git a/src/img.js b/src/img.js
index 5f4ba81..1949b70 100644
--- a/src/img.js
+++ b/src/img.js
@@ -1,109 +1,148 @@
-import React, { Component } from 'react';
-import { findDOMNode } from 'react-dom';
+import React, { useEffect, useState, useRef, useMemo } from 'react';
import { isServer, processReactNode } from 'cloudimage-responsive-utils';
import { getFilteredProps } from './utils.js';
import {blurHashImgStyes as styles} from "cloudimage-responsive-utils";
-import LazyLoad from 'react-lazyload';
+// import LazyLoad from 'react-lazyload';
import Canvas from './canvas';
+import usePrevious from './hooks/usePrevious';
import { BASE_64_PLACEHOLDER } from 'cloudimage-responsive-utils/dist/constants';
-class Img extends Component {
- constructor(props) {
- super(props);
-
- this.server = isServer();
- this.state = {
- cloudimgURL: '',
- loaded: false,
- processed: false
- };
+function Img (props) {
+ const { config = {}, src, blurhash } = props;
+ const { lazyLoading: _lazyLoading, lazyLoadOffset, delay } = config;
+ const { lazyLoading = _lazyLoading } = props;
+
+ // console.log(props);
+
+ const [loaded, setLoaded] = useState(false);
+ const [data, setData] = useState({});
+
+ const [loadedImageDim, setLoadedImageDim] = useState({});
+
+ const imgNode = useRef();
+ const server = useMemo(() => isServer(), []);
+ const previousProps = usePrevious({ innerWidth: config.innerWidth, src });
+
+ const {
+ height, ratio, cloudimgSRCSET, cloudimgURL, processed, previewLoaded
+ } = data;
+
+ const {
+ alt,
+ className,
+ lazyLoadConfig,
+ preserveSize,
+ imgNodeWidth,
+ imgNodeHeight,
+ doNotReplaceURL,
+ disableAnimation,
+ innerRef,
+ ...otherProps
+ } = getFilteredProps(props);
+
+ const processImg = (update, windowScreenBecomesBigger) => {
+ const imageData = processReactNode(
+ props,
+ imgNode.current || imgNode.current.ref,
+ update,
+ windowScreenBecomesBigger,
+ false
+ );
+
+ if (imageData) {
+ setData(imageData);
+ }
+ };
+
+ const updateLoadedImageSize = (image) => {
+ setLoadedImageDim({
+ width: image.naturalWidth,
+ height: image.naturalHeight,
+ ratio: image.naturalWidth / image.naturalHeight
+ });
}
- componentDidMount() {
- if (this.server) return;
-
- this.processImg();
+ const onImgLoad = (event) => {
+ updateLoadedImageSize(event.target)
+ setLoaded(true);
}
- componentDidUpdate(prevProps, prevState) {
- if (this.server) return;
-
- const { config: { innerWidth }, src } = this.props;
-
- if (prevProps.config.innerWidth !== innerWidth) {
- this.processImg(true, innerWidth > prevProps.config.innerWidth);
+ const getCloudimgSRCSET = () => cloudimgSRCSET
+ ?.map(({ dpr, url }) => `${url} ${dpr}x`).join(', ');
+
+ const pictureClassName = `${className} cloudimage-image ${loaded ? 'loaded' : 'loading'}`
+ .trim();
+
+ const pictureStyles = styles.picture({
+ preserveSize,
+ imgNodeWidth,
+ imgNodeHeight,
+ ratio: ratio || loadedImageDim.ratio,
+ previewLoaded,
+ loaded
+ });
+
+ const picture = (
+
+ {blurhash &&
}
+
+
+
+ );
+
+ useEffect(() => {
+ if (server || !(imgNode.current || imgNode.current?.ref)) return;
+
+ if (typeof delay !== 'undefined') {
+ setTimeout(() => {
+ processImg();
+ }, delay);
+ } else {
+ processImg();
}
- if (src !== prevProps.src) {
- this.processImg();
- }
- }
+ innerRef.current = imgNode.current || imgNode.current.ref;
+ }, []);
- processImg = (update, windowScreenBecomesBigger) => {
- const imgNode = findDOMNode(this);
- const data = processReactNode(this.props, imgNode, update, windowScreenBecomesBigger, false);
+ useEffect(() => {
+ if (!previousProps) return;
- this.setState(data);
- }
+ if (previousProps.innerWidth !== innerWidth) {
+ processImg(
+ true,
+ innerWidth > previousProps.innerWidth
+ );
+ }
- updateLoadedImageSize = image => {
- this.setState({
- loadedImageWidth: image.naturalWidth,
- loadedImageHeight: image.naturalHeight,
- loadedImageRatio: image.naturalWidth / image.naturalHeight
- })
- }
+ if (src !== previousProps.src) {
+ processImg();
+ }
+ }, [innerWidth, src]);
- onImgLoad = (event) => {
- this.updateLoadedImageSize(event.target)
- this.setState({ loaded: true });
- }
+ if (server) return ;
- render() {
- const { config = {}, blurhash } = this.props;
- const { lazyLoading: configLazyLoadingValue } = config;
- const { lazyLoading = configLazyLoadingValue } = this.props;
- const {
- height, ratio, cloudimgSRCSET, cloudimgURL, loaded, processed, previewLoaded, loadedImageRatio
- } = this.state;
-
- if (this.server) return ;
- if (!processed) return
;
-
- const {
- alt, className, lazyLoadConfig, preserveSize, imgNodeWidth, imgNodeHeight, doNotReplaceURL, ...otherProps
- } = getFilteredProps(this.props);
-
- const picture = (
-
- {blurhash &&
}
-
- `${url} ${dpr}x`).join(', ')}
- alt={alt}
- onLoad={this.onImgLoad}
- {...otherProps}
- />
-
- );
+ if (!processed) return
;
- return lazyLoading ? (
-
- {picture}
-
- ) : picture;
- }
+ return lazyLoading ? (
+
+ {picture}
+
+ ) : picture;
}
-
-
-
-export default Img;
\ No newline at end of file
+export default Img;
diff --git a/src/index.js b/src/index.js
index 89d8fd9..c476ed4 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,35 +1,54 @@
import './polyfills';
-import React from 'react';
+import React, {
+ useContext, forwardRef, useCallback,
+} from 'react';
import ImgComponent from './img';
import BackgroundImgComponent from './background';
import BlurHashComponent from './blurhash';
import CloudimageProvider, { CloudimageContext } from './provider';
-const Img = (props = {}) => {
+const Img = forwardRef((props, ref) => {
+ const cloudImageContext = useContext(CloudimageContext);
+
+ const callbackRef = useCallback((node) => {
+ if (node && ref) {
+ ref.current = node;
+ }
+ }, []);
+
return (
-
- {(context = {}) => }
-
- )
-}
+
+ );
+});
+
+const BackgroundImg = forwardRef((props, ref) => {
+ const cloudImageContext = useContext(CloudimageContext);
+
+ const callbackRef = useCallback((node) => {
+ if (node && ref) {
+ ref.current = node;
+ }
+ }, []);
-const BackgroundImg = (props = {}) => {
return (
-
- {(context = {}) => }
-
- )
-}
+
+ );
+});
+
+const BlurHash = forwardRef((props, ref) => {
+ const cloudImageContext = useContext(CloudimageContext);
+
+ const callbackRef = useCallback((node) => {
+ if (node && ref) {
+ ref.current = node;
+ }
+ }, []);
-const BlurHash = (props = {}) => {
return (
-
- {(context = {}) => }
-
- )
-}
+
+ );
+});
export default Img;
-
export { CloudimageProvider, Img, BackgroundImg, BlurHash };
diff --git a/src/provider.js b/src/provider.js
index fe5c0f4..d96e151 100644
--- a/src/provider.js
+++ b/src/provider.js
@@ -1,82 +1,81 @@
-import React, { Component } from 'react';
+import React, {
+ useEffect, useMemo, useState, createContext
+} from 'react';
import { debounce } from 'throttle-debounce';
import { CONSTANTS, processParams } from 'cloudimage-responsive-utils';
-export const CloudimageContext = React.createContext({ config: {} });
+const CloudimageContext = createContext({ cloudImageConfig: {} });
+function CloudimageProvider ({ config = {}, children } = {}) {
+ const {
+ token = '',
+ domain = config.customDomain || 'cloudimg.io',
+ customDomain = false,
+ lazyLoading = true,
+ lazyLoadOffset = 100,
+ baseUrl, // to support old name
+ baseURL,
+ apiVersion = 'v7',
+ presets,
+ ratio = 1.5,
+ params = 'org_if_sml=1',
+ imageSizeAttributes = 'use',
+ exactSize = false,
+ doNotReplaceURL = false,
+ limitFactor = 100,
+ devicePixelRatioList = CONSTANTS.DEVICE_PIXEL_RATIO_LIST,
+ } = config;
-class CloudimageProvider extends Component {
- constructor({ config = {} } = {}) {
- super();
-
- const {
- token = '',
- domain = config.customDomain || 'cloudimg.io',
- customDomain = false,
- lazyLoading = true,
- lazyLoadOffset = 100,
- baseUrl, // to support old name
- baseURL,
- apiVersion = 'v7',
- presets,
- ratio = 1.5,
- params = 'org_if_sml=1',
- imageSizeAttributes = 'use',
- exactSize = false,
- doNotReplaceURL = false,
- limitFactor = 100,
- devicePixelRatioList = CONSTANTS.DEVICE_PIXEL_RATIO_LIST,
- } = config;
+ const [cloudImageConfig, setCloudImageConfig] = useState({
+ token,
+ domain,
+ customDomain,
+ lazyLoading,
+ lazyLoadOffset,
+ baseURL: baseUrl || baseURL,
+ apiVersion,
+ ratio,
+ exactSize,
+ presets: presets ? presets :
+ {
+ xs: '(max-width: 575px)', // to 575 PHONE
+ sm: '(min-width: 576px)', // 576 - 767 PHABLET
+ md: '(min-width: 768px)', // 768 - 991 TABLET
+ lg: '(min-width: 992px)', // 992 - 1199 SMALL_LAPTOP_SCREEN
+ xl: '(min-width: 1200px)' // from 1200 USUALSCREEN
+ },
+ params: processParams(params),
+ innerWidth: typeof window !== 'undefined' ? window.innerWidth : null,
+ previewQualityFactor: 10,
+ doNotReplaceURL,
+ devicePixelRatioList,
+ limitFactor,
+ imageSizeAttributes
+ });
- this.state = {
- token,
- domain,
- customDomain,
- lazyLoading,
- lazyLoadOffset,
- baseURL: baseUrl || baseURL,
- apiVersion,
- ratio,
- exactSize,
- presets: presets ? presets :
- {
- xs: '(max-width: 575px)', // to 575 PHONE
- sm: '(min-width: 576px)', // 576 - 767 PHABLET
- md: '(min-width: 768px)', // 768 - 991 TABLET
- lg: '(min-width: 992px)', // 992 - 1199 SMALL_LAPTOP_SCREEN
- xl: '(min-width: 1200px)' // from 1200 USUALSCREEN
- },
- params: processParams(params),
- innerWidth: typeof window !== 'undefined' ? window.innerWidth : null,
- previewQualityFactor: 10,
- doNotReplaceURL,
- devicePixelRatioList,
- limitFactor,
- imageSizeAttributes
- };
+ const updateDimensions = debounce(100, () => {
+ setCloudImageConfig({ ...cloudImageConfig, innerWidth: window.innerWidth });
+ });
+ useEffect(() => {
if (typeof window !== 'undefined') {
- this.state.innerWidth = window.innerWidth;
- window.addEventListener("resize", this.updateDimensions);
+ window.addEventListener("resize", updateDimensions);
}
- }
- componentWillUnmount() {
- window.removeEventListener('resize', this.updateDimensions);
- }
-
- updateDimensions = debounce(100, () => {
- this.setState({ innerWidth: window.innerWidth });
- });
+ return () => {
+ window.removeEventListener('resize', updateDimensions);
+ }
+ }, []);
- render() {
- return (
-
- {this.props.children}
-
- )
- }
+ return (
+ ({ cloudImageConfig }), [cloudImageConfig])}
+ >
+ {children}
+
+ );
}
-export default CloudimageProvider;
\ No newline at end of file
+export { CloudimageContext };
+export default CloudimageProvider;
diff --git a/src/utils.js b/src/utils.js
index a5be698..25e7efa 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -1,48 +1,21 @@
-export const getFilteredProps = props => {
- const {
- config = {},
- alt = '',
- className = '',
- src,
- sizes,
- width,
- height,
- blurhash,
- ratio,
- params,
- lazyLoading,
- ...otherProps
- } = props;
+export const getFilteredProps = ({
+ config = {}, alt = '', className = '', src, sizes, width,
+ height, params, lazyLoading, blurhash, ratio, ...otherProps
+}) => ({
+ alt,
+ className,
+ imgNodeWidth: width,
+ imgNodeHeight: height,
+ ...otherProps,
+});
- return {
- alt,
- className,
- imgNodeWidth: width,
- imgNodeHeight: height,
- ...otherProps
- };
-};
-
-export const getFilteredBgProps = props => {
- const {
- config = {},
- alt = '',
- className = '',
- src,
- sizes,
- width,
- height,
- blurhash,
- ratio,
- params,
- ...otherProps
- } = props;
-
- return {
- config,
- blurhash,
- alt,
- className,
- ...otherProps
- };
-};
\ No newline at end of file
+export const getFilteredBgProps = ({
+ config = {}, alt = '', className = '', src, sizes, width,
+ height, blurhash, ratio, params, ...otherProps
+}) => ({
+ config,
+ blurhash,
+ alt,
+ className,
+ ...otherProps
+});