├── .eslintrc.json
├── .gitignore
├── .prettierrc
├── README.md
├── jsconfig.json
├── next.config.mjs
├── package-lock.json
├── package.json
├── postcss.config.mjs
├── public
├── default.exr
├── heel.glb
└── thumb
│ ├── black
│ ├── 2-1.webp
│ ├── 2-2.webp
│ ├── 2-3.webp
│ └── 2-4.webp
│ ├── clover-og.webp
│ ├── red
│ ├── 1-1.webp
│ ├── 1-2.webp
│ ├── 1-3.webp
│ └── 1-4.webp
│ └── yellow
│ ├── 3-1.webp
│ ├── 3-2.webp
│ ├── 3-3.webp
│ └── 3-4.webp
├── src
├── app
│ ├── _components
│ │ ├── CanvasExperience.jsx
│ │ └── ProductDetails.jsx
│ ├── favicon.ico
│ ├── fonts
│ │ ├── GeistMonoVF.woff
│ │ └── GeistVF.woff
│ ├── globals.css
│ ├── layout.jsx
│ └── page.jsx
├── components
│ ├── Details.jsx
│ ├── Experience.jsx
│ ├── FakeBreadCrumb.jsx
│ ├── Header.jsx
│ ├── LoadingAnimation.jsx
│ ├── Logo.jsx
│ ├── Model.jsx
│ └── Scene.jsx
├── context
│ └── store.js
├── data
│ ├── index.js
│ └── svg.js
└── shader
│ └── customStandardMaterial.js
└── tailwind.config.js
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["prettier-plugin-tailwindcss"]
3 | }
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Clover | Heel Brand
2 |
3 | A simple configurator for a 3D Heel Product Page.
4 |
5 | ### Demo : https://clover-product-page.netlify.app/
6 |
7 |
8 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@/*": ["./src/*"]
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {};
3 |
4 | export default nextConfig;
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "product-page",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@react-three/drei": "^9.113.0",
13 | "@react-three/fiber": "^8.17.7",
14 | "@react-three/postprocessing": "^2.16.2",
15 | "@types/three": "^0.168.0",
16 | "gsap": "^3.12.5",
17 | "leva": "^0.9.35",
18 | "next": "14.2.13",
19 | "react": "^18",
20 | "react-dom": "^18",
21 | "three": "^0.168.0",
22 | "three-stdlib": "^2.33.0"
23 | },
24 | "devDependencies": {
25 | "eslint": "^8",
26 | "eslint-config-next": "14.2.13",
27 | "postcss": "^8",
28 | "prettier": "^3.3.3",
29 | "prettier-plugin-tailwindcss": "^0.6.6",
30 | "tailwindcss": "^3.4.1"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/public/default.exr:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudhir9297/clover-product-page-r3f/8e66cc3793af3ef26af17b74fa78aa8bd1e32fa1/public/default.exr
--------------------------------------------------------------------------------
/public/heel.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudhir9297/clover-product-page-r3f/8e66cc3793af3ef26af17b74fa78aa8bd1e32fa1/public/heel.glb
--------------------------------------------------------------------------------
/public/thumb/black/2-1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudhir9297/clover-product-page-r3f/8e66cc3793af3ef26af17b74fa78aa8bd1e32fa1/public/thumb/black/2-1.webp
--------------------------------------------------------------------------------
/public/thumb/black/2-2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudhir9297/clover-product-page-r3f/8e66cc3793af3ef26af17b74fa78aa8bd1e32fa1/public/thumb/black/2-2.webp
--------------------------------------------------------------------------------
/public/thumb/black/2-3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudhir9297/clover-product-page-r3f/8e66cc3793af3ef26af17b74fa78aa8bd1e32fa1/public/thumb/black/2-3.webp
--------------------------------------------------------------------------------
/public/thumb/black/2-4.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudhir9297/clover-product-page-r3f/8e66cc3793af3ef26af17b74fa78aa8bd1e32fa1/public/thumb/black/2-4.webp
--------------------------------------------------------------------------------
/public/thumb/clover-og.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudhir9297/clover-product-page-r3f/8e66cc3793af3ef26af17b74fa78aa8bd1e32fa1/public/thumb/clover-og.webp
--------------------------------------------------------------------------------
/public/thumb/red/1-1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudhir9297/clover-product-page-r3f/8e66cc3793af3ef26af17b74fa78aa8bd1e32fa1/public/thumb/red/1-1.webp
--------------------------------------------------------------------------------
/public/thumb/red/1-2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudhir9297/clover-product-page-r3f/8e66cc3793af3ef26af17b74fa78aa8bd1e32fa1/public/thumb/red/1-2.webp
--------------------------------------------------------------------------------
/public/thumb/red/1-3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudhir9297/clover-product-page-r3f/8e66cc3793af3ef26af17b74fa78aa8bd1e32fa1/public/thumb/red/1-3.webp
--------------------------------------------------------------------------------
/public/thumb/red/1-4.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudhir9297/clover-product-page-r3f/8e66cc3793af3ef26af17b74fa78aa8bd1e32fa1/public/thumb/red/1-4.webp
--------------------------------------------------------------------------------
/public/thumb/yellow/3-1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudhir9297/clover-product-page-r3f/8e66cc3793af3ef26af17b74fa78aa8bd1e32fa1/public/thumb/yellow/3-1.webp
--------------------------------------------------------------------------------
/public/thumb/yellow/3-2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudhir9297/clover-product-page-r3f/8e66cc3793af3ef26af17b74fa78aa8bd1e32fa1/public/thumb/yellow/3-2.webp
--------------------------------------------------------------------------------
/public/thumb/yellow/3-3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudhir9297/clover-product-page-r3f/8e66cc3793af3ef26af17b74fa78aa8bd1e32fa1/public/thumb/yellow/3-3.webp
--------------------------------------------------------------------------------
/public/thumb/yellow/3-4.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudhir9297/clover-product-page-r3f/8e66cc3793af3ef26af17b74fa78aa8bd1e32fa1/public/thumb/yellow/3-4.webp
--------------------------------------------------------------------------------
/src/app/_components/CanvasExperience.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect, useState } from "react";
2 | import gsap from "gsap";
3 | import Image from "next/image";
4 |
5 | import Experience from "@/components/Experience";
6 | import { StoreContext } from "@/context/store";
7 | import { backgroundColor, cameraPositionData } from "@/data";
8 |
9 | const CanvasExperience = () => {
10 | const { setCameraPosition, currentVariation } = useContext(StoreContext);
11 | const [bgColor, setBgColor] = useState(backgroundColor[0]);
12 |
13 | useEffect(() => {
14 | gsap.to(".background", {
15 | backgroundColor: bgColor,
16 | duration: 1,
17 | ease: "power2.inOut",
18 | });
19 | return () => {};
20 | }, [bgColor]);
21 |
22 | const handleCameraPosition = ({ x, y, z }) => {
23 | setCameraPosition({ x, y, z });
24 | };
25 |
26 | return (
27 |
28 |
34 |
35 | {backgroundColor.map((el) => (
36 |
setBgColor(el)}
39 | style={{ backgroundColor: el }}
40 | className={`h-8 w-8 cursor-pointer rounded-full border-2 border-white`}
41 | />
42 | ))}
43 |
44 |
45 |
46 |
47 | {cameraPositionData.map((el) => (
48 |
handleCameraPosition(el.position)}
51 | className="background relative flex aspect-square cursor-pointer items-center justify-center overflow-hidden rounded-lg"
52 | >
53 |
60 |
61 | ))}
62 |
63 |
64 | );
65 | };
66 |
67 | export default CanvasExperience;
68 |
--------------------------------------------------------------------------------
/src/app/_components/ProductDetails.jsx:
--------------------------------------------------------------------------------
1 | import Details from "@/components/Details";
2 | import { StoreContext } from "@/context/store";
3 | import React, { useContext } from "react";
4 |
5 | const ProductDetails = () => {
6 | const { currentVariation, setCurrentVariation } = useContext(StoreContext);
7 |
8 | const handleVariationClick = (el) => {
9 | setCurrentVariation(el);
10 | };
11 |
12 | return (
13 |
14 |
18 |
19 | );
20 | };
21 |
22 | export default ProductDetails;
23 |
--------------------------------------------------------------------------------
/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudhir9297/clover-product-page-r3f/8e66cc3793af3ef26af17b74fa78aa8bd1e32fa1/src/app/favicon.ico
--------------------------------------------------------------------------------
/src/app/fonts/GeistMonoVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudhir9297/clover-product-page-r3f/8e66cc3793af3ef26af17b74fa78aa8bd1e32fa1/src/app/fonts/GeistMonoVF.woff
--------------------------------------------------------------------------------
/src/app/fonts/GeistVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudhir9297/clover-product-page-r3f/8e66cc3793af3ef26af17b74fa78aa8bd1e32fa1/src/app/fonts/GeistVF.woff
--------------------------------------------------------------------------------
/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | * {
6 | box-sizing: border-border;
7 | }
8 |
--------------------------------------------------------------------------------
/src/app/layout.jsx:
--------------------------------------------------------------------------------
1 | import localFont from "next/font/local";
2 | import "./globals.css";
3 | import Header from "@/components/Header";
4 |
5 | const geistSans = localFont({
6 | src: "./fonts/GeistVF.woff",
7 | variable: "--font-geist-sans",
8 | weight: "100 900",
9 | });
10 | const geistMono = localFont({
11 | src: "./fonts/GeistMonoVF.woff",
12 | variable: "--font-geist-mono",
13 | weight: "100 900",
14 | });
15 |
16 | export const metadata = {
17 | title: "Clover | Heel Brand",
18 | description: "Product Page for a 3D Heel",
19 | metadataBase: new URL("https://clover-product-page.netlify.app"),
20 | openGraph: {
21 | title: "Clover | Heel Brand",
22 | description: "Product Page for a 3D Heel",
23 | url: "https://clover-product-page.netlify.app/",
24 | siteName: "Clover | Heel Brand",
25 | images: [
26 | {
27 | url: "/thumb/clover-og.webp",
28 | width: 1260,
29 | height: 800,
30 | },
31 | ],
32 | locale: "en-US",
33 | type: "website",
34 | },
35 | };
36 |
37 | export default function RootLayout({ children }) {
38 | return (
39 |
40 |
43 |
44 |
{children}
45 |
46 |
47 | );
48 | }
49 |
--------------------------------------------------------------------------------
/src/app/page.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { StoreProvider } from "@/context/store";
3 | import CanvasExperience from "./_components/CanvasExperience";
4 | import ProductDetails from "./_components/ProductDetails";
5 | import FakeBreadCrumb from "../components/FakeBreadCrumb";
6 |
7 | export default function Home() {
8 | return (
9 |
10 |
17 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/Details.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Image from "next/image";
3 |
4 | import { Cart, Star, Truck } from "@/data/svg";
5 | import { productVariation } from "@/data";
6 |
7 | const Details = ({ variation, handleVariationClick }) => {
8 | const { sku, name, subDesc, rating, price, type, totalReview } = variation;
9 |
10 | return (
11 |
12 |
{sku}
13 |
{name}
14 |
15 | {subDesc}
16 |
17 |
18 |
19 |
20 | {Array.from({ length: 5 }, (_, index) => (
21 | = index + 1} />
22 | ))}
23 |
24 |
25 | ({totalReview} Reviews)
26 |
27 |
28 |
₹{price}.00
29 |
30 | inclusive of all taxes
31 |
32 |
33 |
34 | Color: {type}
35 |
36 |
37 | {productVariation.map((el) => (
38 |
handleVariationClick(el)}
40 | key={el.id}
41 | className={`bg-primaryColor relative h-16 w-16 cursor-pointer overflow-hidden rounded-md border ${el.id === variation.id ? "border-lightGreen" : ""}`}
42 | >
43 |
50 |
51 | ))}
52 |
53 |
54 |
61 |
62 | Free Delivery orn order over ₹5000
63 |
64 |
65 | );
66 | };
67 |
68 | export default Details;
69 |
--------------------------------------------------------------------------------
/src/components/Experience.jsx:
--------------------------------------------------------------------------------
1 | import React, { Suspense } from "react";
2 | import { Canvas } from "@react-three/fiber";
3 | import { Environment, Html, Lightformer } from "@react-three/drei";
4 |
5 | import Scene from "@/components/Scene";
6 | import { LoadingAnimation } from "@/components/LoadingAnimation";
7 |
8 | export default function Experience() {
9 | function Fallback() {
10 | return (
11 |
12 |
13 |
14 | );
15 | }
16 |
17 | return (
18 |
94 | );
95 | }
96 |
--------------------------------------------------------------------------------
/src/components/FakeBreadCrumb.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { StoreContext } from "@/context/store";
3 |
4 | const FakeBreadCrumb = () => {
5 | const { currentVariation } = useContext(StoreContext);
6 |
7 | return (
8 |
9 | Home / Women Footwear / Heels /{" "}
10 | {currentVariation.name}
11 |
12 | );
13 | };
14 |
15 | export default FakeBreadCrumb;
16 |
--------------------------------------------------------------------------------
/src/components/Header.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Logo from "./Logo";
3 | import { Github, XLogo } from "@/data/svg";
4 |
5 | const Header = () => {
6 | return (
7 |
29 | );
30 | };
31 |
32 | export default Header;
33 |
--------------------------------------------------------------------------------
/src/components/LoadingAnimation.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export const LoadingAnimation = () => {
4 | return (
5 |
10 | );
11 | };
12 |
--------------------------------------------------------------------------------
/src/components/Logo.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Clover } from "@/data/svg";
3 |
4 | function LogoWrapper() {
5 | return (
6 |
7 |
8 |
9 | Clover.
10 |
11 |
12 | );
13 | }
14 |
15 | export default LogoWrapper;
16 |
--------------------------------------------------------------------------------
/src/components/Model.jsx:
--------------------------------------------------------------------------------
1 | import React, { createRef, useContext, useEffect, useRef } from "react";
2 | import * as THREE from "three";
3 | import gsap from "gsap";
4 | import { useGLTF } from "@react-three/drei";
5 | import { StoreContext } from "@/context/store";
6 | import MeshCustomStandardMaterial from "@/shader/customStandardMaterial";
7 |
8 | export function Model(props) {
9 | const refs = useRef([createRef(), createRef(), createRef(), createRef()]);
10 | const { currentVariation } = useContext(StoreContext);
11 | const { nodes, materials } = useGLTF("/heel.glb");
12 |
13 | const meshList = currentVariation.meshNameList;
14 |
15 | useEffect(() => {
16 | refs.current.forEach((ref) => {
17 | if (ref.current) {
18 | const name = ref.current.name;
19 | const currentColor = `0x${meshList[name].color.replace("#", "")}`;
20 | ref.current.uniforms.uValueZ.value = 7.0;
21 | ref.current.uniforms.uColor2.value = new THREE.Color().setHex(
22 | currentColor,
23 | );
24 |
25 | gsap.to(ref.current.uniforms.uValueZ, {
26 | duration: 0.75,
27 | value: -6.0,
28 | ease: "linear",
29 | onComplete() {
30 | ref.current.uniforms.uValueZ.value = 7.0;
31 | ref.current.uniforms.uColor1.value = new THREE.Color().setHex(
32 | currentColor,
33 | );
34 | },
35 | });
36 | }
37 | });
38 |
39 | return () => {};
40 | }, [currentVariation]);
41 |
42 | return (
43 |
44 |
45 |
52 |
53 |
54 |
61 |
62 |
63 |
70 |
71 |
72 |
79 |
80 |
87 |
88 | );
89 | }
90 |
91 | useGLTF.preload("/heel.glb");
92 |
--------------------------------------------------------------------------------
/src/components/Scene.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect, useRef } from "react";
2 | import { OrbitControls, Center } from "@react-three/drei";
3 | import { useThree } from "@react-three/fiber";
4 | import gsap from "gsap";
5 |
6 | import { Model } from "@/components/Model";
7 | import { StoreContext } from "@/context/store";
8 |
9 | function Scene() {
10 | const { cameraPosition } = useContext(StoreContext);
11 |
12 | const orbitControlsRef = useRef();
13 | const { camera } = useThree();
14 |
15 | useEffect(() => {
16 | if (orbitControlsRef.current) {
17 | gsap.to(camera.position, {
18 | x: cameraPosition.x,
19 | y: cameraPosition.y,
20 | z: cameraPosition.z,
21 | duration: 1,
22 | ease: "power2.inOut",
23 | onUpdate: () => {
24 | orbitControlsRef.current.update();
25 | },
26 | });
27 | }
28 | return () => {};
29 | }, [cameraPosition, camera]);
30 |
31 | return (
32 | <>
33 |
41 |
42 |
43 |
44 | >
45 | );
46 | }
47 |
48 | export default Scene;
49 |
--------------------------------------------------------------------------------
/src/context/store.js:
--------------------------------------------------------------------------------
1 | import React, { createContext, useContext, useState } from "react";
2 | import { cameraPositionData, productVariation } from "@/data";
3 |
4 | export const StoreContext = createContext();
5 |
6 | export const StoreProvider = ({ children }) => {
7 | const [cameraPosition, setCameraPosition] = useState(
8 | cameraPositionData[0].position,
9 | );
10 | const [currentVariation, setCurrentVariation] = useState(productVariation[0]);
11 |
12 | return (
13 |
21 | {children}
22 |
23 | );
24 | };
25 |
--------------------------------------------------------------------------------
/src/data/index.js:
--------------------------------------------------------------------------------
1 | export const productVariation = [
2 | {
3 | id: 1,
4 | sku: "HEEL-RED-X008",
5 | name: "Crimson Flame Stiletto",
6 | subDesc:
7 | "A sleek red Stiletto pump with a high heel, perfect for adding bold elegance to any outfit.",
8 | totalReview: 42,
9 | rating: 4,
10 | price: 7360,
11 | thumbnail: "/thumb/red/1-1.webp",
12 | type: "red",
13 | variation: [
14 | "/thumb/red/1-1.webp",
15 | "/thumb/red/1-2.webp",
16 | "/thumb/red/1-3.webp",
17 | "/thumb/red/1-4.webp",
18 | ],
19 |
20 | meshNameList: {
21 | sole: {
22 | color: "#111111",
23 | metalness: 0.0001,
24 | roughness: 0.918182,
25 | },
26 | inner_side: { color: "#E7E7E7", metalness: 0.0001, roughness: 0.822727 },
27 | inner_base: { color: "#E7B39C", metalness: 0.0001, roughness: 0.790909 },
28 | body: { color: "#C90023", metalness: 0.1, roughness: 0.07273 },
29 | },
30 | },
31 | {
32 | id: 2,
33 | sku: "HEEL-BLACK-X008",
34 | name: "Obsidian Luxe Stiletto",
35 | subDesc:
36 | "A chic brown Stiletto pump with a slender heel, offering understated sophistication and versatile style.",
37 | totalReview: 22,
38 | rating: 3,
39 | price: 9020,
40 | thumbnail: "/thumb/black/2-1.webp",
41 | type: "black",
42 | variation: [
43 | "/thumb/black/2-1.webp",
44 | "/thumb/black/2-2.webp",
45 | "/thumb/black/2-3.webp",
46 | "/thumb/black/2-4.webp",
47 | ],
48 | meshNameList: {
49 | sole: {
50 | color: "#E8DDCD",
51 | metalness: 0.0001,
52 | roughness: 0.918182,
53 | },
54 | inner_side: { color: "#C1A995", metalness: 0.0001, roughness: 0.822727 },
55 | inner_base: { color: "#DFBFB0", metalness: 0.0001, roughness: 0.890909 },
56 | body: { color: "#22241E", metalness: 0.1, roughness: 0.07273 },
57 | },
58 | },
59 | {
60 | id: 3,
61 | sku: "HEEL-YELLOW-X008",
62 | name: "Yellow Glimmer Stiletto",
63 | subDesc:
64 | "A glamorous gold Stiletto pump with a high, shimmering heel that instantly elevates your look with luxe flair.",
65 | totalReview: 12,
66 | rating: 4,
67 | price: 8118,
68 | thumbnail: "/thumb/yellow/3-1.webp",
69 | type: "yellow",
70 | variation: [
71 | "/thumb/yellow/3-1.webp",
72 | "/thumb/yellow/3-2.webp",
73 | "/thumb/yellow/3-3.webp",
74 | "/thumb/yellow/3-4.webp",
75 | ],
76 | meshNameList: {
77 | sole: {
78 | color: "#111111",
79 | metalness: 0.0001,
80 | roughness: 0.918182,
81 | },
82 | inner_side: { color: "#E7E7E7", metalness: 0.0001, roughness: 0.822727 },
83 | inner_base: { color: "#E7B39C", metalness: 0.0001, roughness: 0.790909 },
84 | body: { color: "#FECD33", metalness: 0.1, roughness: 0.07273 },
85 | },
86 | },
87 | ];
88 |
89 | export const backgroundColor = ["#F2F0EA", "#97ABAC"];
90 |
91 | export const cameraPositionData = [
92 | {
93 | id: 1,
94 | position: {
95 | x: -8.376343341043107,
96 | y: 3.029775620301042,
97 | z: 7.203629560630783,
98 | },
99 | },
100 | {
101 | id: 2,
102 | position: {
103 | x: -9.408526434274766,
104 | y: 1.16192894554274,
105 | z: -6.431523799556721,
106 | },
107 | },
108 | {
109 | id: 3,
110 | position: {
111 | x: 13.208084325913742,
112 | y: 2.7860447354758353,
113 | z: -7.958559536282182,
114 | },
115 | },
116 | {
117 | id: 4,
118 | position: {
119 | x: 10.46970645902348,
120 | y: 2.8776727926486334,
121 | z: 12.692286882561012,
122 | },
123 | },
124 | ];
125 |
--------------------------------------------------------------------------------
/src/data/svg.js:
--------------------------------------------------------------------------------
1 | export const Star = ({ fill }) => {
2 | return (
3 |
16 | );
17 | };
18 |
19 | export const Cart = () => {
20 | return (
21 |
35 | );
36 | };
37 |
38 | export function Truck() {
39 | return (
40 |
56 | );
57 | }
58 |
59 | export function Clover() {
60 | return (
61 |
75 | );
76 | }
77 |
78 | export function XLogo() {
79 | return (
80 |
96 | );
97 | }
98 |
99 | export function Github() {
100 | return (
101 |
115 | );
116 | }
117 |
--------------------------------------------------------------------------------
/src/shader/customStandardMaterial.js:
--------------------------------------------------------------------------------
1 | import * as THREE from "three";
2 | import { forwardRef } from "react";
3 | import { extend } from "@react-three/fiber";
4 |
5 | class CustomStandardMaterial extends THREE.MeshStandardMaterial {
6 | constructor(args) {
7 | super(args);
8 |
9 | this.uniforms = {
10 | uValueZ: { value: 7.0 },
11 | uColor1: { value: new THREE.Color("#C90023") },
12 | uColor2: { value: new THREE.Color("#C90023") },
13 | };
14 |
15 | this.onBeforeCompile = (shader) => {
16 | shader.uniforms = {
17 | ...shader.uniforms,
18 | ...this.uniforms,
19 | };
20 |
21 | shader.vertexShader = shader.vertexShader.replace(
22 | "#include
",
23 | `#include
24 | varying vec3 uPosition;
25 | `,
26 | );
27 |
28 | shader.vertexShader = shader.vertexShader.replace(
29 | "vViewPosition = - mvPosition.xyz;",
30 | `vViewPosition = - mvPosition.xyz;
31 | uPosition = position.xyz;
32 | `,
33 | );
34 |
35 | shader.fragmentShader = shader.fragmentShader.replace(
36 | "#include ",
37 | `#include
38 | uniform float uValueZ;
39 | uniform vec3 uColor1;
40 | uniform vec3 uColor2;
41 | varying vec3 uPosition;
42 | `,
43 | );
44 |
45 | shader.fragmentShader = shader.fragmentShader.replace(
46 | "vec4 diffuseColor = vec4( diffuse, opacity );",
47 | `float posZ = (uPosition.y / 1.0) + uValueZ;
48 |
49 | float uValueOffset = (uPosition.y / 1.0) + (uValueZ + 0.25);
50 | float posZBorder = step(uValueOffset, 0.5);
51 | posZ = step(posZ ,0.5);
52 | vec3 borderColor = vec3(1.0, 1.0, 1.0);
53 | float borderOffset = 0.2;
54 | vec3 newColor2 = uColor2;
55 | newColor2 = mix(borderColor, newColor2, posZBorder);
56 | vec3 finalColor = mix(uColor1, newColor2 , posZ);
57 | vec4 diffuseColor = vec4( finalColor, opacity );`,
58 | );
59 | };
60 |
61 | Object.keys(this.uniforms).forEach((name) =>
62 | Object.defineProperty(this, name, {
63 | get: () => this.uniforms[name].value,
64 | set: (v) => (this.uniforms[name].value = v),
65 | }),
66 | );
67 | }
68 | }
69 |
70 | extend({ CustomMaterial: CustomStandardMaterial });
71 |
72 | export default forwardRef(function MeshCustomStandardMaterial(props, ref) {
73 | return ;
74 | });
75 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
5 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
7 | ],
8 | theme: {
9 | extend: {
10 | colors: {
11 | fadeGray: "#535665",
12 | lightGreen: "#199892",
13 | primaryColor: "#F2F0EA",
14 | },
15 | },
16 | },
17 | plugins: [],
18 | };
19 |
--------------------------------------------------------------------------------