Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 49 additions & 13 deletions components/Carousel.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import Image from 'next/legacy/image';
// import Image from 'next/legacy/image';
import { useState, useEffect } from 'react';

// width of each img in px
// width of each frame in px
// needs to be updated if style.scss changes
const IMAGE_WIDTH = 360;
const FRAME_WIDTH = 340;
const ITEMS_PER_SECTION = 4;

const Carousel = ({ images }) => {
const [sections, setSections] = useState([]);
const sectionWidth = (IMAGE_WIDTH * ITEMS_PER_SECTION) / 2;
const [isOpen, setIsOpen] = useState(false);
const [currentImage, setCurrentImage] = useState(null);
const sectionWidth = (FRAME_WIDTH * ITEMS_PER_SECTION) / 2;

useEffect(() => {
const numItems = images.length;
Expand All @@ -19,15 +21,26 @@ const Carousel = ({ images }) => {
left: (i / ITEMS_PER_SECTION) * sectionWidth,
width: sectionWidth,
items: images.slice(i, i + ITEMS_PER_SECTION).map((item, index) => (
<a
href={item}
target="_blank"
rel="noreferrer noopener"
key={index}
tabIndex="-1"
>
<Image src={item} width={IMAGE_WIDTH} height={IMAGE_WIDTH} alt="" />
</a>
<div className="image-frame" key={index}>
<div
className="image-click"
onClick={() => {
setCurrentImage(item);
setIsOpen(true);
}}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
setCurrentImage(item);
setIsOpen(true);
}
}}
role="button"
tabIndex={0}
style={{ cursor: 'pointer' }}
>
<img src={item} alt="" className="framed-image" />
</div>
</div>
)),
});
}
Expand Down Expand Up @@ -65,6 +78,29 @@ const Carousel = ({ images }) => {
);
})}
</div>
{isOpen && (
<div
className="popup-overlay"
onClick={() => setIsOpen(false)}
onKeyDown={(e) => {
if (e.key === 'Escape') {
setIsOpen(false);
}
}}
role="button"
tabIndex={0}
>
<button id="close-button" onClick={() => setIsOpen(false)}>✕</button>
<div
className="popup-content"
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => e.stopPropagation()}
role="presentation"
>
<img src={currentImage} alt="" className="popup-image" />
</div>
</div>
)}
</div>
);
};
Expand Down
143 changes: 132 additions & 11 deletions styles/components/Carousel.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,156 @@

#carousel {
background: #FAFAFA;
height: 480px;
height: 500px;
min-width: 100%;
overflow: hidden;
position: relative;

#carousel-inner {
width: 1600px;
height: 100%;
}

.carousel-sect {
background: linear-gradient(rgba(variables.$acm-black, 0.8), rgba(variables.$acm-black, 1));
position: absolute;
top: 0;
width: 1600px;
height: 100%;
display: flex;
flex-wrap: wrap;
align-content: flex-start;

div {
background-size: cover;
display: inline-block;
height: 240px;
.image-frame {
width: 340px;
height: 250px;
margin: 0;
opacity: 0.9;
padding: 0;
background: #FFF;
border: 1px solid #D0D0D0;
box-shadow:
0 2px 8px rgba(0, 0, 0, 0.1),
inset 0 0 0 1px rgba(255, 255, 255, 0.8);
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
position: relative;
transition: 0.35s cubic-bezier(0.05, 1.04, 0.72, 0.98);
vertical-align: top;
width: 360px;

// Photo frame texture effect
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}

&:hover {
transform: translateY(-4px);
box-shadow:
0 6px 16px rgba(0, 0, 0, 0.15),
inset 0 0 0 1px rgba(255, 255, 255, 0.9);
}

.image-click {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;

.framed-image {
max-width: 100%;
max-height: 100%;
width: 100%;
height: 100%;
object-fit: cover;
display: block;
transition: 0.35s cubic-bezier(0.05, 1.04, 0.72, 0.98);
}
}

&:hover .framed-image {
transform: scale(1.05);
}
}
}

// Popup Overlay w/ the darkened fullscreen background
.popup-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
animation: fadeIn 0.25s ease-out;

#close-button {
position: fixed;
top: 20px;
right: 20px;
background: rgba(0, 0, 0, 0.7);
color: #FFF;
border: 2px solid #FFF;
font-size: 1.5em;
width: 50px;
height: 50px;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: 0.3s;
z-index: 10001;

&:hover {
opacity: 0.6;
background: rgba(255, 255, 255, 0.2);
transform: scale(1.1);
}
}
}

// Inner box that holds the image
.popup-content {
position: relative;
animation: scaleIn 0.25s ease-out; // smooth zoom animation

img.popup-image {
max-width: min(80vw, 1200px);
max-height: min(80vh, 800px);
border-radius: 10px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
display: block;
}
}

// Fade and scale animations
@keyframes fadeIn {
from {
background: rgba(0, 0, 0, 0);
}

to {
background: rgba(0, 0, 0, 0.8);
}
}

@keyframes scaleIn {
from {
transform: scale(0.9);
opacity: 0;
}

to {
transform: scale(1);
opacity: 1;
}
}
}
Loading