Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

breaking: up deps, switch to new drei-view #156

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -23,16 +23,16 @@ This starter allows you to navigate seamlessly between pages with dynamic dom an
_Tailwind is the default style. styled-components (styled) are also available._

```sh
yarn create r3f-app next my-app
# yarn create r3f-app <next> my-app <tailwind|styled>? -ts?
npx create r3f-app next my-app
# npx create r3f-app <next> my-app <tailwind|styled>? -ts?
```

### :passport_control: Typescript

For typescript add the parameter `-ts` or `--typescript`:

```sh
yarn create r3f-app next my-app -ts
npx create r3f-app next my-app -ts
```

### :mount_fuji: Features
@@ -45,7 +45,7 @@ yarn create r3f-app next my-app -ts

### :bullettrain_side: Architecture

Thanks to [tunnel-rat](https://github.com/pmndrs/tunnel-rat) the starter can portal components between separate renderers. Anything rendered inside the `<View/>` component of the starter will be rendered in the 3D Context. For better performances it uses gl.scissor to cut the viewport into segments.
Thanks to [drei/view](https://github.com/pmndrs/drei?tab=readme-ov-file#view) the starter can portal components between separate renderers. Anything rendered inside the `<View/>` component of the starter will be rendered in the 3D Context. For better performance it uses gl.scissor to cut the viewport into segments. Your canvas contents live inside your DOM graph, the separation between HTML and canvas falls away!

```jsx
<div className='relative'>
@@ -56,6 +56,8 @@ Thanks to [tunnel-rat](https://github.com/pmndrs/tunnel-rat) the starter can por
</div>
```

If you use `<OrbitControls/>` inside a `<View/>`, you may need `data-lenis-prevent` to prevent jittering.

### :control_knobs: Available Scripts

- `yarn dev` - Next dev
@@ -69,7 +71,7 @@ Thanks to [tunnel-rat](https://github.com/pmndrs/tunnel-rat) the starter can por
- [`create-r3f-app`](https://github.com/utsuboco/create-r3f-app) &ndash; Command line tool to simplify the installation.
- [`threejs`](https://github.com/mrdoob/three.js/) &ndash; A lightweight, 3D library with a default WebGL renderer.
- [`@react-three/fiber`](https://github.com/pmndrs/react-three-fiber) &ndash; A React renderer for Threejs on the web and react-native.
- [`@react-three/drei` - Optional](https://github.com/pmndrs/drei) &ndash; useful helpers for react-three-fiber
- [`@react-three/drei`](https://github.com/pmndrs/drei) &ndash; useful helpers for react-three-fiber
- [`@react-three/a11y` - Optional](https://github.com/pmndrs/react-three-a11y/) &ndash; Accessibility tools for React Three Fiber
- [`r3f-perf` - Optional](https://github.com/RenaudRohlinger/r3f-perf) &ndash; Tool to easily monitor react threejs performances.

@@ -82,4 +84,5 @@ git clone https://github.com/pmndrs/react-three-next

### Maintainers :

- [`twitter 🐈‍⬛ @onirenaud`](https://twitter.com/onirenaud)
- [`twitter @onirenaud`](https://twitter.com/onirenaud)
- [`twitter @0xca0a`](https://twitter.com/0xca0a')
26 changes: 5 additions & 21 deletions app/blob/page.jsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,20 @@
'use client'

import dynamic from 'next/dynamic'
import { Loading } from '@/components/dom/Loading'

const Blob = dynamic(() => import('@/components/canvas/Examples').then((mod) => mod.Blob), { ssr: false })
const View = dynamic(() => import('@/components/canvas/View').then((mod) => mod.View), {
ssr: false,
loading: () => (
<div className='flex h-96 w-full flex-col items-center justify-center'>
<svg className='-ml-1 mr-3 h-5 w-5 animate-spin text-black' fill='none' viewBox='0 0 24 24'>
<circle className='opacity-25' cx='12' cy='12' r='10' stroke='currentColor' strokeWidth='4' />
<path
className='opacity-75'
fill='currentColor'
d='M4 12a8 8 0 0 1 8-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 0 1 4 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z'
/>
</svg>
</div>
),
})
const Common = dynamic(() => import('@/components/canvas/View').then((mod) => mod.Common), { ssr: false })
const Common = dynamic(() => import('@/components/canvas/Common'), { ssr: false })
const Blob = dynamic(() => import('@/components/canvas/Blob'), { ssr: false })
const View = dynamic(() => import('@/components/canvas/View'), { ssr: false, loading: Loading })

export default function Page() {
return (
<>
<div className='mx-auto flex w-full flex-col flex-wrap items-center md:flex-row lg:w-4/5'>
<div className='mx-auto flex w-full flex-col flex-wrap items-center md:flex-row lg:w-4/5'>
<div className='flex w-full flex-col items-start justify-center p-12 text-center md:w-2/5 md:text-left'>
<p className='w-full uppercase'>Next + React Three Fiber</p>
<h1 className='my-4 text-5xl font-bold leading-tight'>Next 3D Starter</h1>
<p className='mb-8 text-2xl leading-normal'>A minimalist starter for React, React-three-fiber and Threejs.</p>
</div>
</div>

<View className='absolute top-0 flex h-screen w-full flex-col items-center justify-center'>
<Blob />
<Common />
Binary file added app/fonts/ApfelGrotezk-Fett.woff2
Binary file not shown.
Binary file added app/fonts/ApfelGrotezk-Regular.woff2
Binary file not shown.
Binary file added app/fonts/InstrumentSerif-Italic.woff2
Binary file not shown.
Binary file added app/fonts/InstrumentSerif-Regular.woff2
Binary file not shown.
51 changes: 43 additions & 8 deletions app/global.css
Original file line number Diff line number Diff line change
@@ -6,14 +6,49 @@
* {
box-sizing: border-box;
}
}

html {
color: #D9D9D9;
background-color: #000;
}

.torus {
--size: 80vh;
width: var(--size);
height: var(--size);
}

.description {
width: 18vw;
position: relative;
}

html,
body,
#root {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
.description::before {
content: '●';
position: absolute;
top: 0;
left: -2vw;
}

@keyframes float {
0% {
transform: translateY(0);
}
33% {
transform: translateY(-25px);
}
100% {
transform: translateY(0);
}

}

.monolith {
animation: float 5s infinite;
}

*::selection {
background-color: #D9D9D9;
color: #000;
}
16 changes: 14 additions & 2 deletions app/layout.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Layout } from '@/components/dom/Layout'
import dynamic from 'next/dynamic'
import '@/global.css'

const Scene = dynamic(() => import('@/components/canvas/Scene'), { ssr: false })

export const metadata = {
title: 'Next.js + Three.js',
description: 'A minimal starter for Nextjs + React-three-fiber and Threejs.',
@@ -16,7 +18,17 @@ export default function RootLayout({ children }) {
<head />
<body>
{/* To avoid FOUT with styled-components wrap Layout with StyledComponentsRegistry https://beta.nextjs.org/docs/styling/css-in-js#styled-components */}
<Layout>{children}</Layout>
{children}
<Scene
style={{
position: 'fixed',
top: 0,
left: 0,
width: '100vw',
height: '100vh',
pointerEvents: 'none',
}}
/>
</body>
</html>
)
114 changes: 57 additions & 57 deletions app/page.jsx
Original file line number Diff line number Diff line change
@@ -1,82 +1,82 @@
'use client'

import dynamic from 'next/dynamic'
import { Suspense } from 'react'
import { Loading } from '@/components/dom/Loading'
import localFont from 'next/font/local'
import cn from 'clsx'

const Common = dynamic(() => import('@/components/canvas/Common'), { ssr: false })
const Logo = dynamic(() => import('@/components/canvas/Logo'), { ssr: false })
const Torus = dynamic(() => import('@/components/canvas/Torus'), { ssr: false })
const Dog = dynamic(() => import('@/components/canvas/Dog'), { ssr: false })
const Duck = dynamic(() => import('@/components/canvas/Duck'), { ssr: false })
const View = dynamic(() => import('@/components/canvas/View'), { ssr: false, loading: Loading })

const apfel = localFont({
src: './fonts/ApfelGrotezk-Regular.woff2',
})

const Logo = dynamic(() => import('@/components/canvas/Examples').then((mod) => mod.Logo), { ssr: false })
const Dog = dynamic(() => import('@/components/canvas/Examples').then((mod) => mod.Dog), { ssr: false })
const Duck = dynamic(() => import('@/components/canvas/Examples').then((mod) => mod.Duck), { ssr: false })
const View = dynamic(() => import('@/components/canvas/View').then((mod) => mod.View), {
ssr: false,
loading: () => (
<div className='flex h-96 w-full flex-col items-center justify-center'>
<svg className='-ml-1 mr-3 h-5 w-5 animate-spin text-black' fill='none' viewBox='0 0 24 24'>
<circle className='opacity-25' cx='12' cy='12' r='10' stroke='currentColor' strokeWidth='4' />
<path
className='opacity-75'
fill='currentColor'
d='M4 12a8 8 0 0 1 8-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 0 1 4 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z'
/>
</svg>
</div>
),
const instrument = localFont({
src: './fonts/InstrumentSerif-Regular.woff2',
})
const Common = dynamic(() => import('@/components/canvas/View').then((mod) => mod.Common), { ssr: false })

export default function Page() {
return (
<>
<div className='mx-auto flex w-full flex-col flex-wrap items-center md:flex-row lg:w-4/5'>
{/* jumbo */}
<div className='flex w-full flex-col items-start justify-center p-12 text-center md:w-2/5 md:text-left'>
<p className='w-full uppercase'>Next + React Three Fiber</p>
<h1 className='my-4 text-5xl font-bold leading-tight'>Next 3D Starter</h1>
<p className='mb-8 text-2xl leading-normal'>A minimalist starter for React, React-three-fiber and Threejs.</p>
</div>

<div className='w-full text-center md:w-3/5'>
<View className='flex h-96 w-full flex-col items-center justify-center'>
<Suspense fallback={null}>
<Logo route='/blob' scale={0.6} position={[0, 0, 0]} />
<Common />
</Suspense>
</View>
<main className={apfel.className}>
<div className='h-dvh overflow-x-hidden uppercase'>
<div className='absolute inset-4'>
<span className={cn(instrument.className, 'absolute top-0 left-0 text-2xl leading-2xl tracking-tight')}>
next
</span>
<span className={cn(apfel.className, 'absolute top-0 left-1/2 -translate-x-1/2 text-xl leading-xl')}>
next + react three fiber
</span>
<span className={cn(instrument.className, 'absolute top-0 right-0 text-2xl leading-2xl')}>THREE</span>
<span
className={cn(
instrument.className,
'absolute bottom-0 left-1/2 -translate-x-1/2 text-2xl leading-2xl tracking-tight',
)}
>
starter
</span>
<div className='torus absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2'>
<p className='description absolute left-full top-1/2 -translate-y-1/2 text-xs'>
A minimalist starter for Next, React-three-fiber and Threejs.
</p>
<View className='size-full'>
<Torus />
</View>
</div>
</div>
</div>

<div className='mx-auto flex w-full flex-col flex-wrap items-center p-12 md:flex-row lg:w-4/5'>
{/* first row */}
<div className='relative h-48 w-full py-6 sm:w-1/2 md:my-12 md:mb-40'>
<h2 className='mb-3 text-3xl font-bold leading-none text-gray-800'>Events are propagated</h2>
<p className='mb-8 text-gray-600'>Drag, scroll, pinch, and rotate the canvas to explore the 3D scene.</p>
<div className='relative h-64 w-full py-6 sm:w-1/2 md:my-12 md:mb-40'>
<h2 className={'mb-3 text-xl font-bold'}>Events are propagated</h2>
<p className='mb-8'>Drag, scroll, pinch, and rotate the canvas to explore the 3D scene.</p>
</div>
<div className='relative my-12 h-48 w-full py-6 sm:w-1/2 md:mb-40'>
<View orbit className='relative h-full sm:h-48 sm:w-full'>
<Suspense fallback={null}>
<Dog scale={2} position={[0, -1.6, 0]} rotation={[0.0, -0.3, 0]} />
<Common color={'lightpink'} />
</Suspense>
<div className='relative my-12 h-64 w-full py-6 sm:w-1/2 md:mb-40'>
<View className='relative h-full sm:w-full'>
<Dog href='/blob' scale={2} rotation={[0.0, -0.3, 0]} />
<Common color='lightpink' controls enableZoom={true} />
</View>
</div>
{/* second row */}
<div className='relative my-12 h-48 w-full py-6 sm:w-1/2 md:mb-40'>
<View orbit className='relative h-full animate-bounce sm:h-48 sm:w-full'>
<Suspense fallback={null}>
<Duck route='/blob' scale={2} position={[0, -1.6, 0]} />
<Common color={'lightblue'} />
</Suspense>
<div className='relative my-12 h-64 w-full py-6 sm:w-1/2'>
<View className='monolith relative h-full sm:w-full'>
<Duck scale={1.5} />
<Common color='lightblue' controls />
</View>
</div>
<div className='w-full p-6 sm:w-1/2'>
<h2 className='mb-3 text-3xl font-bold leading-none text-gray-800'>Dom and 3D are synchronized</h2>
<p className='mb-8 text-gray-600'>
<div className='h-64 w-full p-6 sm:w-1/2'>
<h2 className='mb-3 text-xl font-bold'>Dom and 3D are synchronized</h2>
<p className='mb-8'>
3D Divs are renderer through the View component. It uses gl.scissor to cut the viewport into segments. You
tie a view to a tracking div which then controls the position and bounds of the viewport. This allows you to
have multiple views with a single, performant canvas. These views will follow their tracking elements,
scroll along, resize, etc.
</p>
</div>
</div>
</>
</main>
)
}
29 changes: 15 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
@@ -17,31 +17,32 @@
"start": "next start"
},
"dependencies": {
"@ducanh2912/next-pwa": "^10.0.0",
"@react-three/drei": "^9.92.7",
"@react-three/fiber": "^8.15.12",
"@ducanh2912/next-pwa": "^10.2.2",
"@react-three/drei": "9.97.0",
"@react-three/fiber": "^8.15.15",
"@studio-freight/lenis": "^1.0.34",
"clsx": "^2.1.0",
"glsl-random": "^0.0.5",
"next": "^14.0.4",
"next": "^14.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"three": "^0.160.0",
"three-stdlib": "^2.28.9",
"tunnel-rat": "^0.1.2"
"three": "^0.160.1",
"three-stdlib": "^2.29.4"
},
"devDependencies": {
"@next/bundle-analyzer": "^14.0.4",
"autoprefixer": "^10.4.16",
"@next/bundle-analyzer": "^14.1.0",
"autoprefixer": "^10.4.17",
"eslint": "^8.56.0",
"eslint-config-next": "^14.0.4",
"eslint-config-next": "^14.1.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-tailwindcss": "^3.13.0",
"eslint-plugin-tailwindcss": "^3.14.0",
"file-loader": "^6.2.0",
"glslify": "^7.1.1",
"glslify-loader": "^2.0.0",
"postcss": "^8.4.32",
"prettier": "^3.1.1",
"postcss": "^8.4.33",
"prettier": "^3.2.4",
"raw-loader": "^4.0.2",
"tailwindcss": "^3.4.0",
"tailwindcss": "^3.4.1",
"url-loader": "^4.1.1"
}
}
22 changes: 22 additions & 0 deletions src/components/canvas/Blob.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use client'

import { useState } from 'react'
import { useCursor, MeshDistortMaterial } from '@react-three/drei'
import { useRouter } from 'next/navigation'

export default function Blob({ route = '/', ...props }) {
const router = useRouter()
const [hovered, hover] = useState(false)
useCursor(hovered)
return (
<mesh
onClick={() => router.push(route)}
onPointerOver={() => hover(true)}
onPointerOut={() => hover(false)}
{...props}
>
<sphereGeometry args={[1, 64, 64]} />
<MeshDistortMaterial roughness={0.5} color={hovered ? 'hotpink' : '#1fb2f5'} />
</mesh>
)
}
Loading
Oops, something went wrong.