├── .gitignore
├── LICENSE
├── package-lock.json
├── package.json
├── public
├── DancingScript-VariableFont_wght.ttf
├── Inter_Bold.json
├── background_hdr.exr
├── epic.jpg
├── favicons
│ ├── about.txt
│ ├── android-chrome-192x192.png
│ ├── android-chrome-512x512.png
│ ├── apple-touch-icon.png
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── favicon.ico
│ └── site.webmanifest
├── index.html
├── merry_xmas.svg
├── music.mp3
├── preview.jpg
└── snowglobe-transformed.glb
├── readme.md
├── sandbox.config.json
└── src
├── App.js
├── FireWorks.js
├── FireWorksMaterial.js
├── LumaWorld.js
├── Overlay.js
├── PostProcessingEffects.js
├── Scene.js
├── SnowFlakes.js
├── SnowGlobeModel.js
├── index.js
└── styles.css
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | .pnpm-debug.log*
9 |
10 | # Diagnostic reports (https://nodejs.org/api/report.html)
11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # Snowpack dependency directory (https://snowpack.dev/)
46 | web_modules/
47 |
48 | # TypeScript cache
49 | *.tsbuildinfo
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Optional stylelint cache
58 | .stylelintcache
59 |
60 | # Microbundle cache
61 | .rpt2_cache/
62 | .rts2_cache_cjs/
63 | .rts2_cache_es/
64 | .rts2_cache_umd/
65 |
66 | # Optional REPL history
67 | .node_repl_history
68 |
69 | # Output of 'npm pack'
70 | *.tgz
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # dotenv environment variable files
76 | .env
77 | .env.development.local
78 | .env.test.local
79 | .env.production.local
80 | .env.local
81 |
82 | # parcel-bundler cache (https://parceljs.org/)
83 | .cache
84 | .parcel-cache
85 |
86 | # Next.js build output
87 | .next
88 | out
89 |
90 | # Nuxt.js build / generate output
91 | .nuxt
92 | dist
93 |
94 | # Gatsby files
95 | .cache/
96 | # Comment in the public line in if your project uses Gatsby and not Next.js
97 | # https://nextjs.org/blog/next-9-1#public-directory-support
98 | # public
99 |
100 | # vuepress build output
101 | .vuepress/dist
102 |
103 | # vuepress v2.x temp and cache directory
104 | .temp
105 | .cache
106 |
107 | # Docusaurus cache and generated files
108 | .docusaurus
109 |
110 | # Serverless directories
111 | .serverless/
112 |
113 | # FuseBox cache
114 | .fusebox/
115 |
116 | # DynamoDB Local files
117 | .dynamodb/
118 |
119 | # TernJS port file
120 | .tern-port
121 |
122 | # Stores VSCode versions used for testing VSCode extensions
123 | .vscode-test
124 |
125 | # yarn v2
126 | .yarn/cache
127 | .yarn/unplugged
128 | .yarn/build-state.yml
129 | .yarn/install-state.gz
130 | .pnp.*
131 | .vercel
132 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Anderson Mancini
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "random-snow-globe-test-anderson",
3 | "version": "1.0.0",
4 | "description": "",
5 | "keywords": [],
6 | "main": "src/index.js",
7 | "dependencies": {
8 | "@lumaai/luma-web": "0.1.15",
9 | "@react-three/drei": "latest",
10 | "@react-three/fiber": "latest",
11 | "@react-three/postprocessing": "latest",
12 | "@types/three": "0.157.2",
13 | "gsap": "3.12.2",
14 | "react": "18.2.0",
15 | "react-dom": "18.2.0",
16 | "react-scripts": "5.0.1",
17 | "three": "latest",
18 | "vercel": "^32.7.2"
19 | },
20 | "scripts": {
21 | "start": "react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test --env=jsdom",
24 | "eject": "react-scripts eject",
25 | "deploy": "vercel --prod"
26 | },
27 | "browserslist": [
28 | ">1%",
29 | "not dead",
30 | "not ie <= 11",
31 | "not op_mini all"
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/public/DancingScript-VariableFont_wght.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ektogamat/christmas-threejs/14aeed59e24e54cd2f40f804aa106e1739bbd694/public/DancingScript-VariableFont_wght.ttf
--------------------------------------------------------------------------------
/public/background_hdr.exr:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ektogamat/christmas-threejs/14aeed59e24e54cd2f40f804aa106e1739bbd694/public/background_hdr.exr
--------------------------------------------------------------------------------
/public/epic.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ektogamat/christmas-threejs/14aeed59e24e54cd2f40f804aa106e1739bbd694/public/epic.jpg
--------------------------------------------------------------------------------
/public/favicons/about.txt:
--------------------------------------------------------------------------------
1 | This favicon was generated using the following graphics from Twitter Twemoji:
2 |
3 | - Graphics Title: 1f384.svg
4 | - Graphics Author: Copyright 2020 Twitter, Inc and other contributors (https://github.com/twitter/twemoji)
5 | - Graphics Source: https://github.com/twitter/twemoji/blob/master/assets/svg/1f384.svg
6 | - Graphics License: CC-BY 4.0 (https://creativecommons.org/licenses/by/4.0/)
7 |
--------------------------------------------------------------------------------
/public/favicons/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ektogamat/christmas-threejs/14aeed59e24e54cd2f40f804aa106e1739bbd694/public/favicons/android-chrome-192x192.png
--------------------------------------------------------------------------------
/public/favicons/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ektogamat/christmas-threejs/14aeed59e24e54cd2f40f804aa106e1739bbd694/public/favicons/android-chrome-512x512.png
--------------------------------------------------------------------------------
/public/favicons/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ektogamat/christmas-threejs/14aeed59e24e54cd2f40f804aa106e1739bbd694/public/favicons/apple-touch-icon.png
--------------------------------------------------------------------------------
/public/favicons/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ektogamat/christmas-threejs/14aeed59e24e54cd2f40f804aa106e1739bbd694/public/favicons/favicon-16x16.png
--------------------------------------------------------------------------------
/public/favicons/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ektogamat/christmas-threejs/14aeed59e24e54cd2f40f804aa106e1739bbd694/public/favicons/favicon-32x32.png
--------------------------------------------------------------------------------
/public/favicons/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ektogamat/christmas-threejs/14aeed59e24e54cd2f40f804aa106e1739bbd694/public/favicons/favicon.ico
--------------------------------------------------------------------------------
/public/favicons/site.webmanifest:
--------------------------------------------------------------------------------
1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Merry Threejs Christmas - By Anderson Mancini
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/public/merry_xmas.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
40 |
--------------------------------------------------------------------------------
/public/music.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ektogamat/christmas-threejs/14aeed59e24e54cd2f40f804aa106e1739bbd694/public/music.mp3
--------------------------------------------------------------------------------
/public/preview.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ektogamat/christmas-threejs/14aeed59e24e54cd2f40f804aa106e1739bbd694/public/preview.jpg
--------------------------------------------------------------------------------
/public/snowglobe-transformed.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ektogamat/christmas-threejs/14aeed59e24e54cd2f40f804aa106e1739bbd694/public/snowglobe-transformed.glb
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Merry Three.js Christmas
2 |
3 | Christmas scene using threejs, react three fiber and gaussian splatting
4 |
5 | [](https://threejs-christmas.vercel.app/)
6 |
7 | ## Setup
8 |
9 | Download [Node.js](https://nodejs.org/en/download/).
10 | Run this followed commands:
11 |
12 | ```bash
13 | # Install dependencies (only the first time)
14 | npm install
15 |
16 | # Run the local server at localhost:8080
17 | npm start
18 |
19 | # Build for production in the dist/ directory
20 | npm run build
21 | ```
22 |
--------------------------------------------------------------------------------
/sandbox.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "infiniteLoopProtection": false,
3 | "hardReloadOnChange": false,
4 | "view": "browser"
5 | }
6 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import { Canvas } from '@react-three/fiber'
2 | import { Loader, PositionalAudio } from '@react-three/drei'
3 | import { useState, Suspense, useRef, useEffect } from 'react'
4 | import PostProcessingEffects from './PostProcessingEffects'
5 | import SnowGlobeModel from './SnowGlobeModel'
6 | import Overlay from './Overlay'
7 | import SceneSetup from './Scene'
8 |
9 | export default function App() {
10 | const audioRef = useRef()
11 | const [inside, setInside] = useState(false)
12 | const isMobile = window.innerWidth < 768
13 | const canvasConfig = { antialias: false, depth: false, stencil: false, alpha: false }
14 | const [ready, setReady] = useState(false)
15 |
16 | return (
17 | <>
18 |
45 |
49 |
50 | >
51 | )
52 | }
53 |
--------------------------------------------------------------------------------
/src/FireWorks.js:
--------------------------------------------------------------------------------
1 | import FireWorksMaterial from './FireWorksMaterial'
2 | import { forwardRef } from 'react'
3 |
4 | export default forwardRef(function FireWorks(props, ref) {
5 | return (
6 |
7 |
8 |
9 |
10 | )
11 | })
12 |
--------------------------------------------------------------------------------
/src/FireWorksMaterial.js:
--------------------------------------------------------------------------------
1 | import React, { useRef, useMemo, useEffect } from 'react'
2 | import { shaderMaterial } from '@react-three/drei'
3 | import { extend, useFrame, useThree } from '@react-three/fiber'
4 | import { FrontSide, AdditiveBlending, Vector2 } from 'three'
5 |
6 | /**
7 | * Fireworks component by Anderson Mancini.
8 | * Based on https://www.shadertoy.com/view/4dBGRw
9 | */
10 |
11 | export default function FireWorksMaterial() {
12 | const { size, viewport } = useThree()
13 | const ref = useRef()
14 |
15 | const FireWorksMaterial = useMemo(() => {
16 | return shaderMaterial(
17 | {
18 | time: 0,
19 | iResolution: new Vector2(size.width, size.height)
20 | },
21 | `
22 | varying vec2 vUv;
23 | void main() {
24 |
25 | vUv = uv;
26 | gl_Position = vec4(position, 1.0);
27 |
28 | }`,
29 | `
30 | varying vec2 vUv;
31 | uniform float time;
32 | uniform vec2 iResolution;
33 |
34 | vec3 texture(vec2 pos, vec2 rocketpos){
35 | float d = (pos.x-rocketpos.x)+(pos.y-rocketpos.y);
36 | vec3 col=vec3(1.0);
37 |
38 | d = mod(d*15.,2.0);
39 | if(d>1.0){
40 | col=vec3(0.0,0.0,1.0);
41 | }
42 | return col;
43 | }
44 |
45 | vec3 rocket(vec2 pos, vec2 rocketpos){
46 | vec3 col = vec3(0.0);
47 | float f = 0.;
48 | float absx= abs(rocketpos.x - pos.x);
49 | float absy = abs(rocketpos.y-pos.y);
50 | if(absx<0.05&&absy<0.15){
51 | col=texture(pos, rocketpos);
52 | }
53 | float point=(rocketpos.y-pos.y-0.25)*-0.7;
54 | if((rocketpos.y-pos.y)>0.1){
55 | f=smoothstep(point-0.001,point+0.001,absx);
56 |
57 | col=mix(vec3(1.0,0.0,0.0),col, f);
58 | }
59 | return col;
60 | }
61 |
62 | float rand(float val, float seed){return cos(val*sin(val*seed)*seed);}
63 | float distance2( in vec2 a, in vec2 b ) { return dot(a-b,a-b); }
64 | mat2 rr = mat2( cos(1.0), -sin(1.0), sin(1.0), cos(1.0) );
65 |
66 | vec3 drawParticles(vec2 pos, vec3 particolor, float time, vec2 cpos, float gravity, float seed, float timelength){
67 | vec3 col= vec3(0.0);
68 | vec2 pp = vec2(1.0,0.0);
69 | for(float i=1.0;i<=64.0;i++){
70 | float d=rand(i, seed);
71 | float fade=(i/64.0)*time;
72 | vec2 particpos = cpos + time*pp*d;
73 | pp = rr*pp;
74 | col = mix(particolor/fade, col, smoothstep(0.0, 0.0001, distance2(particpos, pos)));
75 | }
76 | col*=smoothstep(0.0,1.0,(timelength-time)/timelength);
77 |
78 | return col;
79 | }
80 |
81 | vec3 drawFireworks(float time, vec2 uv, vec3 particolor, float seed){
82 |
83 | float timeoffset = 1.0;
84 | vec3 col=vec3(0.0);
85 | if(time<=0.){
86 | return col;
87 | }
88 | if(mod(time, 2.0)>timeoffset){
89 | col= drawParticles(uv, particolor, mod(time, 2.0)-timeoffset, vec2(rand(ceil(time/2.0),seed),-0.5), 0.5, ceil(time/2.0), seed);
90 | }else{
91 | col= rocket(uv*15., vec2(15.*rand(ceil(time/2.0),seed),15.*(-0.5+(timeoffset-mod(time, 2.0)))));
92 | }
93 |
94 | return col;
95 | }
96 |
97 | void main() {
98 | vec3 col=vec3(0.1,0.1,0.2);
99 | vec2 uv =1.0 - 2.0* gl_FragCoord.xy / iResolution.xy;
100 | uv.x *= iResolution.x/iResolution.y;
101 | col += drawFireworks(time , uv,vec3(1.0,0.1,0.1), 1.);
102 | col += drawFireworks(time-.6, uv,vec3(0.0,1.0,0.5), 1.2);
103 | col += drawFireworks(time-0.9, uv,vec3(1.0,1.0,0.1), 1.9);
104 | col += drawFireworks(time-0.1, uv,vec3(1.0,1.0,0.1), 1.1);
105 |
106 | col -= 0.15;
107 | gl_FragColor = vec4(col, 1.0);
108 | }`
109 | )
110 | }, [])
111 |
112 | extend({ FireWorksMaterial })
113 |
114 | useFrame((state, delta) => {
115 | ref.current.time += delta
116 | })
117 |
118 | useEffect(() => {
119 | ref.current.iResolution.set(size.width, size.height)
120 | }, [viewport])
121 |
122 | return (
123 |
132 | )
133 | }
134 |
--------------------------------------------------------------------------------
/src/LumaWorld.js:
--------------------------------------------------------------------------------
1 | import { extend } from '@react-three/fiber'
2 | import { LumaSplatsThree } from '@lumaai/luma-web'
3 |
4 | extend({ LumaSplats: LumaSplatsThree })
5 |
6 | export default function LumaWorld({ visible }) {
7 | return (
8 |
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/src/Overlay.js:
--------------------------------------------------------------------------------
1 | export default function Overlay({ inside, setInside }) {
2 | return (
3 | <>
4 |
5 |
6 |
7 |
18 | >
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/src/PostProcessingEffects.js:
--------------------------------------------------------------------------------
1 | import { EffectComposer, Bloom, HueSaturation, Vignette, SMAA, TiltShift2 } from '@react-three/postprocessing'
2 |
3 | export default function PostProcessingEffects() {
4 | return (
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/src/Scene.js:
--------------------------------------------------------------------------------
1 | import { Environment, Lightformer, OrbitControls } from '@react-three/drei'
2 |
3 | export default function SceneSetup({ isMobile }) {
4 | return (
5 | <>
6 |
10 |
11 |
20 |
24 |
25 | self.lookAt(0, 0, 0)}
31 | />
32 | self.lookAt(0, 0, 0)}
35 | position={[-5, 1, -1]}
36 | rotation-y={Math.PI / 2}
37 | scale={[50, 10, 1]}
38 | />
39 | self.lookAt(0, 0, 0)}
42 | position={[10, 1, 0]}
43 | rotation-y={-Math.PI / 2}
44 | scale={[50, 10, 1]}
45 | />
46 |
47 |
48 | >
49 | )
50 | }
51 |
--------------------------------------------------------------------------------
/src/SnowFlakes.js:
--------------------------------------------------------------------------------
1 | import { useFrame } from '@react-three/fiber'
2 | import * as THREE from 'three'
3 | import { useMemo, useRef, Suspense } from 'react'
4 | import { easing } from 'maath'
5 |
6 | export default function SnowFlakes({ count }) {
7 | const mesh = useRef()
8 | const dummy = new THREE.Object3D()
9 |
10 | const particles = useMemo(() => {
11 | const temp = []
12 | for (let i = 0; i < count; i++) {
13 | const phi = Math.acos(-1 + Math.random() * 2)
14 | const theta = Math.random() * 2 * Math.PI
15 |
16 | const radius = 5 + Math.random() * 6.5
17 | const x = radius * Math.sin(phi) * Math.cos(theta)
18 | const y = radius * Math.sin(phi) * Math.sin(theta)
19 | const z = radius * Math.cos(phi)
20 |
21 | const speed = 0.001 + Math.random() / 10
22 |
23 | const scale = Math.cos(0.5 + Math.random() * 2.5)
24 |
25 | temp.push({ phi, theta, radius, speed, x, y, z, mx: 0, my: 0, scale })
26 | }
27 | return temp
28 | }, [count])
29 |
30 | useFrame((state, delta) => {
31 | easing.damp3(mesh.current.rotation, [0, -state.mouse.y * state.viewport.width * 2, 0], 0.75, delta)
32 |
33 | particles.forEach((particle, i) => {
34 | let { phi, theta, radius, speed, scale } = particle
35 | theta += speed / 20
36 |
37 | const x = radius * Math.sin(phi) * Math.cos(theta)
38 | const y = 15 + radius * Math.sin(phi) * Math.sin(theta)
39 | const z = radius * Math.cos(phi)
40 |
41 | particle.phi = phi
42 | particle.theta = theta
43 |
44 | dummy.position.set(x, y, z)
45 | dummy.scale.setScalar(scale)
46 | dummy.rotation.set(phi, theta, 0)
47 | dummy.updateMatrix()
48 |
49 | mesh.current.setMatrixAt(i, dummy.matrix)
50 | })
51 |
52 | mesh.current.instanceMatrix.needsUpdate = true
53 | })
54 |
55 | return (
56 |
57 |
58 |
59 |
60 | )
61 | }
62 |
--------------------------------------------------------------------------------
/src/SnowGlobeModel.js:
--------------------------------------------------------------------------------
1 | /*
2 | Auto-generated by: https://github.com/pmndrs/gltfjsx
3 | Author: MMKH (https://sketchfab.com/mmkh)
4 | License: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
5 | Source: https://sketchfab.com/3d-models/snowglobe-day-11-3dinktober2019-snow-08b4443a71c94647a1298346a1ea1203
6 | Title: Snowglobe - Day 11 #3DInktober2019-Snow
7 | */
8 |
9 | import { useFrame, useThree } from '@react-three/fiber'
10 | import { MeshTransmissionMaterial, useGLTF, useTexture, Text, Billboard, Text3D } from '@react-three/drei'
11 | import * as THREE from 'three'
12 | import { useMemo, useRef, useLayoutEffect, useState, useEffect, memo } from 'react'
13 | import { easing } from 'maath'
14 | import gsap from 'gsap'
15 | import SnowFlakes from './SnowFlakes'
16 | import FireWorks from './FireWorks'
17 | import LumaWorld from './LumaWorld'
18 |
19 | export default function SnowGlobeModel(props) {
20 | const { nodes, materials } = useGLTF('/snowglobe-transformed.glb')
21 | const footer = document.querySelector('.footer')
22 | const snowGlobeRef = useRef()
23 | const snowGlobeRef2 = useRef()
24 | const internalWorldRef = useRef()
25 | const fireWorksRef = useRef()
26 | const [insideMesh, setInsideMesh] = useState(false)
27 |
28 | const groupRef = useRef()
29 | const texture = useTexture('/epic.jpg')
30 | const { camera } = useThree()
31 | const cameraPosition = camera.position
32 | const ray = new THREE.Ray(new THREE.Vector2(0, 0), cameraPosition)
33 | const raycaster = new THREE.Raycaster()
34 |
35 | function useGsapContext(scope) {
36 | const ctx = useMemo(() => gsap.context(() => {}, scope), [scope])
37 | return ctx
38 | }
39 |
40 | const ctx = useGsapContext(snowGlobeRef)
41 |
42 | useLayoutEffect(() => {
43 | gsap.to(camera.position, {
44 | z: props.inside ? 0.1 : 3,
45 | x: props.inside ? 0.1 : 3,
46 | ease: 'power3.inOut',
47 | duration: 1.8
48 | })
49 |
50 | return () => ctx.revert()
51 | }, [props.inside])
52 |
53 | useFrame((state, delta) => {
54 | checkIntersection(snowGlobeRef.current, delta)
55 | })
56 |
57 | const checkIntersection = (object, delta) => {
58 | raycaster.set(cameraPosition, ray.direction)
59 |
60 | const intersections = raycaster.intersectObject(object)
61 |
62 | if (intersections.length > 0) {
63 | setInsideMesh(false)
64 | } else {
65 | setInsideMesh(true)
66 | }
67 | easing.dampC(internalWorldRef.current.material.color, intersections.length > 0 ? 'grey' : 'white', 0.25, delta)
68 | easing.damp(footer.style, 'opacity', intersections.length > 0 ? '0.1' : '1', 0.25, delta)
69 | }
70 |
71 | useEffect(() => {
72 | if (!insideMesh) {
73 | snowGlobeRef2.current.visible = false
74 | snowGlobeRef.current.visible = false
75 | fireWorksRef.current.visible = true
76 | camera.fov = 95
77 | camera.updateProjectionMatrix()
78 | } else {
79 | camera.fov = 65
80 | camera.updateProjectionMatrix()
81 | snowGlobeRef2.current.visible = true
82 | snowGlobeRef.current.visible = true
83 | fireWorksRef.current.visible = false
84 | }
85 | }, [insideMesh])
86 |
87 | return (
88 |
93 |
99 |
113 |
114 |
115 |
119 |
120 |
125 |
126 |
127 |
133 |
139 |
140 |
141 | {!props.isMobile && }
142 |
143 |
144 | )
145 | }
146 | useGLTF.preload('/snowglobe-transformed.glb')
147 |
148 | function Texts() {
149 | return (
150 | <>
151 |
157 | ANDERSON MANCINI
158 |
163 |
164 |
165 |
173 | Wishing you a restful holiday season. {'\n\n'}May your Christmas be blessed with lots of love, peace, and happiness.
174 |
175 |
182 | I will be on vacation until January 8th. Thank you for all your support in 2023. Great things are coming in 2024. Please stay tunned.
183 |
184 |
190 | Anderson Mancini
191 |
192 |
193 | >
194 | )
195 | }
196 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client'
2 | import './styles.css'
3 | import App from './App'
4 |
5 | createRoot(document.getElementById('root')).render()
6 |
--------------------------------------------------------------------------------
/src/styles.css:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | user-select: none;
4 | }
5 |
6 | html,
7 | body,
8 | #root {
9 | width: 100%;
10 | height: 100%;
11 | margin: 0;
12 | padding: 0;
13 | overflow: hidden;
14 | }
15 |
16 | body {
17 | background: #f0f0f0;
18 | }
19 | header {
20 | position: absolute;
21 | top: 0;
22 | left: 50%;
23 | width: 20%;
24 | transform: translateX(-50%);
25 | display: flex;
26 | }
27 | footer {
28 | position: absolute;
29 | bottom: 40px;
30 | color: white;
31 | left: 50%;
32 | transform: translateX(-50%);
33 | font-family: Arial, sans-serif;
34 | font-size: 12px;
35 | text-align: center;
36 | width: 100%;
37 | }
38 |
39 | button {
40 | margin: 20px 0;
41 | padding: 20px 40px;
42 | border-radius: 50px;
43 | border: none;
44 | font-size: calc(1em + 0.690625vw);
45 | font-weight: bold;
46 | cursor: pointer;
47 | transition: all 1.2s ease;
48 | }
49 |
50 | button:hover {
51 | padding: 20px 50px;
52 | color: white;
53 | background-color: black;
54 | }
55 |
56 | @media only screen and (max-width: 600px) {
57 | header {
58 | width: 50%;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------