Skip to content

Commit

Permalink
Arnav/image lazy loading (#463)
Browse files Browse the repository at this point in the history
* Added lazy loading for all images and custom lazy loading for the YouTube video player on the workshops page

* Updated comment detailing what YouTube format needs to look like

* Added animations for lazy loading, used CSS to show video controls for workshop page

* Fixed header issue with workshop page

* Fixed gray border underneath workshop video thumbnails

* Change background color to transparent
  • Loading branch information
aroy23 authored Jan 31, 2025
1 parent 05abbad commit b130707
Show file tree
Hide file tree
Showing 12 changed files with 228 additions and 78 deletions.
18 changes: 18 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@szhsin/react-menu": "^4.2.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-lazy-load-image-component": "^1.6.3",
"react-player": "^2.16.0",
"react-router-dom": "^7.0.2",
"react-slick": "^0.30.3",
Expand Down
3 changes: 2 additions & 1 deletion src/components/Gallery/Winner.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import '../../styles/Gallery.css';
import { LazyLoadImage } from 'react-lazy-load-image-component';

export default function Winner({ year, appName, description, category, image, link}) {

Expand All @@ -12,7 +13,7 @@ export default function Winner({ year, appName, description, category, image, li
return (
<div className='winner-container'>
<div className='winner-image'>
<img src={currentImage} alt={appName} />
<LazyLoadImage src={currentImage} alt={appName} />
</div>
<div className='winner-info'>
<h2 className='winner-title'>{appName}</h2>
Expand Down
3 changes: 2 additions & 1 deletion src/components/General/Navbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Link } from 'react-router-dom';
import { Menu } from '@geist-ui/icons';
import '../../styles/Navbar.css';
import HackLogo from '../../images/hothXI-logo.svg';
import { LazyLoadImage } from 'react-lazy-load-image-component';

export default function Navbar() {
const [isOpen, setIsOpen] = useState(false);
Expand Down Expand Up @@ -38,7 +39,7 @@ export default function Navbar() {
return (
<nav className='navbar'>
<Link to='/' onClick={closeMenu} className='nav-hack'>
<img src={HackLogo} alt='ACM Hack Logo' className='nav-hack-logo' />
<LazyLoadImage src={HackLogo} alt='ACM Hack Logo' className='nav-hack-logo' />
<h1>Hack on the Hill</h1>
</Link>

Expand Down
10 changes: 9 additions & 1 deletion src/components/Home/Banner.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import React from 'react';
import '../../styles/Home.css';
import hothBanner from '../../images/hothXI-banner.png';
import { LazyLoadImage } from 'react-lazy-load-image-component';
import 'react-lazy-load-image-component/src/effects/blur.css';

export default function Banner() {
return (
<div className='banner-container'>
<img className='hoth-banner' src={hothBanner} alt='HOTH Banner' />
<LazyLoadImage
className='hoth-banner'
src={hothBanner}
alt='HOTH Banner'
placeholderSrc={hothBanner}
effect='blur'
/>

<div className='banner-text'>
<h1>
Expand Down
50 changes: 34 additions & 16 deletions src/components/Home/HothDescription.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import '../../styles/Home.css';
import img1 from '../../images/hoth-title/IMG_1914.jpg';
import img2 from '../../images/hoth-title/IMG_2030.jpg';
import img3 from '../../images/hoth-title/hack_motif_applied.jpg';
import { LazyLoadImage } from 'react-lazy-load-image-component';
import 'react-lazy-load-image-component/src/effects/blur.css';

export default function HothDescription() {
return (
<div>
<div className='hoth-title-container'>
<div data-aos='light-fade-up'>
<div className='hoth-title-container' data-aos='light-fade-up'>
<div>
<div className='title-left'>
<h1 className='hoth-what'>What&apos;s</h1>
</div>
Expand All @@ -18,46 +20,62 @@ export default function HothDescription() {
</div>
</div>
</div>

<div className='img-left-container' data-aos='light-fade-up'>
<div className='img-left'>
<img
<LazyLoadImage
src={img1}
alt='Participants at Hack on the Hill'
className='hoth-img'
wrapperProps={{
style: { transitionDelay: '0.4s' },
}}
placeholderSrc={img1}
effect='blur'
/>
</div>
<p className='img-left-p'>
Hack on the Hill (HOTH XI) is a{' '}
<strong>12-hour beginner-friendly</strong> hackathon designed to be
Hack on the Hill (HOTH XI) is a <strong>12-hour beginner-friendly</strong> hackathon designed to be
your first hackathon experience. HOTH XI will be in-person on the
Hill in the Carnesale Commons Palisades Room. We hope to see you
there!
</p>
</div>

<div className='img-right-container' data-aos='light-fade-up'>
<div className='img-right'>
<img
<LazyLoadImage
src={img2}
alt='Participants at Hack on the Hill'
className='hoth-img'
wrapperProps={{
style: { transitionDelay: '0.4s' },
}}
placeholderSrc={img2}
effect='blur'
/>
</div>
<p className='img-right-p'>
During HOTH, you&apos;ll get to learn from{' '}
<strong>workshops</strong>, receive technical help from{' '}
<strong>mentors</strong>, and meet new people while participating in{' '}
<strong>fun</strong>
social activities. There will be <strong>prizes</strong> for the
best hacks!
During HOTH, you&apos;ll get to learn from <strong>workshops</strong>, receive technical help from <strong>mentors</strong>, and meet new people while participating in <strong>fun</strong>
social activities. There will be <strong>prizes</strong> for the best hacks!
</p>
</div>

<div className='img-left-container' data-aos='light-fade-up'>
<div className='img-left'>
<img src={img3} alt='Hack banner' className='hoth-img' />
<LazyLoadImage
src={img3}
alt='Hack banner'
className='hoth-img'
wrapperProps={{
style: { transitionDelay: '0.4s' },
}}
placeholderSrc={img3}
effect='blur'
/>
</div>
<p className='img-left-p'>
Hack on the Hill is an event hosted by ACM Hack. A student run
organization, Hack&apos;s mission is to help the community to apply
Hack on the Hill is an event hosted by ACM Hack. A student-run organization, Hack&apos;s mission is to help the community to apply
their creativity and build amazing things with code. Check out our{' '}
<a
className='hack-website-link'
Expand All @@ -73,4 +91,4 @@ export default function HothDescription() {
</div>
</div>
);
}
}
10 changes: 9 additions & 1 deletion src/components/Home/PhotoCarousel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import Slider from 'react-slick';
import 'slick-carousel/slick/slick.css';
import 'slick-carousel/slick/slick-theme.css';
import '../../styles/PhotoCarousel.css';
import { LazyLoadImage } from 'react-lazy-load-image-component';
import 'react-lazy-load-image-component/src/effects/blur.css';

import image1 from '../../images/carousel/image1.jpg';
import image2 from '../../images/carousel/image2.jpg';
Expand All @@ -12,6 +14,7 @@ import image5 from '../../images/carousel/image5.jpg';
import image6 from '../../images/carousel/image6.jpg';
import image7 from '../../images/carousel/image7.jpg';


export default function PhotoCarousel() {
const settings = {
dots: true,
Expand All @@ -27,10 +30,15 @@ export default function PhotoCarousel() {
return (
<Slider {...settings}>
{images.map((image, index) => (
<img
<LazyLoadImage
src={image}
alt={`Carousel ${index + 1}`}
className='carousel-image'
wrapperProps={{
style: {transitionDelay: '0.4s'},
}}
placeholderSrc={image}
effect='blur'
key={index}
/>
))}
Expand Down
115 changes: 72 additions & 43 deletions src/components/Workshops/Workshop.jsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,73 @@
import React from 'react';
import '../../styles/Workshops.css';
import ReactPlayer from 'react-player/youtube';
import { Github, Youtube, File } from '@geist-ui/icons';


export default function Workshop({ title, youtube, author, description, readme, slides}) {


const README = readme ?
<button className='workshop-button'>
<Github color='var(--hackAccent)' size={28} />
<a className='workshop-link' href={readme} target='_blank' rel='noreferrer'>Readme</a>
</button> : null;

const SLIDES = slides ?
<button className='workshop-button'>
<File color='var(--hackAccent)' size={28} />
<a className='workshop-link' href={slides} target='_blank' rel='noreferrer'>Slides</a>
</button> : null;
return (
<div className='workshop-container'>
<ReactPlayer
className='workshop-video'
id={'id_' + title.replace(/ /g, '_')}
controls={true}
width='100%'
url={youtube} />
<hgroup className='workshop-header'>
<h3 className='workshop-title'>{title}</h3>
<h4 className='workshop-author'>Taught by: {author}</h4>
</hgroup>
<p className='workshop-description'>{description}</p>
<div className='workshop-links'>
<button className='workshop-button'>
<Youtube color='var(--hackAccent)' size={28} />
<a className='workshop-link' href={youtube} target='_blank' rel='noreferrer'>Video</a>
</button>
{README}
{SLIDES}
</div>
</div>
);
import React, {useState} from 'react';
import '../../styles/Workshops.css';
import ReactPlayer from 'react-player/youtube';
import { Github, Youtube, File } from '@geist-ui/icons';
import { LazyLoadImage } from 'react-lazy-load-image-component';


export default function Workshop({ title, youtube, author, description, readme, slides}) {

const [isPlayerVisible, setIsPlayerVisible] = useState(false);

const README = readme ?
<button className='workshop-button'>
<Github color='var(--hackAccent)' size={28} />
<a className='workshop-link' href={readme} target='_blank' rel='noreferrer'>&nbsp; Readme</a>
</button> : null;

const SLIDES = slides ?
<button className='workshop-button'>
<File color='var(--hackAccent)' size={28} />
<a className='workshop-link' href={slides} target='_blank' rel='noreferrer'>&nbsp; Slides</a>
</button> : null;
return (
<div className='workshop-container'>

{/* Lazy loads thumbnail images
video player is not actually loaded until a thumbnail is clicked */}
<div className='video-container'>
{!isPlayerVisible ? (
<>
<LazyLoadImage
className='video-thumbnail'
src={`https://img.youtube.com/vi/${youtube.split('/').pop()}/0.jpg`}
alt={title}
effect='blur'
onClick={() => setIsPlayerVisible(true)}
/>
<div
className='video-overlay'
onClick={() => setIsPlayerVisible(true)}
>
<div className='play-button'>
<div className='play-button-triangle' />
</div>
</div>
</>
) : (
<ReactPlayer
className='workshop-video'
id={'id_' + title.replace(/ /g, '_')}
controls={true}
width='100%'
url={youtube}
/>
)}
</div>

<hgroup className='workshop-header'>
<h3 className='workshop-title'>{title}</h3>
<h4 className='workshop-author'>Taught by: {author}</h4>
</hgroup>
<p className='workshop-description'>{description}</p>
<div className='workshop-links'>
<button className='workshop-button'>
<Youtube color='var(--hackAccent)' size={28} />
<a className='workshop-link' href={youtube} target='_blank' rel='noreferrer'>&nbsp; Video</a>
</button>
{README}
{SLIDES}
</div>
</div>
);
}
Loading

0 comments on commit b130707

Please sign in to comment.