Created
September 26, 2022 10:34
-
-
Save eseQ/9f4c93c88ee2b89eaef385800886b460 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable @typescript-eslint/no-unsafe-call */ | |
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ | |
/* eslint-disable max-len */ | |
import { useCallback, useEffect, useRef, useState } from 'react'; | |
import Image from 'next/image'; | |
import { useTranslation } from 'next-i18next'; | |
import { useIsMounted } from '@utils'; | |
import useMeasure from 'react-use/lib/useMeasure'; | |
import One from '@images/landing/examples/1.png'; | |
import Two from '@images/landing/examples/2.png'; | |
import Three from '@images/landing/examples/3.png'; | |
import Four from '@images/landing/examples/4.png'; | |
import Five from '@images/landing/examples/5.png'; | |
import Six from '@images/landing/examples/6.png'; | |
import Seven from '@images/landing/examples/7.png'; | |
import Eight from '@images/landing/examples/8.png'; | |
import Nine from '@images/landing/examples/9.png'; | |
import Section from '../elements/Section'; | |
const data = [ | |
[One, Two, Three], | |
[Four, Five, Six], | |
[Seven, Eight, Nine], | |
[], // hack to make the last column workable on safari | |
].map((d) => d.map((i) => ({ name: i.src.match(/(?!.*\/)(.*)$/)?.[0] || '', image: i }))); | |
type Element = { name: string; image: StaticImageData }; | |
type ColumnProps = { | |
list: Element[]; | |
toTop?: boolean; | |
horizontal?: boolean; | |
id?: string; | |
}; | |
const duration = 50000; | |
const margin = 24; | |
function Column({ list, toTop = false, id: columnId = 'default', horizontal }: ColumnProps) { | |
const makeEntries = useCallback((array: Element[]) => { | |
const elements = [...array].map((d) => ({ ...d, name: `${d.name}-cloned` })); | |
let result = [...elements, ...array]; | |
if (!toTop) result = [...array, ...elements]; | |
return result.reduce((r, d) => ({ ...r, [d.name]: d }), {}) as Record<string, Element>; | |
}, [toTop]); | |
const [entries] = useState(() => makeEntries(list)); | |
const order = useRef(Object.keys(entries)); | |
const [ref, { width: boxWidth, height: boxHeight }] = useMeasure<HTMLDivElement>(); | |
const getDistance = useCallback((id: string) => { | |
const { height, width } = entries[id]?.image || {}; | |
if (horizontal) return (boxHeight / height) * width; | |
return (boxWidth / width) * height; | |
}, [boxWidth, boxHeight, entries, horizontal]); | |
const getOffsets = useCallback(() => { | |
let array = [...order.current]; | |
if (toTop) array = array.reverse(); | |
return array.reduce((r, d) => { | |
const prevId = array[array.indexOf(d) - 1]; | |
return { | |
...r, | |
[d]: (prevId ? (getDistance(prevId) + r[prevId]) : 0) + margin, | |
}; | |
}, {} as Record<string, number>); | |
}, [getDistance, toTop]); | |
const [offsets, setOffsets] = useState(getOffsets); | |
useEffect(() => { setOffsets(getOffsets()); }, [getOffsets]); | |
const startElement = order.current[toTop ? 0 : order.current.length - 1]; | |
const distance = Math.round(Math.max(...Object.values(offsets)) + getDistance(startElement)); | |
let position = 'top'; | |
if (!horizontal && toTop) position = 'bottom'; | |
if (horizontal && toTop) position = 'right'; | |
if (horizontal && !toTop) position = 'left'; | |
return ( | |
<> | |
<style> | |
{` | |
@keyframes flight-${columnId} { | |
0% { transform: translate${horizontal ? 'X' : 'Y'}(0) translateZ(0); } | |
100% { transform: translate${horizontal ? 'X' : 'Y'}(${distance * (toTop ? -1 : 1)}px) translateZ(0); } | |
} | |
`} | |
</style> | |
<div | |
ref={ref} | |
className="w-full md:w-[32%] h-[31%] md:h-full relative" | |
style={{ ...(Object.keys(entries).length > 0 ? {} : { width: 0, position: 'absolute' }) }} | |
> | |
<div | |
className={['relative', horizontal ? 'left-1/2 -translate-x-1/2' : 'top-1/2 -translate-y-1/2'].filter(Boolean).join(' ')} | |
style={{ | |
[horizontal ? 'width' : 'height']: Number.isInteger(distance) ? distance : 0, | |
[horizontal ? 'height' : 'width']: '100%', | |
}} | |
> | |
{boxWidth > 0 && Object.keys(entries).map((id) => ( | |
<div | |
key={entries[id].name} | |
className={['absolute', horizontal ? 'h-full top-0' : 'w-full left-0'].filter(Boolean).join(' ')} | |
style={{ | |
height : Math.round(horizontal ? boxHeight : (boxWidth / entries[id].image.width) * entries[id].image.height), | |
width : Math.round(horizontal ? (boxHeight / entries[id].image.height) * entries[id].image.width : boxWidth), | |
[position]: 0, | |
animation : `flight-${columnId} ${duration}ms linear -${Math.round(offsets[id] / (distance / duration))}ms infinite`, | |
}} | |
> | |
<Image | |
src={entries[id].image} | |
placeholder="blur" | |
layout="responsive" | |
/> | |
</div> | |
))} | |
</div> | |
</div> | |
</> | |
); | |
} | |
function Content({ horizontal }: { horizontal?: boolean }) { | |
const isMounted = useIsMounted(); | |
if (!isMounted) return null; | |
return ( | |
<> | |
{data.map((col, index) => ( | |
<Column | |
key={col.map((d) => d.name).join('-')} | |
id={col.map((d) => d.name).join('-').replace(/\./g, '')} | |
list={col} | |
toTop={index % 2 === (horizontal ? 1 : 0)} | |
horizontal={horizontal} | |
/> | |
))} | |
</> | |
); | |
} | |
type Props = { className?: string; horizontal?: boolean; } | |
function Examples({ className, horizontal }: Props) { | |
const { t } = useTranslation('landing'); | |
return ( | |
<Section className={['', className].filter(Boolean).join(' ')}> | |
<h2 className="text-center mx-6 md:mx-0"> | |
{t('examples.title')} | |
</h2> | |
<div className="mt-8 w-full relative pb-[110%] md:pb-[56.25%] overflow-hidden"> | |
<div className="absolute left-0 top-0 h-full w-full flex flex-col md:flex-row justify-between"> | |
<Content horizontal={horizontal} /> | |
</div> | |
</div> | |
</Section> | |
); | |
} | |
export default Examples; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment