This commit is contained in:
Mackie 2026-06-07 08:19:42 +08:00
parent da0d7ba0d2
commit f09bc39a12
11 changed files with 92 additions and 85 deletions

View file

@ -13,8 +13,8 @@ const geistMono = Geist_Mono({
}); });
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Create Next App", title: "ByMackie 3D Animation",
description: "Generated by create next app", description: "showcase bymackie",
}; };
export default function RootLayout({ export default function RootLayout({

View file

@ -1,65 +1,18 @@
import Image from "next/image"; 'use client';
import dynamic from 'next/dynamic';
const ShowcaseCanvas = dynamic(
() => import('@/components/three/ShowcaseCanvas'),
{ ssr: false }
);
export default function Home() { export default function Home() {
return ( return (
<div className="flex flex-col flex-1 items-center justify-center bg-zinc-50 font-sans dark:bg-black"> <div className="flex flex-col items-center justify-center min-h-screen bg-zinc-50 dark:bg-black p-8">
<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"> <main className="w-full max-w-3xl flex flex-col gap-8">
<Image <ShowcaseCanvas />
className="dark:invert" <h1 className="text-3xl font-semibold text-black dark:text-zinc-50">3D Showcase</h1>
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"
>
<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"
>
Documentation
</a>
</div>
</main> </main>
</div> </div>
); );
} }

View file

@ -1,5 +1,4 @@
'use client'; 'use client';
import { useGLTF } from '@react-three/drei'; import { useGLTF } from '@react-three/drei';
export function Model({ url }: { url: string }) { export function Model({ url }: { url: string }) {

View file

@ -1,36 +1,51 @@
'use client'; 'use client';
import { Canvas } from '@react-three/fiber'; import { Canvas } from '@react-three/fiber';
import { OrbitControls, Stage } from '@react-three/drei'; import { OrbitControls, Stage, ContactShadows, Environment } from '@react-three/drei';
import { Suspense } from 'react'; import { Suspense } from 'react';
import { EffectComposer, Bloom, Noise, Vignette } from '@react-three/postprocessing';
import { useAssetLoader } from '@/hooks/useAssetLoader';
import { Model } from './Model'; import { Model } from './Model';
import { motion } from 'framer-motion';
export default function ShowcaseCanvas() { export default function ShowcaseCanvas() {
const { asset, loading, error } = useAssetLoader('/assets.json');
if (loading || error) return null;
return ( return (
<div className="h-[500px] w-full border border-zinc-800 rounded-xl overflow-hidden bg-black"> // Added 'bg-zinc-50' and a subtle 'bg-grid' style pattern
<Canvas dpr={[1, 2]} shadows camera={{ fov: 45 }}> // Replace your div className with this:
<color attach="background" args={['#050505']} /> <div className="relative h-[650px] w-full rounded-2xl overflow-hidden shadow-2xl bg-stone-100 border border-stone-200 bg-[linear-gradient(to_right,#d6d3d1_1px,transparent_1px),linear-gradient(to_bottom,#d6d3d1_1px,transparent_1px)] bg-[size:40px_40px]">
{/* 3D Canvas */}
<Canvas shadows camera={{ position: [0, 0, 4], fov: 45 }}>
<Suspense fallback={null}> <Suspense fallback={null}>
<Stage intensity={0.5} environment="city"> <Environment preset="city" />
{asset && <Model url={asset.modelUrl} />} <Stage intensity={0.5} adjustCamera={false}>
<Model url="/models/shoe.glb" />
</Stage> </Stage>
<ContactShadows opacity={0.4} scale={10} blur={2} far={4.5} />
</Suspense> </Suspense>
<OrbitControls
<EffectComposer disableNormalPass> makeDefault
<Bloom luminanceThreshold={1} mipmapBlur intensity={1.5} /> enablePan={false}
<Noise opacity={0.05} /> minPolarAngle={Math.PI / 4}
<Vignette eskil={false} offset={0.1} darkness={1.1} /> maxPolarAngle={Math.PI / 1.75}
</EffectComposer> />
<OrbitControls makeDefault />
</Canvas> </Canvas>
{/* UI Overlay */}
<motion.div
initial={{ opacity: 0, x: 50 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.8, ease: "easeOut" }}
className="absolute top-10 right-10 z-10 w-72 bg-black/80 backdrop-blur-xl p-6 rounded-2xl text-white shadow-2xl"
>
<h2 className="text-2xl font-bold tracking-tight">AIR JORDAN 1</h2>
<p className="text-zinc-300 mt-2 text-sm leading-relaxed">
Engineered for performance and style. Experience the pinnacle of sneaker culture.
</p>
<div className="flex items-center justify-between mt-6">
<span className="text-lg font-bold">$170.00</span>
<button className="bg-white text-black px-5 py-2 text-sm rounded-full font-bold hover:bg-zinc-200 transition-colors duration-300">
BUY NOW
</button>
</div>
</motion.div>
</div> </div>
); );
} }

View file

@ -1,7 +1,8 @@
import type { NextConfig } from "next"; import type { NextConfig } from "next";
const nextConfig: NextConfig = { const nextConfig: NextConfig = {
/* config options here */ // Add this to allow network access
allowedDevOrigins: ['192.168.50.47'],
}; };
export default nextConfig; export default nextConfig;

View file

@ -13,6 +13,7 @@
"@react-three/fiber": "^9.6.1", "@react-three/fiber": "^9.6.1",
"@react-three/postprocessing": "^3.0.4", "@react-three/postprocessing": "^3.0.4",
"@types/three": "^0.184.1", "@types/three": "^0.184.1",
"framer-motion": "^12.40.0",
"next": "16.2.7", "next": "16.2.7",
"react": "19.2.4", "react": "19.2.4",
"react-dom": "19.2.4", "react-dom": "19.2.4",

38
pnpm-lock.yaml generated
View file

@ -20,6 +20,9 @@ importers:
'@types/three': '@types/three':
specifier: ^0.184.1 specifier: ^0.184.1
version: 0.184.1 version: 0.184.1
framer-motion:
specifier: ^12.40.0
version: 12.40.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
next: next:
specifier: 16.2.7 specifier: 16.2.7
version: 16.2.7(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) version: 16.2.7(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@ -1290,6 +1293,20 @@ packages:
resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
framer-motion@12.40.0:
resolution: {integrity: sha512-uaBd3qC1v3KQqBEjwTUd183K6PbS+j0yR9w9VmEOLWA/tnUcSn8Xa3uck7t4dgpDoUss8xQTcj8W2L07lrnLFg==}
peerDependencies:
'@emotion/is-prop-valid': '*'
react: ^18.0.0 || ^19.0.0
react-dom: ^18.0.0 || ^19.0.0
peerDependenciesMeta:
'@emotion/is-prop-valid':
optional: true
react:
optional: true
react-dom:
optional: true
function-bind@1.1.2: function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
@ -1724,6 +1741,12 @@ packages:
minimist@1.2.8: minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
motion-dom@12.40.0:
resolution: {integrity: sha512-HxU3ZaBwNPVQUBQf1xxgq+7JrPNZvjLVxgbpEZL7RrWJnsxOf0/OM+yrHG9ogLQ31Do/r57Oz2gQWPK+6q62mg==}
motion-utils@12.39.0:
resolution: {integrity: sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==}
ms@2.1.3: ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
@ -3609,6 +3632,15 @@ snapshots:
dependencies: dependencies:
is-callable: 1.2.7 is-callable: 1.2.7
framer-motion@12.40.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
dependencies:
motion-dom: 12.40.0
motion-utils: 12.39.0
tslib: 2.8.1
optionalDependencies:
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
function-bind@1.1.2: {} function-bind@1.1.2: {}
function.prototype.name@1.1.8: function.prototype.name@1.1.8:
@ -4012,6 +4044,12 @@ snapshots:
minimist@1.2.8: {} minimist@1.2.8: {}
motion-dom@12.40.0:
dependencies:
motion-utils: 12.39.0
motion-utils@12.39.0: {}
ms@2.1.3: {} ms@2.1.3: {}
n8ao@1.10.1(postprocessing@6.39.1(three@0.184.0))(three@0.184.0): n8ao@1.10.1(postprocessing@6.39.1(three@0.184.0))(three@0.184.0):

BIN
public/models/shoe_blue.glb Normal file

Binary file not shown.

BIN
public/models/shoe_grey.glb Normal file

Binary file not shown.

BIN
public/models/shoe_red.glb Normal file

Binary file not shown.