updated slider

This commit is contained in:
2024-08-20 19:01:03 +05:00
parent af02c066f3
commit 51f082e6e1
15 changed files with 133 additions and 31 deletions
+1
View File
@@ -18,6 +18,7 @@
"react-router-dom": "^6.23.1",
"react-router-hash-link": "^2.4.3",
"react-swipeable": "^7.0.1",
"react-usestateref": "^1.0.9",
"usehooks-ts": "^3.1.0",
"zustand": "^4.5.4"
},
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 551 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 867 KiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

+1 -1
View File
@@ -94,7 +94,7 @@ function Figure({
backgroundSize: 'auto,100% 100%',
transition: { duration: 0.075 },
}}
className="flex px-6 2xl:max-w-[22vw] w-full rounded-2xl pt-6 bg-no-repeat bg-[position:bottom_right_24px,bottom_right] h-[262px] relative before:bg-[#14161F] before:w-full after:h-full after:-mt-6 after:-ml-6 after:absolute after:-z-[9999] after:rounded-2xl"
className="flex px-6 2xl:max-w-[22vw] w-full rounded-2xl pt-6 bg-no-repeat bg-[position:bottom_right_24px,bottom_right] h-[262px] relative"
>
<div className="flex flex-col justify-between py-6 max-sm:max-w-[50vw]">
<h3 className="lg:font-medium l-text 2xl:max-w-[70%]">{title}</h3>
+1 -1
View File
@@ -35,7 +35,7 @@ export function Products() {
<MiniTitle className="lg:hidden" text="Продукты" />
<div
className={
'relative flex gax-y-4 bg-[#14161F] before:bg-[#3D425C4D] before:absolute before:w-full before:h-full before:-m-1 [&::-webkit-scrollbar]:hidden rounded-xl p-1 mb-2 w-fit max-w-full overflow-auto sm:max-lg:mt-[13px] mt-6' +
'flex gax-y-4 bg-[#3D425C4D] [&::-webkit-scrollbar]:hidden rounded-xl p-1 mb-2 w-fit max-w-full overflow-auto sm:max-lg:mt-[13px] mt-6' +
(curTab !== 2
? ' max-[912px]:[-webkit-mask-image:_linear-gradient(to_left,rgba(32,35,50,0)_0%,rgba(32,35,50,1)_20%)]'
: '')
@@ -5,7 +5,7 @@ import { useInView } from 'framer-motion';
export function TrainingsTab() {
return (
<div className="bg-[#14161F] before:bg-[#3D425C4D] before:absolute before:w-full before:h-full before:rounded-xl lg:before:-m-10 rounded-xl lg:aspect-[1520/546] sm:aspect-[720/460] lg:h-[max(534px,34vw)] md:h-[min(460px,60vw)] sm:h-[max(460px,60vw)] w-full lg:bg-[url('src/assets/products/trainings/trainings.png')] 2xl:bg-[length:70%] bg-right-bottom lg:bg-[length:55%] lg:p-10 sm:p-7 p-5 bg-no-repeat">
<div className="bg-[#3D425C4D] rounded-xl lg:aspect-[1520/546] sm:aspect-[720/460] lg:h-[max(534px,34vw)] md:h-[min(460px,60vw)] sm:h-[max(460px,60vw)] w-full lg:bg-[url('src/assets/products/trainings/trainings.png')] 2xl:bg-[length:70%] bg-right-bottom lg:bg-[length:55%] lg:p-10 sm:p-7 p-5 bg-no-repeat">
<div className="lg:max-w-[max(455px,28vw)]">
<div className="sm:max-lg:border-b border-[#3D425C] sm:max-lg:bg-[url('src/assets/products/trainings/trainings.png')] bg-no-repeat md:bg-contain bg-right-bottom sm:bg-[length:40%]">
<div className="sm:max-lg:max-w-[51vw] max-sm:border-b border-[#3D425C]">
+109 -28
View File
@@ -5,6 +5,21 @@ import { Title } from '../../ui/Title';
import { useSwipeable } from 'react-swipeable';
import { ArrowLeftIcon } from '../icons/ArrowLeftIcon';
import { ArrowRightIcon } from '../icons/ArrowRightIcon';
import { PauseIcon } from '../icons/PauseIcon';
import { PlayIcon } from '../icons/PlayIcon';
import useStateRef from 'react-usestateref';
enum Media {
video,
img,
}
interface IProject<TMedia extends Media> {
src: TMedia extends Media.img ? string : string[];
title: string;
tags: string[];
media?: TMedia;
}
export function Projects() {
return (
@@ -20,19 +35,49 @@ export function Projects() {
<Slider
projects={[
{
src: 'src/assets/projects/tank.jpg',
tags: ['Симулятор', 'VR-приложение'],
title: 'Ремонт и обслуживание двигателей спецтехники',
src: 'src/assets/projects/loader.png',
tags: ['Симулятор'],
title: 'Симулятор погрузчика',
media: Media.img,
},
{
src: 'src/assets/projects/helicopter.jpg',
tags: ['Симулятор'],
src: [
'src/assets/projects/operator.mp4',
'src/assets/projects/operator.png',
],
tags: ['Симулятор', 'VR-приложение'],
title: 'Обучение работе с системой водоочистки',
media: Media.video,
} as IProject<Media.video>,
{
src: 'src/assets/projects/plane.png',
tags: ['Симулятор', 'VR-приложение'],
title: 'L 410 NG Aircraft',
media: Media.img,
},
{
src: 'src/assets/projects/hangar.png',
tags: ['Симулятор', 'VR-приложение'],
title: 'Сборка-разборка вертолётного двигателя',
media: Media.img,
},
{
src: 'src/assets/projects/trains.png',
tags: ['Симулятор', 'VR-приложение'],
title: 'Тренажер РЖД: ЭП2Д, Иволга, ЭП20, ТЭ33А, ТЭМ2',
media: Media.img,
},
{
src: 'src/assets/projects/laboratory.png',
tags: ['Симулятор', 'VR-приложение'],
title: 'Учебная лаборатория определения жирности молока',
media: Media.img,
},
{
src: 'src/assets/projects/train_big.jpg',
tags: ['Симулятор'],
title: 'Симулятор машиниста',
media: Media.img,
},
]}
/>
@@ -40,22 +85,62 @@ export function Projects() {
);
}
function Project({
src,
title,
tags,
}: {
src: string;
title: string;
tags: string[];
}) {
function Project<T extends Media>({ src, title, tags, media }: IProject<T>) {
const [buffering, setBuffering] = useState(true);
const [playing, setPlaying, playingRef] = useStateRef(false);
const videoRef = useRef<HTMLVideoElement>(null);
useEffect(() => console.log(buffering), [buffering]);
useEffect(() => console.log(playingRef), [playingRef]);
useEffect(() => console.log(playing), [playing]);
return (
<div className="bg-[#3D425C] bg-opacity-50 rounded-2xl aspect-[624/462] lg:min-w-[39vw] sm:min-w-[67vw] min-w-[calc(100vw-32px)] lg:translate-x-[calc(15.5vw-32px)] pointer-events-none flex flex-col">
<div
className="bg-cover bg-center bg-no-repeat min-h-[340px] flex-1 rounded-2xl"
style={{ backgroundImage: `url(${src})` }}
/>
<div className="p-5 flex flex-col justify-between gap-3 h-fit">
<div className="bg-[#3D425C] bg-opacity-50 rounded-2xl aspect-[624/462] lg:min-w-[39vw] sm:min-w-[67vw] min-w-[calc(100vw-32px)] lg:translate-x-[calc(15.5vw-32px)] flex flex-col relative">
{media === Media.img ? (
<div
className="bg-cover bg-center bg-no-repeat min-h-[340px] flex-1 rounded-t-2xl"
style={{ backgroundImage: `url(${src})` }}
/>
) : (
<div
className="flex-1 rounded-t-2xl overflow-hidden relative flex justify-center items-center group bg-cover bg-center bg-no-repeat"
style={{ backgroundImage: `url(${src[1]})` }}
>
<video
src={src[0]}
ref={videoRef}
muted
loop
className="rounded-t-2xl object-cover aspect-[495/340]"
onPlaying={() => setBuffering(false)}
onWaiting={() => setBuffering(true)}
/>
<button
onClick={() => {
if (playingRef.current) {
videoRef.current?.pause();
setPlaying(false);
} else {
videoRef.current?.play();
setPlaying(true);
}
}}
className={
'absolute p-4 rounded-full border border-white group-hover:block ' +
(!buffering && playing ? 'hidden' : 'block')
}
>
{!buffering && playing ? (
<PauseIcon className="w-4 h-4" />
) : (
<PlayIcon className="w-4 h-4" />
)}
</button>
{buffering && <div className="rounded-t-2xl absolute" />}
</div>
)}
<div className="p-5 flex flex-col justify-between gap-3">
<h4 className="font-medium h4">{title}</h4>
<div className="flex gap-2">
{tags.map(tag => (
@@ -72,11 +157,7 @@ function Project({
);
}
function Slider({
projects,
}: {
projects: { src: string; title: string; tags: string[] }[];
}) {
function Slider({ projects }: { projects: IProject<Media>[] }) {
const width = useWindowWidth();
const baseOffset =
width >= 1024 ? width * 0.39 : width >= 640 ? width * 0.67 : width - 20;
@@ -90,10 +171,10 @@ function Slider({
switch (action) {
case 'prev':
setSliderOffset(prev => prev - baseOffset);
return [state[state.length - 2], ...state.slice(0, -1)];
return [state[state.length - 5], ...state.slice(0, -1)];
case 'next':
setSliderOffset(prev => prev + baseOffset);
return [...state.slice(1), state[state.length - 3]];
return [...state.slice(1), state[4]];
default:
return state;
}
@@ -174,7 +255,7 @@ function Slider({
<div className="h-1 bg-[#3D425C] w-full">
<div
className="bg-[#ffffff] h-1 duration-500"
style={{ width: `${((slide + 1) / 3) * 100}%` }}
style={{ width: `${((slide + 1) / projects.length) * 100}%` }}
/>
</div>
<button
+15
View File
@@ -0,0 +1,15 @@
export function PauseIcon({ className }: { className?: string }) {
return (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={className}
>
<path d="M10 3H7L5 5V21H8L10 19V3Z" fill="white" />
<path d="M19 3H16L14 5V21H17L19 19V3Z" fill="white" />
</svg>
);
}
+5
View File
@@ -1887,6 +1887,11 @@ react-swipeable@^7.0.1:
resolved "https://registry.yarnpkg.com/react-swipeable/-/react-swipeable-7.0.1.tgz#cd299f5986c5e4a7ee979839658c228f660e1e0c"
integrity sha512-RKB17JdQzvECfnVj9yDZsiYn3vH0eyva/ZbrCZXZR0qp66PBRhtg4F9yJcJTWYT5Adadi+x4NoG53BxKHwIYLQ==
react-usestateref@^1.0.9:
version "1.0.9"
resolved "https://registry.yarnpkg.com/react-usestateref/-/react-usestateref-1.0.9.tgz#d40bc54db116e786b6b2bb1cd20fe06e7f8187f3"
integrity sha512-t8KLsI7oje0HzfzGhxFXzuwbf1z9vhBM1ptHLUIHhYqZDKFuI5tzdhEVxSNzUkYxwF8XdpOErzHlKxvP7sTERw==
react@^18.3.1:
version "18.3.1"
resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891"