This commit is contained in:
2026-04-13 00:49:59 +03:00
parent a282bdbab0
commit e71f19605a
30 changed files with 1055 additions and 66 deletions

287
app/about/page.tsx Normal file
View File

@@ -0,0 +1,287 @@
'use client'
import { useRef, useState, useEffect } from 'react'
import { motion, useScroll, useTransform } from 'framer-motion'
import Image from 'next/image'
import Link from 'next/link'
const services = [
{
title: 'Mimari Hizmetler',
description: 'A.N.Tarchitecture, proaktif entegre tasarım yaklaşımıyla kapsamlı bir profesyonel mimari hizmet yelpazesi sunar:',
list: [
'Proje Geliştirme: Saha analizi, potansiyel çalışma, sosyal kabul edilebilirlik stratejileri ve planlama',
'Plan ve Şartnamelerin Tasarımı ve Üretimi.',
'Proje Modelleme Yönetimi: Entegre BIM (Yapı Bilgi Modellemesi) tasarımı',
'Düzenleyici Stratejiler: Yönetmelikler, saha çalışması, enerji ve güneş ışığı analizi',
'Kentsel Tasarım: Master planlama, peyzaj tasarımı ve inşaat mühendisliği işleri',
'3D Görselleştirme: 3D perspektifler, modelleme ve animasyonlar',
'İnşaat Hizmeti ve Yönetimi: İnşaat yönetimi ve saha denetimi'
],
button: 'PROJELERİ GÖRÜNTÜLE',
image: 'https://images.unsplash.com/photo-1486406146926-c627a92ad1ab?q=80&w=2070&auto=format&fit=crop'
},
{
title: 'İç Mimari Hizmetleri',
description: 'A.N.Tarchitecture, sürdürülebilirlik, zarafet ve işlevselliği şirketinizin değerleriyle uyumlu akıllı düzenlerle birleştirerek, imajını ve işe alım kapasitesini artıran iç mimari konusunda derin bir uzmanlık sunar:',
list: [
'Aktif Müşteri Dinleme: Alan programlama ve planlama, bütçe analizi',
'Konsept Geliştirme: Plan ve şartnamelerin üretimi, malzeme seçimi, entegre mobilya ve özel objelerin kavramsallaştırılması',
'Entegre Tabela: Plan ve şartnamelerin modellenmesi ve üretimi',
'3D Görselleştirme: 3D perspektifler, modelleme ve animasyonlar'
],
button: 'TÜM PROJELER',
image: 'https://images.unsplash.com/photo-1600210492486-724fe5c67fb0?q=80&w=2070&auto=format&fit=crop'
},
{
title: 'Planlama ve Uzmanlık Hizmetleri',
description: 'A.N.Tarchitecture, bina planlama, programlama ve denetleme konularında, müşteri ihtiyaçlarını ve gereksinimlerini net bir şekilde tanımlamak için titiz ve sistematik yöntemler kullanarak çeşitli uzmanlıklar sunar.',
list: [
'Fonksiyonel ve Teknik Programların (FTP) Geliştirilmesi: Müdahale stratejileri, organizasyonun genel özellikleri, işlevsellik ve alanlar',
'Fizibilite Çalışması ve Ön Konseptler: Teknik ve düzenleyici koşulların ve kısıtlamaların analizi',
'Teknik Uzmanlık: Su yalıtım sorunlarının ve erken yaşlanmanın tespiti, yaşam döngüsü analizi'
],
button: null,
image: 'https://images.unsplash.com/photo-1518780664697-55e3ad937233?q=80&w=2070&auto=format&fit=crop'
}
]
const projectPhases = [
{ num: '00', title: 'ÖN KAVRAMSAL ÇALIŞMALAR', desc: 'Bir binanın kapsamını belirlemek için ihtiyaçlarını ve işlevlerini anlamaya ve belgelemeye yardımcı olan hazırlık aşaması.' },
{ num: '01', title: 'TASARIM AŞAMASI', desc: 'Bu aşama saha incelemesi, program incelemesi, ilk eskiz tasarımı ve ön bütçe analizini içerir.' },
{ num: '02', title: 'TASARIM GELİŞTİRME', desc: 'Eskizler onaylandıktan sonra, gerekli ön planları, kesitleri ve görünüşleri oluşturarak projeyi daha detaylı geliştiriyoruz.' },
{ num: '03', title: 'İNŞAAT BELGELERİ', desc: 'Bu inşaat için gerekli olan teknik planların gerçek geliştirilme aşamasıdır.' },
{ num: '04', title: 'İHALE', desc: 'Genel yüklenici tekliflerinin analizinde ve inşaat sözleşmesinin imzalanmasında müşteriye yardımcı oluyor ve danışmanlık yapıyoruz.' },
{ num: '05', title: 'SAHA DENETİMİ', desc: 'Projenin planlara sadık kalarak yürütülmesini sağlıyoruz. İnşaat sahasındaki öngörülemeyen sorunlara tasarımı uyarlamak için çözümler sunuyoruz.' }
]
export default function AboutPage() {
const targetRef = useRef<HTMLDivElement>(null)
const { scrollY } = useScroll()
const [isAtBottom, setIsAtBottom] = useState(false)
useEffect(() => {
const handleScroll = () => {
const windowHeight = window.innerHeight
const documentHeight = document.documentElement.scrollHeight
const scrollPosition = window.scrollY + windowHeight
setIsAtBottom(scrollPosition > documentHeight - 150)
}
window.addEventListener('scroll', handleScroll)
return () => window.removeEventListener('scroll', handleScroll)
}, [])
const cargoFontSize = useTransform(scrollY, [0, 300], ["10vw", "4vw"])
const archFontSize = useTransform(scrollY, [0, 300], ["8vw", "3vw"])
const bottomPadding = useTransform(scrollY, [0, 300], ["2.5rem", "1rem"])
const { scrollYProgress } = useScroll({
target: targetRef,
offset: ["start start", "end end"]
})
return (
<main className="relative bg-[#0a0a0a] text-white selection:bg-white selection:text-black" style={{ overflowX: 'clip' }}>
{/* SECTION 1: Intro */}
<section className="relative min-h-screen flex items-center justify-center pt-40 pb-32 px-6 md:px-16">
<div className="max-w-[1400px] w-full grid grid-cols-1 lg:grid-cols-[1fr_400px] gap-12 lg:gap-24 items-start">
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 1.2, ease: [0.22, 1, 0.36, 1] }}
>
<p className="text-[24px] md:text-[36px] lg:text-[42px] leading-[1.2] font-normal tracking-tight text-white/95">
Mimariyi, teknik disiplin ile yaratıcı vizyonun kusursuz dengesi olarak tanımlıyorum. Tasarım sürecimde hem hem de dış mekân projelerinde bütüncül bir yaklaşımı benimsiyor; projelerimi sadece birer yapı olarak değil, çevresiyle nefes alan yaşam alanları olarak tasarlıyorum.
Projelerimin teknik omurgasını AutoCAD üzerinde detaylı plan ve kesit çalışmalarıyla oluştururken, 3D modelleme ve görselleştirme süreçlerinde sunduğum gerçekçi sunumlarla tasarıma derinlik kazandırıyorum. Konsept tasarımından uygulama detaylarına kadar her aşamada, mekânın işlevselliğini estetik bir değerle harmanlamayı amaçlıyorum. </p>
</motion.div>
<motion.div
initial={{ opacity: 0, x: 20 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
transition={{ duration: 1.2, delay: 0.3, ease: [0.22, 1, 0.36, 1] }}
className="flex flex-col space-y-12 lg:space-y-16 mt-4"
>
<p className="text-[13px] md:text-[14px] leading-relaxed text-zinc-400 font-medium">
2006 yılında Toronto'da CGBWstudio adıyla Charles-Bernard Gagnon tarafından kurulan ve 2008'de Quebec City'ye taşınan firma, felsefesi ve sürecin her aşamasında yüksek kaliteli profesyonel hizmet sunma taahhüdü sayesinde bölge pazarında öne çıkmıştır. Bu yaklaşım, firmanın yenilikçi projelerden oluşan bir portföy oluşturmasını sağlamıştır. 2013 yılında ekip, daha anlamlı bir isim olan A.N.Tarchitecture adını seçmiştir.
</p>
<div className="flex flex-col space-y-6">
<div className="relative w-full aspect-[3/4] rounded-[1px] overflow-hidden grayscale border border-white/10 group max-w-[300px] lg:max-w-none">
<Image
src="https://images.unsplash.com/photo-1595392756136-ba72c36bcfe0?q=80&w=1974&auto=format&fit=crop"
alt="Ayça Nur Turhan"
fill
sizes="(max-width: 1024px) 300px, 400px"
className="object-cover transition-transform duration-1000 group-hover:scale-105"
/>
</div>
<div className="text-[10px] md:text-[11px] font-bold uppercase tracking-[0.2em] leading-[1.5] text-white/60">
AYÇA NUR TURHAN KURUCU <br />MİMAR, OAQ
</div>
</div>
</motion.div>
</div>
</section>
{/* SECTION 2: Services Sequential Scroll Animation */}
<section ref={targetRef} className="relative md:h-[180vh] bg-[#0a0a0a]">
<div className="hidden md:block sticky top-0 h-screen w-full p-4">
<div className="grid grid-cols-3 gap-4 w-full h-full">
{services.map((service, index) => (
<ServiceCard
key={index}
index={index}
service={service}
scrollYProgress={scrollYProgress}
/>
))}
</div>
</div>
{/* Mobile Vertical Stack */}
<div className="md:hidden flex flex-col space-y-1 px-4 pb-20">
{services.map((service, index) => (
<ServiceCardMobile key={index} service={service} />
))}
</div>
</section>
{/* SECTION 3: Project Phases */}
<section className="relative min-h-screen bg-[#0a0a0a] pt-24 md:pt-40 pb-40 md:pb-60 px-6 md:px-16 border-t border-white/10">
<div className="max-w-[1400px] mx-auto grid grid-cols-1 lg:grid-cols-[1fr_2fr] gap-12 md:gap-20">
<div className="flex flex-col items-start space-y-6 md:space-y-8">
<h2 className="text-[36px] md:text-[52px] font-normal tracking-tight">Proje Aşamaları</h2>
<Link
href="/contact"
className="bg-white text-black px-6 py-2.5 text-[10px] font-extrabold tracking-[0.2em] uppercase hover:bg-zinc-200 transition-colors rounded-[1px]"
>
PROJENİZİ BAŞLATIN
</Link>
</div>
<div className="flex flex-col">
{projectPhases.map((phase, idx) => (
<div
key={idx}
className="grid grid-cols-1 sm:grid-cols-[60px_1.2fr_2fr] gap-4 md:gap-8 py-8 md:py-10 border-t border-white/20 first:border-t-0 px-4 md:px-6 -mx-4 md:-mx-6 hover:bg-white hover:text-black transition-all duration-300 group cursor-default"
>
<div className="text-[12px] font-bold text-white/40 group-hover:text-black/40">{phase.num}</div>
<div className="text-[12px] font-extrabold tracking-widest uppercase">{phase.title}</div>
<div className="text-[13px] md:text-[14px] leading-relaxed text-zinc-400 group-hover:text-black md:pr-10">{phase.desc}</div>
</div>
))}
<div className="border-t border-white/20 w-full" />
</div>
</div>
</section>
{/* BOTTOM BRANDING */}
<motion.div
animate={{ opacity: isAtBottom ? 0 : 1 }}
transition={{ duration: 0.3 }}
className="fixed bottom-0 left-0 w-full pointer-events-none z-50 overflow-hidden pt-10"
>
<motion.div
style={{ padding: bottomPadding }}
className="flex justify-between items-end"
>
<motion.h1 style={{ fontSize: cargoFontSize }} className="font-bebas leading-[0.8] text-black mix-blend-difference tracking-tighter">A.N.T</motion.h1>
<motion.h1 style={{ fontSize: archFontSize }} className="font-bebas leading-[0.8] text-black mix-blend-difference tracking-tighter">ARCHITECTURE</motion.h1>
</motion.div>
</motion.div>
</main>
)
}
function ServiceCard({ service, index, scrollYProgress }: { service: any, index: number, scrollYProgress: any }) {
// Cards start visible but at different Y positions (staggered cascade)
// Card 0: y=0 (top), Card 1: y=200px lower, Card 2: y=400px lower
// As user scrolls, each card slides UP to y=0 to align with Card 0
const initialOffsets = [0, 200, 400]
// Sequential timing: Card 1 arrives first, Card 2 arrives after
const ranges: [number, number][] = [
[0, 0.01], // Card 0: always in place
[0, 0.50], // Card 1: slides up during first half
[0.15, 0.85], // Card 2: starts after Card 1 is moving, arrives by 85%
]
const y = useTransform(
scrollYProgress,
ranges[index],
[initialOffsets[index], 0]
)
return (
<motion.div
style={{ y }}
className="relative w-full h-full bg-white text-black flex flex-col rounded-2xl overflow-hidden"
>
{/* Content Area */}
<div className="flex-1 p-10 lg:p-14 overflow-hidden">
<h2 className="text-[32px] lg:text-[40px] mb-10 font-normal tracking-tight leading-[1.1]">
{service.title}
</h2>
<p className="text-[15px] mb-10 text-black/80 leading-relaxed font-medium">
{service.description}
</p>
<ul className="space-y-4 mb-12">
{service.list.map((item: string, i: number) => (
<li key={i} className="text-[14px] text-black/70 flex items-start border-b border-black/5 pb-2 last:border-0 leading-snug">
<span className="mr-3 text-black/30"></span>
<span>{item}</span>
</li>
))}
</ul>
{service.button && (
<Link href="/projects" className="inline-block border border-black text-[10px] font-bold tracking-[0.2em] px-8 py-3.5 uppercase hover:bg-black hover:text-white transition-all duration-300">
{service.button}
</Link>
)}
</div>
{/* Fixed Image at Bottom */}
<div className="h-[35%] lg:h-[40%] relative w-full overflow-hidden grayscale group hover:grayscale-0 transition-all duration-700">
<Image
src={service.image}
alt={service.title}
fill
sizes="33vw"
className="object-cover scale-110 group-hover:scale-100 transition-transform duration-1000"
/>
</div>
</motion.div>
)
}
function ServiceCardMobile({ service }: { service: any }) {
return (
<div className="w-full bg-white text-black flex flex-col overflow-hidden rounded-[1px] mb-1">
<div className="p-8">
<h2 className="text-[26px] mb-6 font-normal tracking-tight leading-tight">{service.title}</h2>
<p className="text-[14px] mb-6 text-black/80 leading-relaxed">{service.description}</p>
<ul className="space-y-3 mb-8">
{service.list.map((item: string, i: number) => (
<li key={i} className="text-[13px] text-black/70 flex items-start border-b border-black/5 pb-2 last:border-0">
<span className="mr-2 text-black/30"></span>
<span>{item}</span>
</li>
))}
</ul>
{service.button && (
<Link href="/projects" className="inline-block border border-black text-[9px] font-bold tracking-widest px-6 py-3 uppercase">
{service.button}
</Link>
)}
</div>
<div className="h-[250px] relative w-full grayscale">
<Image src={service.image} alt={service.title} fill sizes="100vw" className="object-cover" />
</div>
</div>
)
}

