Add component for automatic carousel with fading animation

This commit is contained in:
Aarni Halinen
2021-02-22 20:21:52 +02:00
parent 7a0480d9dc
commit b071706bc5
8 changed files with 97 additions and 3 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 865 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 954 KiB

+79
View File
@@ -0,0 +1,79 @@
import React from "react";
import Image from "next/image";
import styled, { keyframes, Keyframes } from "styled-components";
interface CrossFadeImagesProps {
images: string[];
width: number | string;
height: number | string;
}
const SINGLE_IMAGE_PRESENTATION_TIME = 5;
const SINGLE_CROSSFADE_TIME = 1;
const SINGLE_IMAGE_TIME = SINGLE_IMAGE_PRESENTATION_TIME + SINGLE_CROSSFADE_TIME;
const AnimatedImage = styled(Image)<{ layout: "fill"; $delay: number }>`
animation-delay: ${(p) => p.$delay}s;
`;
const Container = styled.div<{ $animation: Keyframes; $duration: number; }>`
position: relative;
& > div {
position: absolute !important;
left: 0;
top: 0;
img {
animation-name: ${(p) => p.$animation};
animation-timing-function: ease-in-out;
animation-iteration-count: infinite;
animation-duration: ${(p) => p.$duration}s;
}
}
`;
const CrossFadeImages: React.FC<CrossFadeImagesProps> = ({
images,
}) => {
const len = images.length;
const TOTAL_TIME = SINGLE_IMAGE_TIME * len;
const animation = keyframes`
0% {
opacity:1;
}
${(SINGLE_IMAGE_PRESENTATION_TIME / TOTAL_TIME) * 100}% {
opacity:1;
}
${(1 / len) * 100}% {
opacity:0;
}
${100 - ((SINGLE_CROSSFADE_TIME / TOTAL_TIME) * 100)}% {
opacity:0;
}
100% {
opacity:1;
}
`;
const delays = images.map((_, idx) => idx * SINGLE_IMAGE_TIME).reverse();
return (
<Container
$animation={animation}
$duration={len * SINGLE_IMAGE_TIME}
>
{ images.map((image, idx) => (
<AnimatedImage
key={image}
src={image}
objectFit="cover"
layout="fill"
$delay={delays[idx]}
/>
)) }
</Container>
);
};
export default CrossFadeImages;
+1
View File
@@ -9,3 +9,4 @@ export { default as FullWidthSection } from "./Sections/FullWidthSection";
export { default as InfoBox } from "./InfoBox";
export { default as Accordion } from "./Accordion/Accordion";
export { default as Link } from "./Link";
export { default as CrossFadeImages } from "./CrossFadeImages";
+17 -3
View File
@@ -6,7 +6,7 @@ import { Event } from "@models/Event";
import { Post } from "@models/Feed";
import {
Divider, CTASection, TextSection, Link,
Divider, CTASection, TextSection, Link, CrossFadeImages,
} from "@components/index";
import ActualPageHero from "./ActualPageHero";
import EventCalendar from "./EventCalendar";
@@ -23,9 +23,12 @@ const Gallery = styled.div`
@media screen and (max-width: ${breakpoints.mobile}) {
flex-flow: column;
& > div {
min-height: 100vw;
}
}
div {
& > div {
flex: 1 0;
}
`;
@@ -49,7 +52,18 @@ const ActualPageView: React.FC<ActualPageViewProps> = ({ events, feed }) => (
</CTASection>
<Gallery>
<Image src="https://placehold.it/400x400" alt="TODO" layout="responsive" objectFit="cover" width={400} height={400} />
{/* <Image src="https://placehold.it/400x400" alt="TODO" layout="responsive" objectFit="cover" width={400} height={400} /> */}
<CrossFadeImages
images={[
"/img/koydenveto-jani-mannonen-miika-koskela-17.jpg",
"/img/koydenveto-jani-mannonen-miika-koskela-28.jpg",
"/img/koydenveto-jani-mannonen-miika-koskela-78.jpg",
"/img/Varaslahto2020-AinoSuomi-73.jpg",
"/img/wappusitsit-jani-mannonen-29.jpg",
]}
width={400}
height={400}
/>
<Image src="https://placehold.it/400x400" alt="TODO" layout="responsive" objectFit="cover" width={400} height={400} />
<Image src="https://placehold.it/400x400" alt="TODO" layout="responsive" objectFit="cover" width={400} height={400} />
</Gallery>