102 lines
4.0 KiB
Vue
102 lines
4.0 KiB
Vue
<script setup lang="ts">
|
|
import { AnimatePresence, motion, useMotionValue, useSpring } from "motion-v";
|
|
import VuesaxIcon from "~/components/ui/VuesaxIcon.vue";
|
|
import SectionHead from "~/components/ui/SectionHead.vue";
|
|
|
|
const industries = computed(() => useSiteStore().sections.industries);
|
|
const INDUSTRIES = computed(() => industries.value.items);
|
|
|
|
const hovered = ref<number | null>(null);
|
|
const x = useMotionValue(0);
|
|
const y = useMotionValue(0);
|
|
const sx = useSpring(x, { stiffness: 220, damping: 26, mass: 0.6 });
|
|
const sy = useSpring(y, { stiffness: 220, damping: 26, mass: 0.6 });
|
|
|
|
const onMove = (e: MouseEvent) => {
|
|
x.set(e.clientX);
|
|
y.set(e.clientY);
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<section
|
|
id="industries"
|
|
aria-labelledby="industries-title"
|
|
class="relative border-t border-line py-24 md:py-32"
|
|
@mousemove="onMove"
|
|
>
|
|
<div class="mx-auto max-w-[1480px] px-5 md:px-8">
|
|
<SectionHead heading-id="industries-title" v-bind="industries.head" />
|
|
|
|
<div class="mt-16" @mouseleave="hovered = null">
|
|
<motion.div
|
|
v-for="(ind, i) in INDUSTRIES"
|
|
:key="ind.name"
|
|
v-bind="fade(i * 0.05)"
|
|
data-hover
|
|
:class="`group relative flex cursor-pointer items-center justify-between gap-4 border-t border-line px-2 py-7 transition-colors duration-300 hover:bg-panel/40 md:px-4 md:py-9 ${
|
|
i === INDUSTRIES.length - 1 ? 'border-b' : ''
|
|
}`"
|
|
@mouseenter="hovered = i"
|
|
>
|
|
<div class="flex min-w-0 items-baseline gap-5 md:gap-8">
|
|
<span class="shrink-0 font-mono text-xs text-muted">
|
|
{{ faNum(String(i + 1).padStart(2, "0")) }}
|
|
</span>
|
|
<h3
|
|
class="truncate text-2xl font-black transition-all duration-300 group-hover:-translate-x-2 group-hover:text-accent md:text-5xl"
|
|
>
|
|
{{ ind.name }}
|
|
</h3>
|
|
<span
|
|
class="hidden shrink-0 font-mono text-[10px] tracking-[0.3em] text-muted/60 md:inline"
|
|
>
|
|
{{ ind.nameEn }}
|
|
</span>
|
|
</div>
|
|
<div class="flex shrink-0 items-center gap-4 md:gap-8">
|
|
<span v-if="ind.count" class="text-xs text-muted md:text-sm">{{ ind.count }} پروژه</span>
|
|
<span
|
|
class="flex size-10 items-center justify-center rounded-full border border-line text-muted transition-all duration-300 group-hover:-translate-x-1.5 group-hover:border-accent group-hover:text-accent md:size-12"
|
|
>
|
|
<VuesaxIcon name="arrow-left" class="size-4 md:size-5" />
|
|
</span>
|
|
</div>
|
|
</motion.div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ---- floating preview (desktop) ---- -->
|
|
<motion.div
|
|
:style="{ x: sx, y: sy }"
|
|
class="pointer-events-none fixed left-0 top-0 z-40 hidden lg:block"
|
|
>
|
|
<AnimatePresence>
|
|
<motion.div
|
|
v-if="hovered !== null"
|
|
:key="hovered"
|
|
:initial="{ opacity: 0, scale: 0.82, rotate: -3 }"
|
|
:animate="{ opacity: 1, scale: 1, rotate: 0 }"
|
|
:exit="{ opacity: 0, scale: 0.9, rotate: 2 }"
|
|
:transition="{ duration: 0.35, ease: EASE }"
|
|
class="relative -ml-40 -mt-28 h-56 w-80 overflow-hidden rounded-xl border border-accent/40 shadow-[0_25px_80px_rgba(0,0,0,0.6)]"
|
|
>
|
|
<img
|
|
:src="INDUSTRIES[hovered]!.image.url"
|
|
:alt="INDUSTRIES[hovered]!.image.alt || INDUSTRIES[hovered]!.name"
|
|
decoding="async"
|
|
class="absolute inset-0 size-full object-cover grayscale-[35%] transition-all duration-500"
|
|
/>
|
|
<div class="absolute inset-0 bg-accent/15 mix-blend-multiply" />
|
|
<div class="absolute inset-0 bg-gradient-to-t from-bg/70 to-transparent" />
|
|
<div
|
|
class="absolute bottom-3 start-3 rounded-md border border-line bg-bg/80 px-3 py-1.5 font-mono text-[10px] tracking-widest text-ink backdrop-blur"
|
|
>
|
|
{{ INDUSTRIES[hovered]!.nameEn }}
|
|
</div>
|
|
</motion.div>
|
|
</AnimatePresence>
|
|
</motion.div>
|
|
</section>
|
|
</template>
|