first commit

This commit is contained in:
2024-03-20 00:12:01 +03:00
commit 1e0ab27b74
148 changed files with 12934 additions and 0 deletions

View File

@@ -0,0 +1,132 @@
.card {
--on-background: #122F60;
--on-background-secondary: #2C4678;
--background: #D8E2FF;
width: 100%;
height: 580px;
position: relative;
}
.mockupPhoneContainer {
position: absolute;
bottom: 0;
right: 0;
z-index: 0;
}
.backdropImage {
z-index: 1;
bottom: 0;
left: 0;
position: absolute;
width: auto;
height: 650px;
}
.ridebusImageMask {
z-index: 1;
position: absolute;
width: 640px;
height: 640px;
mask-repeat: no-repeat;
mask-size: contain;
mask-position: 50%;
bottom: -140px;
right: -140px;
}
.ridebusImageAnchor {
bottom: 320px;
left: 320px;
position: absolute;
}
.ridebusImage {
z-index: 1;
bottom: -400px;
left: -400px;
position: absolute;
width: 800px;
height: 800px;
}
.screenshot {
z-index: 1;
bottom: 0;
left: 0;
position: absolute;
width: auto;
height: 650px;
}
.smallCitiesLine {
z-index: 1;
position: absolute;
display: flex;
flex-direction: row;
right: -50px;
bottom: 260px;
transform-origin: right center;
transform: rotate(4deg);
svg {
width: 220px;
height: auto;
}
}
.bigCitiesLine {
z-index: 1;
position: absolute;
display: flex;
flex-direction: row;
right: -280px;
bottom: 100px;
transform-origin: right center;
transform: rotate(356deg);
gap: 9px;
svg {
width: 260px;
height: auto;
}
}
@media (max-width: 1180px) {
.card {
height: 490px;
}
.ridebusImageMask {
width: 440px;
height: 440px;
bottom: -70px;
right: -110px;
}
.ridebusImageAnchor {
bottom: 220px;
left: 220px;
}
.smallCitiesLine {
bottom: 200px;
}
.bigCitiesLine {
bottom: 50px;
}
}
@media (max-width: 800px) {
.card {
height: 390px;
}
.smallCitiesLine {
bottom: 160px;
svg {
width: 170px;
}
}
.bigCitiesLine svg {
width: 200px;
}
}

View File

@@ -0,0 +1,144 @@
import { useRef } from 'react';
import styles from './citiesCard.module.scss';
import { Card } from '@/components/card';
import useLocale from '@/utils/useLocale';
import { LocalesMap } from '@/utils/useLocale';
import CityPolotskSmallEn from '@/assets/images/city-polotsk-small-en.svg';
import CityNovopolotskSmallEn from '@/assets/images/city-novopolotsk-small-en.svg';
import CityBigEn from '@/assets/images/city-big-en.svg';
import CityPolotskSmallRu from '@/assets/images/city-polotsk-small-ru.svg';
import CityNovopolotskSmallRu from '@/assets/images/city-novopolotsk-small-ru.svg';
import CityBigRu from '@/assets/images/city-big-ru.svg';
import maskUrl from '@/assets/images/cities-star-mask.svg?url';
import ridebusImage from '@/assets/images/cities-backdrop.jpg';
import Image from 'next/image';
import useFrame from '@/utils/useFrame';
import useScroll from '@/utils/useScroll';
import { useRouter } from 'next/router';
import useMediaQuery from '@/utils/useMediaQuery';
const locales: LocalesMap = {
ru: {
title: 'Поддержка нескольких городов',
description:
'Поддерживаются города Полоцк и Новополоцк, но мы планируем добавлять расписание общественного транспорта и для других городов Беларуси',
},
en: {
title: 'Multi-city support',
description:
'The cities of Polotsk and Novopolotsk are supported, but we plan to add public transportation schedules for other cities of Belarus as well',
},
};
function SmallCitiesLine() {
const router = useRouter();
const rootRef = useRef<HTMLDivElement>(null);
const breakpoint800 = useMediaQuery('(max-width: 800px)');
const cityWidth = breakpoint800 ? 170 : 220;
const swapWidth = cityWidth * 1;
useScroll((scrollY, delta, time) => {
const offset = -(scrollY / 6) - (time / 30);
if (rootRef.current) {
rootRef.current.style.transform = `rotate(3deg) translate3D(${offset % swapWidth + cityWidth}px, 0px, 0px)`;
}
}, {}, [cityWidth]);
const CityPolotsk = router.locale === 'ru' ? CityPolotskSmallRu : CityPolotskSmallEn;
return (
<div ref={rootRef} className={styles.smallCitiesLine}>
<CityPolotsk />
<CityPolotsk />
<CityPolotsk />
<CityPolotsk />
<CityPolotsk />
<CityPolotsk />
<CityPolotsk />
<CityPolotsk />
<CityPolotsk />
</div>
);
}
function BigCitiesLine() {
const router = useRouter();
const rootRef = useRef<HTMLDivElement>(null);
const breakpoint800 = useMediaQuery('(max-width: 800px)');
const cityWidth = (breakpoint800 ? 200 : 260) + 9;
const swapWidth = cityWidth * 1;
useScroll((scrollY, delta, time) => {
const offset = (scrollY / 6) + (time / 30);
if (rootRef.current) {
rootRef.current.style.transform = `rotate(356deg) translate3D(${offset % swapWidth}px, 0px, 0px)`;
}
}, {}, [cityWidth]);
const CityNovopolotsk = router.locale === 'ru' ? CityNovopolotskSmallRu : CityNovopolotskSmallEn;
return (
<div ref={rootRef} className={styles.bigCitiesLine}>
<CityNovopolotsk />
<CityNovopolotsk />
<CityNovopolotsk />
<CityNovopolotsk />
<CityNovopolotsk />
<CityNovopolotsk />
<CityNovopolotsk />
<CityNovopolotsk />
<CityNovopolotsk />
</div>
);
}
export function CitiesCard() {
const t = useLocale(locales);
const ridebusImageMaskRef = useRef<HTMLDivElement>(null);
const ridebusImageRef = useRef<HTMLDivElement>(null);
useFrame((delta, time) => {
if (ridebusImageMaskRef.current && ridebusImageRef.current) {
ridebusImageMaskRef.current.style.transform = `rotate(${-(time * 0.005) % 360}deg) translate3D(0px, 0px, 0px)`;
ridebusImageRef.current.style.transform = `rotate(${(time * 0.005) % 360}deg) translate3D(0px, 0px, 0px)`;
}
});
return (
<Card
className={styles.card}
title={t('title')}
subtitle={t('description')}
backdropOnText
classes={{ subtitle: styles.description, title: styles.title }}
>
<div className={styles.mockupPhoneContainer}>
<div
className={styles.ridebusImageMask}
ref={ridebusImageMaskRef}
style={{
maskImage: `url(${maskUrl.src})`,
}}>
<div ref={ridebusImageRef} className={styles.ridebusImageAnchor}>
<Image
className={styles.ridebusImage}
height={800}
width={800}
placeholder="blur"
alt=""
src={ridebusImage}
/>
</div>
</div>
<SmallCitiesLine />
<BigCitiesLine />
</div>
</Card>
);
}

View File

@@ -0,0 +1 @@
export * from './citiesCard';