68
app/contact/page.tsx Normal file
View File

@@ -0,0 +1,68 @@
'use client'
import Link from 'next/link'
const socialLinks = [
{ name: 'FACEBOOK', href: 'https://facebook.com' },
{ name: 'INSTAGRAM', href: 'https://instagram.com' },
{ name: 'LINKEDIN', href: 'https://linkedin.com' },
{ name: '***', href: '**' },
]
const offices = [
{
num: '01',
name: 'Fethiye | Merkez Ofis',
address: 'Karagözler, 48300 Fethiye/Muğla',
phone: '0553 093 72 25'
}
]
export default function ContactPage() {
return (
<main className="relative min-h-screen bg-[#111111] text-white flex flex-col selection:bg-white selection:text-black">
<div className="flex-1 flex flex-col justify-center items-end px-10 pt-40">
<div className="flex flex-wrap justify-end gap-4">
{socialLinks.map((link) => (
<Link
key={link.name}
href={link.href}
className="border border-white/20 px-4 py-2 text-[10px] font-extrabold tracking-widest hover:bg-white hover:text-black transition-all duration-300 rounded-[1px]"
>
{link.name}
</Link>
))}
</div>
</div>
<div className="w-full px-10 pb-40">
<div className="border-t border-white/20">
{offices.map((office) => (
<div
key={office.num}
className="grid grid-cols-1 md:grid-cols-[60px_1fr_1fr_1fr] gap-8 py-10 border-b border-white/20 items-start hover:bg-white hover:text-black px-6 -mx-6 transition-all duration-300 group cursor-default"
>
<div className="text-[12px] font-bold text-white/40 group-hover:text-black/40">
{office.num}
</div>
<div className="text-[20px] md:text-[24px] font-normal tracking-tight">
{office.name}
</div>
<div className="text-[12px] leading-relaxed text-zinc-400 group-hover:text-black whitespace-pre-line">
{office.address}
</div>
<div className="text-[12px] font-bold text-right group-hover:text-black">
{office.phone}
</div>
</div>
))}
</div>
</div>
</main>
)
}

