mirror of
https://github.com/supabase/supabase.git
synced 2026-10-17 15:19:08 +03:00
reset ticket tilt on mouseout
This commit is contained in:
1 parent
71f9a95ffb
commit
6b4697bac5
2 files changed
+90
-123
No files matched your search
@@ -3,55 +3,49 @@
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import * as THREE from 'three'
|
||||
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
|
||||
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js'
|
||||
import { FontLoader } from 'three/examples/jsm/loaders/FontLoader.js'
|
||||
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry.js'
|
||||
import { useParams } from 'common'
|
||||
|
||||
const ThreeCanvas: React.FC<{ username: string }> = ({ username = 'Francesco Sansalvadore' }) => {
|
||||
// Will come from auth
|
||||
const params = useParams()
|
||||
|
||||
const canvasRef = useRef<HTMLDivElement>(null)
|
||||
// const textLines = ['Sansalvadore', 'Francesco']
|
||||
const textLines = params?.username?.split(' ').reverse() || []
|
||||
const orbitRef = useRef<THREE.Object3D | null>(null)
|
||||
const animationFrameRef = useRef<number>()
|
||||
const targetRotation = useRef<{ x: number; y: number }>({ x: 0, y: 0 })
|
||||
const textLines = params?.username?.split(' ').reverse() || username?.split(' ').reverse() || []
|
||||
const LINE_HEIGHT = 1.4
|
||||
|
||||
useEffect(() => {
|
||||
if (!canvasRef.current) return
|
||||
|
||||
const isDesktop = window.innerWidth > 768
|
||||
const isDesktop = () => window.innerWidth > 768
|
||||
const canvasWidth = isDesktop() ? window.innerWidth : window.innerWidth
|
||||
const canvasHeight = window.innerHeight
|
||||
|
||||
// Initialize scene, camera, and renderer
|
||||
const scene = new THREE.Scene()
|
||||
const camera = new THREE.PerspectiveCamera(
|
||||
75,
|
||||
isDesktop
|
||||
? window.innerWidth / 2 / window.innerHeight
|
||||
: window.innerWidth / window.innerHeight,
|
||||
0.1,
|
||||
1000
|
||||
)
|
||||
const camera = new THREE.PerspectiveCamera(75, canvasWidth / canvasHeight, 0.1, 1000)
|
||||
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })
|
||||
renderer.setSize(isDesktop ? window.innerWidth / 2 : window.innerWidth, window.innerHeight)
|
||||
renderer.setSize(canvasWidth, canvasHeight)
|
||||
renderer.shadowMap.enabled = true
|
||||
canvasRef.current.appendChild(renderer.domElement)
|
||||
|
||||
// Controls
|
||||
const controls = new OrbitControls(camera, canvasRef.current)
|
||||
controls.target.set(0, 0.75, 0)
|
||||
controls.autoRotate
|
||||
controls.enableDamping = true
|
||||
controls.enableZoom = false
|
||||
controls.dampingFactor = 0.1
|
||||
controls.minPolarAngle = Math.PI / 2
|
||||
controls.maxPolarAngle = Math.PI / 2
|
||||
// Create orbit object for camera rotation
|
||||
const orbit = new THREE.Object3D()
|
||||
orbit.rotation.order = 'YXZ'
|
||||
scene.add(orbit)
|
||||
orbitRef.current = orbit
|
||||
|
||||
// Camera setup
|
||||
const cameraDistance = 30
|
||||
camera.position.z = cameraDistance
|
||||
orbit.add(camera)
|
||||
|
||||
// Rest of your existing setup code (loader, materials, etc.)
|
||||
const loader = new GLTFLoader()
|
||||
|
||||
// Create a metal material
|
||||
const metalTexture = new THREE.TextureLoader().load('/images/launchweek/13/ticket/2000px.avif') // Load a metal texture image
|
||||
const metalTexture = new THREE.TextureLoader().load('/images/launchweek/13/ticket/2000px.avif')
|
||||
const metalMaterial = new THREE.MeshStandardMaterial({
|
||||
map: metalTexture,
|
||||
color: 0x9ea1a1,
|
||||
@@ -63,34 +57,29 @@ const ThreeCanvas: React.FC<{ username: string }> = ({ username = 'Francesco San
|
||||
const rgbeLoader = new RGBELoader()
|
||||
rgbeLoader.load('/images/launchweek/13/ticket/canary_wharf_1k.hdr', (texture) => {
|
||||
texture.mapping = THREE.EquirectangularReflectionMapping
|
||||
|
||||
scene.environment = texture
|
||||
|
||||
metalMaterial.envMap = texture
|
||||
metalMaterial.envMapIntensity = 7
|
||||
})
|
||||
|
||||
let ticket3DImport: THREE.Mesh
|
||||
|
||||
// loader.load('/images/launchweek/13/ticket/3D-ticket-cutout.glb', (gltf) => {
|
||||
loader.load('/images/launchweek/13/ticket/3D-ticket-cutout-holes.glb', (gltf) => {
|
||||
loader.load('/images/launchweek/13/ticket/3D-ticket-cutout-no-holes.glb', (gltf) => {
|
||||
ticket3DImport = gltf.scene.children[0] as THREE.Mesh
|
||||
ticket3DImport.rotation.x = Math.PI * 0.5
|
||||
|
||||
ticket3DImport.traverse((child) => {
|
||||
if (child) {
|
||||
child.traverse((node) => {
|
||||
// @ts-ignore
|
||||
child.material = metalMaterial
|
||||
child.receiveShadow = true
|
||||
})
|
||||
// @ts-ignore
|
||||
child.material = metalMaterial
|
||||
child.receiveShadow = true
|
||||
}
|
||||
})
|
||||
|
||||
scene.add(ticket3DImport)
|
||||
camera.lookAt(ticket3DImport.position)
|
||||
|
||||
// Load font and add text geometry as a cut-out
|
||||
// Load font and add text geometry
|
||||
const fontLoader = new FontLoader()
|
||||
fontLoader.load('/images/launchweek/13/ticket/Inter_Regular.json', (font) => {
|
||||
const textMaterial = new THREE.MeshStandardMaterial({
|
||||
@@ -109,21 +98,9 @@ const ThreeCanvas: React.FC<{ username: string }> = ({ username = 'Francesco San
|
||||
})
|
||||
const textMesh = new THREE.Mesh(textGeometry, textMaterial)
|
||||
textMesh.updateMatrix()
|
||||
textMesh.position.set(-5.8, -5 + LINE_HEIGHT * (index + 1), -0.2) // Adjust position as needed
|
||||
textMesh.position.set(-5.8, -5 + LINE_HEIGHT * (index + 1), -0.2)
|
||||
textMesh.castShadow = true
|
||||
scene.add(textMesh)
|
||||
|
||||
// if (ticket3DImport) {
|
||||
// ticket3DImport.updateMatrixWorld()
|
||||
// textMesh.updateMatrixWorld()
|
||||
// const csgTicket = CSG.fromMesh(ticket3DImport)
|
||||
// const csgText = CSG.fromMesh(textMesh)
|
||||
|
||||
// const subtractedMesh = CSG.toMesh(csgTicket.subtract(csgText), ticket3DImport.matrix)
|
||||
// subtractedMesh.material = ticket3DImport.material
|
||||
// scene.remove(ticket3DImport) // Remove old ticket mesh
|
||||
// scene.add(subtractedMesh) // Add new ticket with cut-out
|
||||
// }
|
||||
})
|
||||
|
||||
const footerContent = [
|
||||
@@ -147,7 +124,7 @@ const ThreeCanvas: React.FC<{ username: string }> = ({ username = 'Francesco San
|
||||
})
|
||||
const textMesh = new THREE.Mesh(textGeometry, textMaterial)
|
||||
textMesh.updateMatrix()
|
||||
textMesh.position.set(line.position.x, line.position.y, line.position.z) // Adjust position as needed
|
||||
textMesh.position.set(line.position.x, line.position.y, line.position.z)
|
||||
textMesh.castShadow = true
|
||||
scene.add(textMesh)
|
||||
})
|
||||
@@ -155,93 +132,83 @@ const ThreeCanvas: React.FC<{ username: string }> = ({ username = 'Francesco San
|
||||
})
|
||||
|
||||
camera.position.z = 30
|
||||
const ambientLight = new THREE.AmbientLight(0xffffff, 30)
|
||||
// scene.add(ambientLight)
|
||||
|
||||
// Top right light
|
||||
const topRightLight = new THREE.SpotLight(0xffffff, 1)
|
||||
topRightLight.position.set(2, 8, 10)
|
||||
topRightLight.castShadow = true
|
||||
// scene.add(topRightLight)
|
||||
|
||||
// Bottom left light
|
||||
const bottomLeftLight = new THREE.SpotLight(0x44ff4f, 2)
|
||||
bottomLeftLight.position.set(-5, -5, 10)
|
||||
bottomLeftLight.target.position.set(0, 0, 0)
|
||||
bottomLeftLight.castShadow = true
|
||||
// scene.add(bottomLeftLight)
|
||||
|
||||
// const topRightLightHelper = new THREE.SpotLightHelper(topRightLight)
|
||||
// const topRightLightCameraHelper = new THREE.CameraHelper(topRightLight.shadow.camera)
|
||||
// const bottomLeftLightHelper = new THREE.SpotLightHelper(bottomLeftLight)
|
||||
// const bottomLeftLightCameraHelper = new THREE.CameraHelper(bottomLeftLight.shadow.camera)
|
||||
|
||||
// Helpers
|
||||
// scene.add(topRightLightHelper)
|
||||
// scene.add(topRightLightCameraHelper)
|
||||
// scene.add(bottomLeftLightHelper)
|
||||
// scene.add(bottomLeftLightCameraHelper)
|
||||
|
||||
// Left light
|
||||
const leftLight = new THREE.PointLight(0xffffff, 0.015)
|
||||
leftLight.position.set(50, 0, 100)
|
||||
leftLight.castShadow = true
|
||||
// scene.add(leftLight)
|
||||
|
||||
// Right light
|
||||
const rightLight = new THREE.PointLight(0xffffff, 0.015)
|
||||
rightLight.position.set(-50, 0, 100)
|
||||
rightLight.castShadow = true
|
||||
// scene.add(rightLight)
|
||||
|
||||
// Animation loop
|
||||
// Animation function with smooth transitions
|
||||
const animate = () => {
|
||||
requestAnimationFrame(animate)
|
||||
if (orbitRef.current) {
|
||||
// Smooth interpolation
|
||||
orbitRef.current.rotation.x +=
|
||||
(targetRotation.current.x - orbitRef.current.rotation.x) * 0.1
|
||||
orbitRef.current.rotation.y +=
|
||||
(targetRotation.current.y - orbitRef.current.rotation.y) * 0.1
|
||||
orbitRef.current.rotation.z = 0
|
||||
}
|
||||
|
||||
renderer.render(scene, camera)
|
||||
animationFrameRef.current = requestAnimationFrame(animate)
|
||||
}
|
||||
|
||||
// Start animation
|
||||
animate()
|
||||
|
||||
// Mouse movement handler with distance-based sensitivity
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
if (!canvasRef.current || !orbitRef.current) return
|
||||
|
||||
// Get canvas bounds
|
||||
const canvasRect = canvasRef.current.getBoundingClientRect()
|
||||
|
||||
// Calculate center of the canvas
|
||||
const centerX = canvasRect.left + canvasRect.width / 2
|
||||
const centerY = canvasRect.top + canvasRect.height / 2
|
||||
|
||||
// Calculate distance from cursor to center
|
||||
const deltaX = e.clientX - centerX
|
||||
const deltaY = e.clientY - centerY
|
||||
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY)
|
||||
|
||||
// Maximum distance for sensitivity calculation (diagonal of the canvas)
|
||||
const maxDistance = Math.sqrt(
|
||||
Math.pow(canvasRect.width / 2, 2) + Math.pow(canvasRect.height / 2, 2)
|
||||
)
|
||||
|
||||
// Calculate sensitivity based on distance (inverse relationship)
|
||||
const sensitivity = 0.002 * (1 - Math.min(distance / maxDistance, 1))
|
||||
|
||||
// Update target rotation with distance-based sensitivity
|
||||
targetRotation.current.y = deltaX * sensitivity
|
||||
targetRotation.current.x = deltaY * sensitivity * 0.3
|
||||
}
|
||||
|
||||
// Reset handler with smooth transition
|
||||
const resetRotation = () => {
|
||||
targetRotation.current = { x: 0, y: 0 }
|
||||
console.log('out')
|
||||
}
|
||||
|
||||
// Handle window resize
|
||||
const handleResize = () => {
|
||||
const isDesktop = window.innerWidth > 768
|
||||
camera.aspect = isDesktop
|
||||
? window.innerWidth / 2 / window.innerHeight
|
||||
: window.innerWidth / window.innerHeight
|
||||
const newWidth = isDesktop() ? window.innerWidth : window.innerWidth
|
||||
camera.aspect = newWidth / window.innerHeight
|
||||
camera.updateProjectionMatrix()
|
||||
renderer.setSize(isDesktop ? window.innerWidth / 2 : window.innerWidth, window.innerHeight)
|
||||
ticket3DImport.position.x = isDesktop() ? 10 : 0
|
||||
renderer.setSize(newWidth, window.innerHeight)
|
||||
}
|
||||
|
||||
let orbit: THREE.Object3D
|
||||
|
||||
// orbit object on mousemove
|
||||
const handleMouseMoveRotate = (e: any) => {
|
||||
let scale = -0.001
|
||||
orbit.rotateY((e.movementX - 1) * scale)
|
||||
orbit.rotateX(e.movementY * scale * 0.1)
|
||||
orbit.rotation.z = 0
|
||||
// TODO: reset initial rotation when mouse leaves the canvas
|
||||
}
|
||||
|
||||
//the camera rotation pivot
|
||||
orbit = new THREE.Object3D()
|
||||
orbit.rotation.order = 'YXZ' //this is important to keep level, so Z should be the last axis to rotate in order...
|
||||
// orbit.position.copy( ticket.position );
|
||||
scene.add(orbit)
|
||||
|
||||
//offset the camera and add it to the pivot
|
||||
//you could adapt the code so that you can 'zoom' by changing the z value in camera.position in a mousewheel event..
|
||||
let cameraDistance = 30
|
||||
camera.position.z = cameraDistance
|
||||
orbit.add(camera)
|
||||
|
||||
// Event listeners
|
||||
window.addEventListener('resize', handleResize)
|
||||
window.addEventListener('mousemove', handleMouseMoveRotate)
|
||||
window.addEventListener('mousemove', handleMouseMove)
|
||||
window.addEventListener('mouseout', resetRotation)
|
||||
|
||||
// Cleanup on unmount
|
||||
// Cleanup
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize)
|
||||
window.removeEventListener('mousemove', handleMouseMoveRotate)
|
||||
window.removeEventListener('mousemove', handleMouseMove)
|
||||
window.removeEventListener('mouseout', resetRotation)
|
||||
|
||||
if (animationFrameRef.current) {
|
||||
cancelAnimationFrame(animationFrameRef.current)
|
||||
}
|
||||
canvasRef.current?.removeChild(renderer.domElement)
|
||||
renderer.dispose()
|
||||
}
|
||||
@@ -249,7 +216,7 @@ const ThreeCanvas: React.FC<{ username: string }> = ({ username = 'Francesco San
|
||||
|
||||
return (
|
||||
<div className="relative flex justify-end items-center max-h-screen bg-alternative overflow-hidden">
|
||||
<div ref={canvasRef} className="w-full md:w-1/2 h-full" />
|
||||
<div ref={canvasRef} className="w-full h-full" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Binary file not shown.
Reference in new issue
Block a user