1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
|
import React, { useRef, useEffect } from 'react'
import * as THREE from 'three'
interface CityscapeBackgroundProps {
className?: string
}
export function CityscapeBackground({ className }: CityscapeBackgroundProps) {
const canvasRef = useRef<HTMLCanvasElement>(null)
const sceneRef = useRef<THREE.Scene>()
const rendererRef = useRef<THREE.WebGLRenderer>()
const cameraRef = useRef<THREE.PerspectiveCamera>()
const animationIdRef = useRef<number>()
const speedLinesRef = useRef<THREE.LineSegments>()
const mouseRef = useRef(new THREE.Vector2())
const startTimeRef = useRef<number>(Date.now())
useEffect(() => {
if (!canvasRef.current) {
console.log('CityscapeBackground: Canvas ref not available')
return
}
console.log('FuturistBackground: Initializing 3D futurist scene')
// Scene setup with futurist art movement atmosphere
const scene = new THREE.Scene()
scene.background = new THREE.Color(0x000000) // Black background
scene.fog = new THREE.Fog(0x000000, 30, 120) // Black fog for depth
// Camera setup for isometric view
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 200)
camera.position.set(30, 25, 30)
camera.lookAt(0, 0, 0)
// Renderer setup
const renderer = new THREE.WebGLRenderer({
canvas: canvasRef.current,
antialias: false, // Disable for better performance
alpha: false // Disable alpha for opaque background
})
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 1.5)) // Limit pixel ratio for performance
// Store references
sceneRef.current = scene
rendererRef.current = renderer
cameraRef.current = camera
// Futurist art movement lighting - dramatic contrasts inspired by paintings
const ambientLight = new THREE.AmbientLight(0x1a1a1a, 0.4) // Warm grey ambient
scene.add(ambientLight)
// Bold primary light - warm like industrial sunlight
const primaryLight = new THREE.DirectionalLight(0xffaa33, 1.8) // Golden orange
primaryLight.position.set(-25, 35, 20)
scene.add(primaryLight)
// Dynamic red light for energy and motion
const dynamicLight = new THREE.DirectionalLight(0xdd2222, 1.2) // Bold red
dynamicLight.position.set(20, 10, -15)
scene.add(dynamicLight)
// Contrasting blue for depth and drama
const contrastLight = new THREE.PointLight(0x2244bb, 0.8, 50) // Deep blue
contrastLight.position.set(-30, 5, 0)
scene.add(contrastLight)
// Yellow accent for vibrant highlights
const accentLight = new THREE.PointLight(0xdddd22, 0.7, 40) // Yellow
accentLight.position.set(25, 20, -5)
scene.add(accentLight)
// Green industrial light
const industrialLight = new THREE.PointLight(0x22aa22, 0.6, 45) // Industrial green
industrialLight.position.set(0, -5, 30)
scene.add(industrialLight)
// Create speed lines
createSpeedLines()
console.log('FuturistBackground: Dynamic futurist art created, starting animation')
// Reset start time
startTimeRef.current = Date.now()
// Animation loop
const animate = () => {
animationIdRef.current = requestAnimationFrame(animate)
const time = Date.now() * 0.001 // Convert to seconds for smoother animation
// Animate speed lines
animateSpeedLines(time)
// Apply interactive effects
applyFuturistInteractions()
// Dramatic camera movement inspired by futurist dynamism
const cameraSpeed = time * 0.2
camera.position.x = 20 + Math.sin(cameraSpeed * 1.3) * 12 + Math.cos(cameraSpeed * 0.7) * 6
camera.position.y = 15 + Math.cos(cameraSpeed * 0.9) * 8 + Math.sin(cameraSpeed * 1.1) * 4
camera.position.z = 25 + Math.sin(cameraSpeed * 0.6) * 15 + Math.cos(cameraSpeed * 1.4) * 8
// Dynamic look-at point for more dramatic perspective shifts
const lookAtTarget = new THREE.Vector3(
Math.sin(cameraSpeed * 0.8) * 5,
Math.cos(cameraSpeed * 0.5) * 3,
Math.sin(cameraSpeed * 1.2) * 8
)
camera.lookAt(lookAtTarget)
renderer.render(scene, camera)
}
animate()
// Mouse tracking for interactive effects
const handleMouseMove = (event: MouseEvent) => {
// Convert screen coordinates to normalized device coordinates (-1 to +1)
mouseRef.current.set(
(event.clientX / window.innerWidth) * 2 - 1,
-(event.clientY / window.innerHeight) * 2 + 1
)
}
// Handle window resize
const handleResize = () => {
if (camera && renderer) {
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth, window.innerHeight)
}
}
window.addEventListener('mousemove', handleMouseMove)
window.addEventListener('resize', handleResize)
// Cleanup
return () => {
if (animationIdRef.current) {
cancelAnimationFrame(animationIdRef.current)
}
window.removeEventListener('mousemove', handleMouseMove)
window.removeEventListener('resize', handleResize)
// Dispose of Three.js objects
scene.clear()
renderer.dispose()
}
}, [])
const applyFuturistInteractions = () => {
const camera = cameraRef.current!
const raycaster = new THREE.Raycaster()
// Convert mouse coordinates to world position
raycaster.setFromCamera(mouseRef.current, camera)
// Interactive speed line distortion
if (speedLinesRef.current) {
const positions = speedLinesRef.current.geometry.attributes.position.array as Float32Array
for (let i = 0; i < positions.length; i += 6) { // Step by 6 for line pairs
const linePos = new THREE.Vector3(positions[i], positions[i + 1], positions[i + 2])
const mouseWorldPos = new THREE.Vector3()
raycaster.ray.at(linePos.z, mouseWorldPos)
const distance = linePos.distanceTo(mouseWorldPos)
const distortRadius = 15
if (distance < distortRadius) {
const distortStrength = (distortRadius - distance) / distortRadius
// Create dramatic wave distortion around mouse
const waveX = Math.sin(Date.now() * 0.02 + i) * distortStrength * 2.0
const waveY = Math.cos(Date.now() * 0.025 + i) * distortStrength * 1.5
positions[i] += waveX
positions[i + 1] += waveY
positions[i + 3] += waveX * 0.8 // End point follows with slight lag
positions[i + 4] += waveY * 0.8
}
}
speedLinesRef.current.geometry.attributes.position.needsUpdate = true
}
}
const createSpeedLines = () => {
const scene = sceneRef.current!
// Create sharp, dramatic speed lines
const motionVertices = []
const motionColors = []
// High-contrast speed colors
const speedColors = [
{ r: 1.0, g: 1.0, b: 1.0 }, // Brilliant white
{ r: 1.0, g: 0.1, b: 0.1 }, // Sharp red
{ r: 1.0, g: 0.8, b: 0.0 }, // Electric yellow
{ r: 0.0, g: 1.0, b: 1.0 }, // Cyan
{ r: 1.0, g: 0.0, b: 1.0 }, // Magenta
]
// Create 300 sharp speed lines for intense motion
for (let i = 0; i < 300; i++) {
// Start point - spread across space
const startX = (Math.random() - 0.5) * 100
const startY = (Math.random() - 0.5) * 40
const startZ = Math.random() * -200
// End point - long streaks suggesting extreme speed
const endX = startX + (Math.random() - 0.5) * 20
const endY = startY + (Math.random() - 0.5) * 8
const endZ = startZ + 20 + Math.random() * 40 // Longer streaks
motionVertices.push(startX, startY, startZ)
motionVertices.push(endX, endY, endZ)
// High-contrast colors for sharp appearance
const colorIndex = Math.floor(Math.random() * speedColors.length)
const color = speedColors[colorIndex]
motionColors.push(color.r, color.g, color.b)
motionColors.push(color.r, color.g, color.b)
}
const motionGeometry = new THREE.BufferGeometry()
motionGeometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(motionVertices), 3))
motionGeometry.setAttribute('color', new THREE.BufferAttribute(new Float32Array(motionColors), 3))
const motionMaterial = new THREE.LineBasicMaterial({
vertexColors: true,
transparent: true,
opacity: 0.9,
linewidth: 2
})
const speedLines = new THREE.LineSegments(motionGeometry, motionMaterial)
speedLinesRef.current = speedLines
scene.add(speedLines)
}
const animateSpeedLines = (time: number) => {
if (!speedLinesRef.current) return
const positions = speedLinesRef.current.geometry.attributes.position.array as Float32Array
for (let i = 0; i < positions.length; i += 6) { // Step by 6 since we have line pairs
// Extreme speed effect - lines blazing toward camera
positions[i + 2] += 6.0 + Math.sin(time * 2 + i) * 3.0 // High variable speed
positions[i + 5] += 6.0 + Math.sin(time * 2 + i) * 3.0 // End point matches
// Intense lateral movement for speed blur
const lateralMotion = Math.sin(time * 1.5 + i * 0.15) * 0.8
positions[i] += lateralMotion
positions[i + 3] += lateralMotion
// Sharp vertical oscillation for dynamic energy
const verticalMotion = Math.cos(time * 2.2 + i * 0.12) * 0.6
positions[i + 1] += verticalMotion
positions[i + 4] += verticalMotion
// Reset lines that have blazed past camera
if (positions[i + 2] > 100) {
positions[i + 2] = -250 - Math.random() * 100
positions[i + 5] = positions[i + 2] + 20 + Math.random() * 40
positions[i] = (Math.random() - 0.5) * 100
positions[i + 1] = (Math.random() - 0.5) * 40
positions[i + 3] = positions[i] + (Math.random() - 0.5) * 20
positions[i + 4] = positions[i + 1] + (Math.random() - 0.5) * 8
}
}
speedLinesRef.current.geometry.attributes.position.needsUpdate = true
// Rapid rotation for intense motion blur
speedLinesRef.current.rotation.x += 0.008
speedLinesRef.current.rotation.y += 0.012
speedLinesRef.current.rotation.z += 0.025
}
return (
<canvas
ref={canvasRef}
className={className}
style={{
position: 'fixed',
top: 0,
left: 0,
width: '100%',
height: '100%',
zIndex: 0,
pointerEvents: 'auto',
opacity: 1,
visibility: 'visible',
}}
/>
)
}
|