View File

@@ -10,6 +10,8 @@
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
--font-bebas: var(--font-bebas-neue);
--font-swiss: var(--font-oswald);
}
@media (prefers-color-scheme: dark) {
@@ -22,5 +24,15 @@
body {
background: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
font-family: var(--font-oswald), Arial, Helvetica, sans-serif;
}
@layer utilities {
.no-scrollbar::-webkit-scrollbar {
display: none;
}
.no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
}

View File

@@ -1,6 +1,7 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import { Geist, Geist_Mono, Bebas_Neue, Oswald } from "next/font/google";
import "./globals.css";
import LayoutContent from "@/components/LayoutContent";
const geistSans = Geist({
variable: "--font-geist-sans",
@@ -12,9 +13,20 @@ const geistMono = Geist_Mono({
subsets: ["latin"],
});
const bebasNeue = Bebas_Neue({
variable: "--font-bebas-neue",
weight: "400",
subsets: ["latin"],
});
const oswald = Oswald({
variable: "--font-oswald",
subsets: ["latin", "latin-ext"],
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
title: "A.N.T ARCHITECTURE",
description: "Ayça Nur Turhan - Mimarlık ve Tasarım",
};
export default function RootLayout({
@@ -24,10 +36,12 @@ export default function RootLayout({
}>) {
return (
<html
lang="en"
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
lang="tr"
className={`${geistSans.variable} ${geistMono.variable} ${bebasNeue.variable} ${oswald.variable} h-full antialiased`}
>
<body className="min-h-full flex flex-col">{children}</body>
<body className="min-h-full flex flex-col bg-white">
<LayoutContent>{children}</LayoutContent>
</body>
</html>
);
}

View File

@@ -1,65 +1,108 @@
import Image from "next/image";
'use client'
import { useState, useEffect } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import ProjectSlider from '@/components/ProjectSlider'
import { projects } from '@/data/projects'
export default function Home() {
const [isRevealed, setIsRevealed] = useState(false)
const [isAtCorners, setIsAtCorners] = useState(false)
useEffect(() => {
const reveal = setTimeout(() => setIsRevealed(true), 500)
const move = setTimeout(() => setIsAtCorners(true), 2500)
return () => {
clearTimeout(reveal)
clearTimeout(move)
}
}, [])
const transition = { duration: 1.8, ease: [0.76, 0, 0.24, 1] }
return (
<div className="flex flex-col flex-1 items-center justify-center bg-zinc-50 font-sans dark:bg-black">
<main className="flex flex-1 w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
<Image
className="dark:invert"
src="/next.svg"
alt="Next.js logo"
width={100}
height={20}
priority
/>
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
To get started, edit the page.tsx file.
</h1>
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
Looking for a starting point or more instructions? Head over to{" "}
<a
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
>
Templates
</a>{" "}
or the{" "}
<a
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
>
Learning
</a>{" "}
center.
</p>
</div>
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
<a
className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
<main className="relative h-screen w-full bg-white overflow-hidden">
<div className="absolute inset-0 z-50 pointer-events-none">
<div className="relative w-full h-full">
{/* Main Container for the dynamic text */}
<motion.div
animate={{
top: isAtCorners ? "100%" : "50%",
left: isAtCorners ? "0%" : "50%",
x: isAtCorners ? "0%" : "-50%",
y: isAtCorners ? "-100%" : "-100%",
}}
transition={transition}
className="absolute overflow-hidden"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={16}
height={16}
/>
Deploy Now
</a>
<a
className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
{/* The Text Reveal & Cross-fade */}
<motion.div
initial={{ y: "100%" }}
animate={{ y: isRevealed ? "0%" : "100%" }}
transition={transition}
className="relative"
>
{/* AYÇA NUR TURHAN */}
<motion.h1
animate={{ opacity: isAtCorners ? 0 : 1 }}
transition={{ duration: 0.8, ease: "easeInOut" }}
className="text-[10vw] md:text-[8vw] font-bebas leading-[0.75] text-black tracking-tighter whitespace-nowrap"
>
AYÇA NUR TURHAN
</motion.h1>
{/* A.N.T (Cross-fading in) */}
<motion.h1
initial={{ opacity: 0 }}
animate={{ opacity: isAtCorners ? 1 : 0 }}
transition={{ duration: 0.8, ease: "easeInOut" }}
className="absolute inset-0 text-[10vw] md:text-[8vw] font-bebas leading-[0.75] text-black tracking-tighter whitespace-nowrap"
>
A.N.T
</motion.h1>
</motion.div>
</motion.div>
{/* ARCHITECTURE (Remains constant) */}
<motion.div
animate={{
top: isAtCorners ? "100%" : "50%",
left: isAtCorners ? "100%" : "50%",
x: isAtCorners ? "-100%" : "-50%",
y: isAtCorners ? "-100%" : "0%",
}}
transition={transition}
className="absolute overflow-hidden"
>
Documentation
</a>
<motion.h1
initial={{ y: "100%" }}
animate={{ y: isRevealed ? "0%" : "100%" }}
transition={{ ...transition, delay: 0.1 }}
className="text-[10vw] md:text-[8vw] font-bebas leading-[0.75] text-black tracking-tighter whitespace-nowrap"
>
ARCHITECTURE
</motion.h1>
</motion.div>
</div>
</main>
</div>
);
</div>
<AnimatePresence>
{isAtCorners && (
<motion.div
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ delay: 0.6, duration: 1.2, ease: "easeOut" }}
className="h-full flex flex-col justify-center z-10"
>
<ProjectSlider projects={projects} />
</motion.div>
)}
</AnimatePresence>
</main>
)
}

87
app/projects/page.tsx Normal file
View File

@@ -0,0 +1,87 @@
'use client'
import { motion, useScroll, useTransform } from 'framer-motion'
import Image from 'next/image'
import { projects } from '@/data/projects'
import { useState, useEffect } from 'react'
export default function ProjectsPage() {
const { scrollY } = useScroll()
const [isAtBottom, setIsAtBottom] = useState(false)
useEffect(() => {
const handleScroll = () => {
const windowHeight = window.innerHeight
const documentHeight = document.documentElement.scrollHeight
const scrollPosition = window.scrollY + windowHeight
setIsAtBottom(scrollPosition > documentHeight - 100)
}
window.addEventListener('scroll', handleScroll)
return () => window.removeEventListener('scroll', handleScroll)
}, [])
const cargoFontSize = useTransform(scrollY, [0, 300], ["10vw", "4vw"])
const archFontSize = useTransform(scrollY, [0, 300], ["8vw", "3vw"])
const bottomPadding = useTransform(scrollY, [0, 300], ["2.5rem", "1rem"])
return (
<main className="relative min-h-screen bg-white pt-32 pb-60 px-6 md:px-10">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 md:gap-8">
{projects.map((project, idx) => (
<motion.div
key={project.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: idx * 0.05 }}
className="group cursor-pointer"
>
<div className="relative aspect-[4/3] overflow-hidden rounded-[1px] bg-zinc-100">
<Image
src={project.image}
alt={project.title}
fill
className="object-cover grayscale group-hover:grayscale-0 transition-all duration-700 ease-in-out scale-105 group-hover:scale-100"
/>
</div>
<div className="mt-4 flex flex-col space-y-1 opacity-0 group-hover:opacity-100 transition-opacity duration-300">
<span className="text-[10px] font-bold text-black/60 uppercase tracking-wider">
{project.year} {project.location}
</span>
<div className="flex justify-between items-center">
<span className="text-[10px] font-bold text-black uppercase tracking-widest">
{project.title}
</span>
<span className="text-sm"></span>
</div>
</div>
</motion.div>
))}
</div>
<motion.div
animate={{ opacity: isAtBottom ? 0 : 1 }}
transition={{ duration: 0.3 }}
className="fixed bottom-0 left-0 w-full pointer-events-none z-50 overflow-hidden pt-10"
>
<motion.div
style={{ padding: bottomPadding }}
className="flex justify-between items-end"
>
<motion.h1
style={{ fontSize: cargoFontSize }}
className="font-bebas leading-[0.8] text-black tracking-tighter"
>
A.N.T
</motion.h1>
<motion.h1
style={{ fontSize: archFontSize }}
className="font-bebas leading-[0.8] text-black tracking-tighter"
>
ARCHITECTURE
</motion.h1>
</motion.div>
</motion.div>
</main>
)
}