Skip to content

Commit 8943bef

Browse files
authored
Add lazy loading images (#21)
* Add lazy loading images on scroll * Format color to HSL
1 parent 5555a7b commit 8943bef

File tree

5 files changed

+95
-19
lines changed

5 files changed

+95
-19
lines changed

src/AppHeader.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ import { Link } from "react-router-dom";
33

44
import './AppHeader.scss';
55
import headerLogo from './images/site-images/Company-Logo.svg';
6+
import { ImageContainer } from './ImageContainer';
67

78

89
export const AppHeader: React.FC = () => {
910
return (
1011
<div className="appheader">
1112
<div className="appheader__logo">
1213
<Link to="/" aria-label="logo image">
13-
<img src={headerLogo} className="logo-image" alt=""/>
14+
<ImageContainer imgUrl={headerLogo} imgClassName="logo-image" alt="logoImage"/>
1415
</Link>
1516
</div>
1617
<div className="appheader__moltincartcontainer">

src/Home.scss

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
&-title-wrap {
1818
width: 100%;
1919
padding: 15px 0 30px;
20-
background-color: rgba(0,0,0,0.5);
20+
background-color: hsla(0, 0, 0, 0.6);
2121
color: $mainBackgroundColor;
2222
position: absolute;
2323
bottom: 0px;
@@ -146,7 +146,7 @@
146146
&-title-wrap {
147147
width: 100%;
148148
padding: 15px 0 30px;
149-
background-color: rgba(0,0,0,0.5);
149+
background-color: hsla(0, 0, 0, 0.6);
150150
color: $mainBackgroundColor;
151151
position: absolute;
152152
bottom: 0;
@@ -245,7 +245,7 @@
245245
.goods-info {
246246
width: 100%;
247247
padding: 45px 30px 35px;
248-
background-color: rgba(0, 0, 0, 0.6);
248+
background-color: hsla(0, 0, 0, 0.6);
249249
position: absolute;
250250
bottom: 0;
251251
}

src/Home.tsx

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React from 'react';
2+
import { ImageContainer } from './ImageContainer';
23

34
import './Home.scss'
45

@@ -28,7 +29,7 @@ export const Home: React.FC = () => {
2829
return (
2930
<div className="home-page-b2c">
3031
<section className="main-banner">
31-
<img src={bannerImage1} alt={bannerFileName1} className="main-banner-image" />
32+
<ImageContainer imgUrl={bannerImage1} alt={bannerFileName1} imgClassName="main-banner-image" />
3233
<div className="main-banner-title-wrap">
3334
<div className="container">
3435
<h1 className="goods-heading">Go from bean to cup at the touch of a button</h1>
@@ -57,7 +58,7 @@ export const Home: React.FC = () => {
5758
<button type="button" className="epbtn primary learn-more-btn">Learn more</button>
5859
</div>
5960
</div>
60-
<img src={productImage1} alt={productFileName1} className="main-goods-image" />
61+
<ImageContainer imgUrl={productImage1} alt={productFileName1} imgClassName="main-goods-image" />
6162
</div>
6263
</li>
6364
<li className="main-goods__cell">
@@ -68,7 +69,7 @@ export const Home: React.FC = () => {
6869
<p className="goods-description">Make the most of your fresh ingredients with the 3X Bluicer Pro. This high performance blender juicer features our Kinetix®</p>
6970
<button type="button" className="epbtn primary learn-more-btn">Add to cart</button>
7071
</div>
71-
<img src={productImage2} alt={productFileName2} className="main-goods-image" />
72+
<ImageContainer imgUrl={productImage2} alt={productFileName2} imgClassName="main-goods-image" />
7273
</div>
7374
</li>
7475
<li className="main-goods__cell">
@@ -81,7 +82,7 @@ export const Home: React.FC = () => {
8182
</p>
8283
<button type="button" className="epbtn primary learn-more-btn">Ice cream makers</button>
8384
</div>
84-
<img src={productImage3} alt={productFileName3} className="main-goods-image" />
85+
<ImageContainer imgUrl={productImage3} alt={productFileName3} imgClassName="main-goods-image" />
8586
</div>
8687
</li>
8788
<li className="main-goods__cell">
@@ -93,7 +94,7 @@ export const Home: React.FC = () => {
9394
Free shipping on all orders of $50!
9495
</p>
9596
</div>
96-
<img src={productImage4} alt={productFileName4} className="main-goods-image" />
97+
<ImageContainer imgUrl={productImage4} alt={productFileName4} imgClassName="main-goods-image" />
9798
</div>
9899
</li>
99100
<li className="main-goods__cell">
@@ -104,7 +105,7 @@ export const Home: React.FC = () => {
104105
View Our Products in 360
105106
</p>
106107
</div>
107-
<img src={productImage5} alt={productFileName5} className="main-goods-image" />
108+
<ImageContainer imgUrl={productImage5} alt={productFileName5} imgClassName="main-goods-image" />
108109
</div>
109110
</li>
110111
</ul>
@@ -113,7 +114,7 @@ export const Home: React.FC = () => {
113114
</section>
114115

115116
<section className="main-banner banner-section-2">
116-
<img src={bannerImage2} alt={bannerFileName2} className="main-banner-image" />
117+
<ImageContainer imgUrl={bannerImage2} alt={bannerFileName2} imgClassName="main-banner-image" />
117118
<div className="main-banner-title-wrap">
118119
<div className="container">
119120
<h2 className="goods-heading">Immersion Blenders</h2>
@@ -141,7 +142,7 @@ export const Home: React.FC = () => {
141142
<p className="goods-title">Our blenders were voted the best value 3 years in a row</p>
142143
<p className="goods-description">“Bellevie is a quality blender maker that will process your ingredients into smooth, creamy perfection”</p>
143144
</div>
144-
<img src={productImage6} alt={productFileName6} className="main-goods-image" />
145+
<ImageContainer imgUrl={productImage6} alt={productFileName6} imgClassName="main-goods-image" />
145146
</div>
146147
</li>
147148
<li className="main-goods__cell">
@@ -151,7 +152,7 @@ export const Home: React.FC = () => {
151152
<p className="goods-title">Stainless steel perfection</p>
152153
<p className="goods-description">Our blenders are made with top of the line stainless steel. The appliances are built strong and to last. These appliances can wistand even the most active chefs kitchen.</p>
153154
</div>
154-
<img src={productImage7} alt={productFileName7} className="main-goods-image" />
155+
<ImageContainer imgUrl={productImage7} alt={productFileName7} imgClassName="main-goods-image" />
155156
</div>
156157
</li>
157158
</ul>
@@ -160,7 +161,7 @@ export const Home: React.FC = () => {
160161
</section>
161162

162163
<section className="main-banner banner-section-3">
163-
<img src={bannerImage3} alt={bannerFileName3} className="main-banner-image" />
164+
<ImageContainer imgUrl={bannerImage3} alt={bannerFileName3} imgClassName="main-banner-image" />
164165
<div className="main-banner-title-wrap">
165166
<div className="container">
166167
<h2 className="goods-heading">The BellVie Difference</h2>

src/ImageContainer.tsx

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import React, {
2+
useEffect,
3+
useState,
4+
} from 'react';
5+
6+
interface ImageContainerProps {
7+
imgUrl: string;
8+
imgClassName?: string;
9+
alt: string,
10+
imageStyle?: any,
11+
}
12+
13+
export const ImageContainer: React.FC<ImageContainerProps> = (props) => {
14+
const {
15+
imgUrl, imgClassName, alt, imageStyle,
16+
} = props;
17+
18+
const imgAlt = alt != null ? alt : '';
19+
const [error, setError] = useState(false);
20+
const [imageSrc, setImageSrc] = useState<string>();
21+
const [imageRef, setImageRef] = useState<any>();
22+
23+
useEffect(
24+
() => {
25+
let observer:any;
26+
let didCancel = false;
27+
if (imageRef && imageSrc !== imgUrl) {
28+
if (IntersectionObserver) {
29+
observer = new IntersectionObserver(
30+
(entries) => {
31+
entries.forEach((entry) => {
32+
if (
33+
!didCancel
34+
&& (entry.intersectionRatio > 0 || entry.isIntersecting)
35+
) {
36+
setImageSrc(imgUrl);
37+
observer.unobserve(imageRef);
38+
}
39+
});
40+
},
41+
{
42+
threshold: 0,
43+
},
44+
);
45+
observer.observe(imageRef);
46+
} else {
47+
setImageSrc(imgUrl);
48+
}
49+
}
50+
return () => {
51+
didCancel = true;
52+
if (observer && observer.unobserve) {
53+
observer.unobserve(imageRef);
54+
}
55+
};
56+
},
57+
[imgUrl, imageSrc, imageRef],
58+
);
59+
60+
const handlePictureError = () => {
61+
setError(true);
62+
};
63+
64+
if (!error) {
65+
return (<img style={imageStyle} className={imgClassName} ref={setImageRef} alt={imgAlt} src={imageSrc} onError={() => handlePictureError()} />);
66+
}
67+
return null;
68+
};
69+
70+
ImageContainer.defaultProps = {
71+
imgClassName: '',
72+
imageStyle: {},
73+
};

src/ProductMainImage.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from 'react';
22
import { Product, loadImageHref } from './service';
33
import { useResolve } from './hooks';
4+
import { ImageContainer } from './ImageContainer';
45

56
interface ProductMainImageProps {
67
product: Product;
@@ -15,11 +16,11 @@ export const ProductMainImage: React.FC<ProductMainImageProps> = (props) => {
1516
return (
1617
<>
1718
{productImageUrl && (
18-
<img
19-
className="productmainimage"
20-
src={productImageUrl}
21-
style={{ width: props.size, height: props.size, objectFit: 'fill', backgroundColor: productBackground }}
22-
alt={props.product.name}
19+
<ImageContainer
20+
imgClassName="productmainimage"
21+
imgUrl={productImageUrl}
22+
alt={props.product.name}
23+
imageStyle={{ width: props.size, height: props.size, objectFit: 'fill', backgroundColor: productBackground }}
2324
/>
2425
)}
2526
</>

0 commit comments

Comments
 (0)