126 lines
4.9 KiB
TypeScript
126 lines
4.9 KiB
TypeScript
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
|
import { MoveHorizontalIcon } from 'lucide-react'
|
|
import { Reveal } from '../Reveal'
|
|
import { SectionHeading } from '../SectionHeading'
|
|
import { useSite } from '../../api'
|
|
import { SingletonPill } from '../../admin/Editable'
|
|
|
|
export function BeforeAfter() {
|
|
const { before_after } = useSite()
|
|
const containerRef = useRef<HTMLDivElement>(null)
|
|
const [position, setPosition] = useState(52)
|
|
const dragging = useRef(false)
|
|
|
|
const setFromClientX = useCallback((clientX: number) => {
|
|
const el = containerRef.current
|
|
if (!el) return
|
|
const rect = el.getBoundingClientRect()
|
|
const ratio = ((clientX - rect.left) / rect.width) * 100
|
|
setPosition(Math.min(98, Math.max(2, ratio)))
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
const move = (e: MouseEvent | TouchEvent) => {
|
|
if (!dragging.current) return
|
|
const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX
|
|
setFromClientX(clientX)
|
|
}
|
|
const stop = () => {
|
|
dragging.current = false
|
|
}
|
|
window.addEventListener('mousemove', move)
|
|
window.addEventListener('touchmove', move)
|
|
window.addEventListener('mouseup', stop)
|
|
window.addEventListener('touchend', stop)
|
|
return () => {
|
|
window.removeEventListener('mousemove', move)
|
|
window.removeEventListener('touchmove', move)
|
|
window.removeEventListener('mouseup', stop)
|
|
window.removeEventListener('touchend', stop)
|
|
}
|
|
}, [setFromClientX])
|
|
|
|
const onKeyDown = (e: React.KeyboardEvent) => {
|
|
if (e.key === 'ArrowLeft') setPosition((p) => Math.max(2, p - 4))
|
|
if (e.key === 'ArrowRight') setPosition((p) => Math.min(98, p + 4))
|
|
}
|
|
|
|
return (
|
|
<section className="bg-char py-24 lg:py-32" aria-labelledby="ba-title">
|
|
<div className="mx-auto max-w-site px-6 lg:px-10">
|
|
|
|
<SectionHeading
|
|
title={before_after.title}
|
|
lead={before_after.lead}
|
|
align="center"
|
|
/>
|
|
|
|
<div className="flex justify-start">
|
|
<SingletonPill resource="before_after" label="ویرایش تصاویر و برچسبهای قبل/بعد" />
|
|
</div>
|
|
|
|
<Reveal delay={0.1}>
|
|
<div
|
|
ref={containerRef}
|
|
className="relative mx-auto mt-14 aspect-[4/3] w-full max-w-4xl select-none overflow-hidden rounded-xl border border-ivory/10 shadow-2xl md:aspect-[16/10]"
|
|
onMouseDown={(e) => {
|
|
dragging.current = true
|
|
setFromClientX(e.clientX)
|
|
}}
|
|
onTouchStart={(e) => {
|
|
dragging.current = true
|
|
setFromClientX(e.touches[0].clientX)
|
|
}}
|
|
>
|
|
<img
|
|
src={before_after.before}
|
|
alt={before_after.before_label}
|
|
className="absolute inset-0 h-full w-full object-cover object-center"
|
|
draggable={false}
|
|
/>
|
|
|
|
<div className="absolute inset-0 overflow-hidden" style={{ clipPath: `inset(0 0 0 ${position}%)` }} aria-hidden="true">
|
|
<img
|
|
src={before_after.after}
|
|
alt=""
|
|
className="absolute inset-0 h-full w-full object-cover object-center"
|
|
draggable={false}
|
|
/>
|
|
</div>
|
|
|
|
<span className="pointer-events-none absolute left-6 top-6 z-30 rounded-md bg-ink/85 px-4 py-2 text-xs font-light text-ivory backdrop-blur-md border border-ivory/10 shadow-lg">
|
|
{before_after.before_label}
|
|
</span>
|
|
<span className="pointer-events-none absolute right-6 top-6 z-30 rounded-md bg-ink/85 px-4 py-2 text-xs font-light text-copper backdrop-blur-md border border-copper/30 shadow-lg">
|
|
{before_after.after_label}
|
|
</span>
|
|
|
|
<div className="pointer-events-none absolute inset-y-0 z-30" style={{ left: `${position}%` }}>
|
|
<div className="h-full w-[2px] bg-copper shadow-[0_0_12px_rgba(213,135,76,0.9)]" />
|
|
</div>
|
|
|
|
<button
|
|
type="button"
|
|
role="slider"
|
|
aria-label="مقایسه کانسپت و پروژه ساختهشده"
|
|
aria-valuemin={0}
|
|
aria-valuemax={100}
|
|
aria-valuenow={Math.round(position)}
|
|
onKeyDown={onKeyDown}
|
|
onMouseDown={() => {
|
|
dragging.current = true
|
|
}}
|
|
onTouchStart={() => {
|
|
dragging.current = true
|
|
}}
|
|
className="absolute top-1/2 z-40 flex h-14 w-14 -translate-x-1/2 -translate-y-1/2 cursor-ew-resize items-center justify-center rounded-full bg-copper text-char shadow-2xl transition-transform duration-200 ease-lux hover:scale-110 focus:outline-none focus-visible:ring-2 focus-visible:ring-ivory"
|
|
style={{ left: `${position}%` }}
|
|
>
|
|
<MoveHorizontalIcon className="h-5 w-5" />
|
|
</button>
|
|
</div>
|
|
</Reveal>
|
|
</div>
|
|
</section>
|
|
)
|
|
} |