feat: reworked HeroSection background
1
build/assets/index-BAFm1LaZ.css
Normal file
128
build/assets/index-VO0wVEDp.js
Normal file
9
build/assets/three-eMwtdKkp.js
Normal file
BIN
build/favicon.ico
Normal file
|
After Width: | Height: | Size: 22 KiB |
@@ -1,5 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
|
||||||
<rect width="100" height="100" rx="20" fill="#0F1410"/>
|
|
||||||
<text x="50" y="62" font-family="system-ui, sans-serif" font-size="36" font-weight="700" text-anchor="middle" fill="#E1E3DF">R</text>
|
|
||||||
<text x="68" y="62" font-family="system-ui, sans-serif" font-size="36" font-weight="700" text-anchor="middle" fill="#7FD998">IT</text>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 401 B |
@@ -2,7 +2,7 @@
|
|||||||
<html lang="de">
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="description" content="Ragusa IT-Consulting - Professionelle Webentwicklung, IT-Beratung und technischer Support" />
|
<meta name="description" content="Ragusa IT-Consulting - Professionelle Webentwicklung, IT-Beratung und technischer Support" />
|
||||||
<meta name="theme-color" content="#0F1410" />
|
<meta name="theme-color" content="#0F1410" />
|
||||||
@@ -10,10 +10,10 @@
|
|||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||||
<script type="module" crossorigin src="/assets/index-DpUrPOvK.js"></script>
|
<script type="module" crossorigin src="/assets/index-VO0wVEDp.js"></script>
|
||||||
<link rel="modulepreload" crossorigin href="/assets/three-A7r-9XvZ.js">
|
<link rel="modulepreload" crossorigin href="/assets/three-eMwtdKkp.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/motion-BIOHP8Ul.js">
|
<link rel="modulepreload" crossorigin href="/assets/motion-BIOHP8Ul.js">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-D4N8_we_.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-BAFm1LaZ.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
BIN
build/logo.png
Normal file
|
After Width: | Height: | Size: 171 KiB |
9
build/logo.svg
Normal file
|
After Width: | Height: | Size: 224 KiB |
@@ -2,7 +2,7 @@
|
|||||||
<html lang="de">
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="description" content="Ragusa IT-Consulting - Professionelle Webentwicklung, IT-Beratung und technischer Support" />
|
<meta name="description" content="Ragusa IT-Consulting - Professionelle Webentwicklung, IT-Beratung und technischer Support" />
|
||||||
<meta name="theme-color" content="#0F1410" />
|
<meta name="theme-color" content="#0F1410" />
|
||||||
|
|||||||
@@ -18,7 +18,9 @@
|
|||||||
"@emailjs/browser": "^4.4.1",
|
"@emailjs/browser": "^4.4.1",
|
||||||
"@react-three/drei": "^10.7.7",
|
"@react-three/drei": "^10.7.7",
|
||||||
"@react-three/fiber": "^9.5.0",
|
"@react-three/fiber": "^9.5.0",
|
||||||
|
"@react-three/rapier": "^2.2.0",
|
||||||
"motion": "^12.28.1",
|
"motion": "^12.28.1",
|
||||||
|
"ogl": "^1.0.11",
|
||||||
"react": "^19.2.3",
|
"react": "^19.2.3",
|
||||||
"react-dom": "^19.2.3",
|
"react-dom": "^19.2.3",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
|
|||||||
44
pnpm-lock.yaml
generated
@@ -17,15 +17,24 @@ importers:
|
|||||||
'@react-three/fiber':
|
'@react-three/fiber':
|
||||||
specifier: ^9.5.0
|
specifier: ^9.5.0
|
||||||
version: 9.5.0(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(three@0.182.0)
|
version: 9.5.0(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(three@0.182.0)
|
||||||
|
'@react-three/rapier':
|
||||||
|
specifier: ^2.2.0
|
||||||
|
version: 2.2.0(@react-three/fiber@9.5.0(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(three@0.182.0))(react@19.2.3)(three@0.182.0)
|
||||||
motion:
|
motion:
|
||||||
specifier: ^12.28.1
|
specifier: ^12.28.1
|
||||||
version: 12.28.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
version: 12.28.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||||
|
ogl:
|
||||||
|
specifier: ^1.0.11
|
||||||
|
version: 1.0.11
|
||||||
react:
|
react:
|
||||||
specifier: ^19.2.3
|
specifier: ^19.2.3
|
||||||
version: 19.2.3
|
version: 19.2.3
|
||||||
react-dom:
|
react-dom:
|
||||||
specifier: ^19.2.3
|
specifier: ^19.2.3
|
||||||
version: 19.2.3(react@19.2.3)
|
version: 19.2.3(react@19.2.3)
|
||||||
|
react-icons:
|
||||||
|
specifier: ^5.5.0
|
||||||
|
version: 5.5.0(react@19.2.3)
|
||||||
react-router-dom:
|
react-router-dom:
|
||||||
specifier: ^7.12.0
|
specifier: ^7.12.0
|
||||||
version: 7.12.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
version: 7.12.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||||
@@ -144,6 +153,9 @@ packages:
|
|||||||
'@dimforge/rapier3d-compat@0.12.0':
|
'@dimforge/rapier3d-compat@0.12.0':
|
||||||
resolution: {integrity: sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==}
|
resolution: {integrity: sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==}
|
||||||
|
|
||||||
|
'@dimforge/rapier3d-compat@0.19.2':
|
||||||
|
resolution: {integrity: sha512-AZHL1jqUF55QJkJyU1yKeh4ImX2J93bVLIezT1+o0FZqTix6O06MOaqpKoJ4MmbDCsoZmwO+qc471/SDMDm2AA==}
|
||||||
|
|
||||||
'@emailjs/browser@4.4.1':
|
'@emailjs/browser@4.4.1':
|
||||||
resolution: {integrity: sha512-DGSlP9sPvyFba3to2A50kDtZ+pXVp/0rhmqs2LmbMS3I5J8FSOgLwzY2Xb4qfKlOVHh29EAutLYwe5yuEZmEFg==}
|
resolution: {integrity: sha512-DGSlP9sPvyFba3to2A50kDtZ+pXVp/0rhmqs2LmbMS3I5J8FSOgLwzY2Xb4qfKlOVHh29EAutLYwe5yuEZmEFg==}
|
||||||
engines: {node: '>=14.0.0'}
|
engines: {node: '>=14.0.0'}
|
||||||
@@ -364,6 +376,13 @@ packages:
|
|||||||
react-native:
|
react-native:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@react-three/rapier@2.2.0':
|
||||||
|
resolution: {integrity: sha512-mVsqbKXlGZoN+XrqdhzFZUQmy8pibEOVzl4k7LC+LHe84bQnYBSagy1Hvbda6bL1PJDdTFyiDiBk5buKFinNIQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@react-three/fiber': ^9.0.4
|
||||||
|
react: ^19
|
||||||
|
three: '>=0.159.0'
|
||||||
|
|
||||||
'@rolldown/pluginutils@1.0.0-beta.53':
|
'@rolldown/pluginutils@1.0.0-beta.53':
|
||||||
resolution: {integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==}
|
resolution: {integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==}
|
||||||
|
|
||||||
@@ -753,6 +772,9 @@ packages:
|
|||||||
node-releases@2.0.27:
|
node-releases@2.0.27:
|
||||||
resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
|
resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
|
||||||
|
|
||||||
|
ogl@1.0.11:
|
||||||
|
resolution: {integrity: sha512-kUpC154AFfxi16pmZUK4jk3J+8zxwTWGPo03EoYA8QPbzikHoaC82n6pNTbd+oEaJonaE8aPWBlX7ad9zrqLsA==}
|
||||||
|
|
||||||
path-key@3.1.1:
|
path-key@3.1.1:
|
||||||
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
|
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -779,6 +801,11 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^19.2.3
|
react: ^19.2.3
|
||||||
|
|
||||||
|
react-icons@5.5.0:
|
||||||
|
resolution: {integrity: sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==}
|
||||||
|
peerDependencies:
|
||||||
|
react: '*'
|
||||||
|
|
||||||
react-refresh@0.18.0:
|
react-refresh@0.18.0:
|
||||||
resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==}
|
resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -1119,6 +1146,8 @@ snapshots:
|
|||||||
|
|
||||||
'@dimforge/rapier3d-compat@0.12.0': {}
|
'@dimforge/rapier3d-compat@0.12.0': {}
|
||||||
|
|
||||||
|
'@dimforge/rapier3d-compat@0.19.2': {}
|
||||||
|
|
||||||
'@emailjs/browser@4.4.1': {}
|
'@emailjs/browser@4.4.1': {}
|
||||||
|
|
||||||
'@esbuild/aix-ppc64@0.27.2':
|
'@esbuild/aix-ppc64@0.27.2':
|
||||||
@@ -1278,6 +1307,15 @@ snapshots:
|
|||||||
- '@types/react'
|
- '@types/react'
|
||||||
- immer
|
- immer
|
||||||
|
|
||||||
|
'@react-three/rapier@2.2.0(@react-three/fiber@9.5.0(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(three@0.182.0))(react@19.2.3)(three@0.182.0)':
|
||||||
|
dependencies:
|
||||||
|
'@dimforge/rapier3d-compat': 0.19.2
|
||||||
|
'@react-three/fiber': 9.5.0(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(three@0.182.0)
|
||||||
|
react: 19.2.3
|
||||||
|
suspend-react: 0.1.3(react@19.2.3)
|
||||||
|
three: 0.182.0
|
||||||
|
three-stdlib: 2.36.1(three@0.182.0)
|
||||||
|
|
||||||
'@rolldown/pluginutils@1.0.0-beta.53': {}
|
'@rolldown/pluginutils@1.0.0-beta.53': {}
|
||||||
|
|
||||||
'@rollup/rollup-android-arm-eabi@4.55.3':
|
'@rollup/rollup-android-arm-eabi@4.55.3':
|
||||||
@@ -1603,6 +1641,8 @@ snapshots:
|
|||||||
|
|
||||||
node-releases@2.0.27: {}
|
node-releases@2.0.27: {}
|
||||||
|
|
||||||
|
ogl@1.0.11: {}
|
||||||
|
|
||||||
path-key@3.1.1: {}
|
path-key@3.1.1: {}
|
||||||
|
|
||||||
picocolors@1.1.1: {}
|
picocolors@1.1.1: {}
|
||||||
@@ -1627,6 +1667,10 @@ snapshots:
|
|||||||
react: 19.2.3
|
react: 19.2.3
|
||||||
scheduler: 0.27.0
|
scheduler: 0.27.0
|
||||||
|
|
||||||
|
react-icons@5.5.0(react@19.2.3):
|
||||||
|
dependencies:
|
||||||
|
react: 19.2.3
|
||||||
|
|
||||||
react-refresh@0.18.0: {}
|
react-refresh@0.18.0: {}
|
||||||
|
|
||||||
react-router-dom@7.12.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
|
react-router-dom@7.12.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
|
||||||
|
|||||||
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 22 KiB |
@@ -1,5 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
|
||||||
<rect width="100" height="100" rx="20" fill="#0F1410"/>
|
|
||||||
<text x="50" y="62" font-family="system-ui, sans-serif" font-size="36" font-weight="700" text-anchor="middle" fill="#E1E3DF">R</text>
|
|
||||||
<text x="68" y="62" font-family="system-ui, sans-serif" font-size="36" font-weight="700" text-anchor="middle" fill="#7FD998">IT</text>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 401 B |
BIN
public/logo.png
Normal file
|
After Width: | Height: | Size: 171 KiB |
9
public/logo.svg
Normal file
|
After Width: | Height: | Size: 224 KiB |
6
src/components/effects/GradientBlinds.css
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
.gradient-blinds-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
389
src/components/effects/GradientBlinds.tsx
Normal file
@@ -0,0 +1,389 @@
|
|||||||
|
import React, { useEffect, useRef } from 'react';
|
||||||
|
import { Renderer, Program, Mesh, Triangle } from 'ogl';
|
||||||
|
import './GradientBlinds.css';
|
||||||
|
|
||||||
|
export interface GradientBlindsProps {
|
||||||
|
className?: string;
|
||||||
|
dpr?: number;
|
||||||
|
paused?: boolean;
|
||||||
|
gradientColors?: string[];
|
||||||
|
angle?: number;
|
||||||
|
noise?: number;
|
||||||
|
blindCount?: number;
|
||||||
|
blindMinWidth?: number;
|
||||||
|
mouseDampening?: number;
|
||||||
|
mirrorGradient?: boolean;
|
||||||
|
spotlightRadius?: number;
|
||||||
|
spotlightSoftness?: number;
|
||||||
|
spotlightOpacity?: number;
|
||||||
|
distortAmount?: number;
|
||||||
|
shineDirection?: 'left' | 'right';
|
||||||
|
mixBlendMode?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAX_COLORS = 8;
|
||||||
|
const hexToRGB = (hex: string): [number, number, number] => {
|
||||||
|
const c = hex.replace('#', '').padEnd(6, '0');
|
||||||
|
const r = parseInt(c.slice(0, 2), 16) / 255;
|
||||||
|
const g = parseInt(c.slice(2, 4), 16) / 255;
|
||||||
|
const b = parseInt(c.slice(4, 6), 16) / 255;
|
||||||
|
return [r, g, b];
|
||||||
|
};
|
||||||
|
const prepStops = (stops?: string[]) => {
|
||||||
|
const base = (stops && stops.length ? stops : ['#FF9FFC', '#5227FF']).slice(0, MAX_COLORS);
|
||||||
|
if (base.length === 1) base.push(base[0]);
|
||||||
|
while (base.length < MAX_COLORS) base.push(base[base.length - 1]);
|
||||||
|
const arr: [number, number, number][] = [];
|
||||||
|
for (let i = 0; i < MAX_COLORS; i++) arr.push(hexToRGB(base[i]));
|
||||||
|
const count = Math.max(2, Math.min(MAX_COLORS, stops?.length ?? 2));
|
||||||
|
return { arr, count };
|
||||||
|
};
|
||||||
|
|
||||||
|
const GradientBlinds: React.FC<GradientBlindsProps> = ({
|
||||||
|
className,
|
||||||
|
dpr,
|
||||||
|
paused = false,
|
||||||
|
gradientColors,
|
||||||
|
angle = 0,
|
||||||
|
noise = 0.3,
|
||||||
|
blindCount = 16,
|
||||||
|
blindMinWidth = 60,
|
||||||
|
mouseDampening = 0.15,
|
||||||
|
mirrorGradient = false,
|
||||||
|
spotlightRadius = 0.5,
|
||||||
|
spotlightSoftness = 1,
|
||||||
|
spotlightOpacity = 1,
|
||||||
|
distortAmount = 0,
|
||||||
|
shineDirection = 'left',
|
||||||
|
mixBlendMode = 'lighten'
|
||||||
|
}) => {
|
||||||
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
const rafRef = useRef<number | null>(null);
|
||||||
|
const programRef = useRef<Program | null>(null);
|
||||||
|
const meshRef = useRef<Mesh<Triangle> | null>(null);
|
||||||
|
const geometryRef = useRef<Triangle | null>(null);
|
||||||
|
const rendererRef = useRef<Renderer | null>(null);
|
||||||
|
const mouseTargetRef = useRef<[number, number]>([0, 0]);
|
||||||
|
const lastTimeRef = useRef<number>(0);
|
||||||
|
const firstResizeRef = useRef<boolean>(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const container = containerRef.current;
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
const renderer = new Renderer({
|
||||||
|
dpr: dpr ?? (typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1),
|
||||||
|
alpha: true,
|
||||||
|
antialias: true
|
||||||
|
});
|
||||||
|
rendererRef.current = renderer;
|
||||||
|
const gl = renderer.gl;
|
||||||
|
const canvas = gl.canvas as HTMLCanvasElement;
|
||||||
|
|
||||||
|
canvas.style.width = '100%';
|
||||||
|
canvas.style.height = '100%';
|
||||||
|
canvas.style.display = 'block';
|
||||||
|
container.appendChild(canvas);
|
||||||
|
|
||||||
|
const vertex = `
|
||||||
|
attribute vec2 position;
|
||||||
|
attribute vec2 uv;
|
||||||
|
varying vec2 vUv;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vUv = uv;
|
||||||
|
gl_Position = vec4(position, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const fragment = `
|
||||||
|
#ifdef GL_ES
|
||||||
|
precision mediump float;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uniform vec3 iResolution;
|
||||||
|
uniform vec2 iMouse;
|
||||||
|
uniform float iTime;
|
||||||
|
|
||||||
|
uniform float uAngle;
|
||||||
|
uniform float uNoise;
|
||||||
|
uniform float uBlindCount;
|
||||||
|
uniform float uSpotlightRadius;
|
||||||
|
uniform float uSpotlightSoftness;
|
||||||
|
uniform float uSpotlightOpacity;
|
||||||
|
uniform float uMirror;
|
||||||
|
uniform float uDistort;
|
||||||
|
uniform float uShineFlip;
|
||||||
|
uniform vec3 uColor0;
|
||||||
|
uniform vec3 uColor1;
|
||||||
|
uniform vec3 uColor2;
|
||||||
|
uniform vec3 uColor3;
|
||||||
|
uniform vec3 uColor4;
|
||||||
|
uniform vec3 uColor5;
|
||||||
|
uniform vec3 uColor6;
|
||||||
|
uniform vec3 uColor7;
|
||||||
|
uniform int uColorCount;
|
||||||
|
|
||||||
|
varying vec2 vUv;
|
||||||
|
|
||||||
|
float rand(vec2 co){
|
||||||
|
return fract(sin(dot(co, vec2(12.9898,78.233))) * 43758.5453);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 rotate2D(vec2 p, float a){
|
||||||
|
float c = cos(a);
|
||||||
|
float s = sin(a);
|
||||||
|
return mat2(c, -s, s, c) * p;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 getGradientColor(float t){
|
||||||
|
float tt = clamp(t, 0.0, 1.0);
|
||||||
|
int count = uColorCount;
|
||||||
|
if (count < 2) count = 2;
|
||||||
|
float scaled = tt * float(count - 1);
|
||||||
|
float seg = floor(scaled);
|
||||||
|
float f = fract(scaled);
|
||||||
|
|
||||||
|
if (seg < 1.0) return mix(uColor0, uColor1, f);
|
||||||
|
if (seg < 2.0 && count > 2) return mix(uColor1, uColor2, f);
|
||||||
|
if (seg < 3.0 && count > 3) return mix(uColor2, uColor3, f);
|
||||||
|
if (seg < 4.0 && count > 4) return mix(uColor3, uColor4, f);
|
||||||
|
if (seg < 5.0 && count > 5) return mix(uColor4, uColor5, f);
|
||||||
|
if (seg < 6.0 && count > 6) return mix(uColor5, uColor6, f);
|
||||||
|
if (seg < 7.0 && count > 7) return mix(uColor6, uColor7, f);
|
||||||
|
if (count > 7) return uColor7;
|
||||||
|
if (count > 6) return uColor6;
|
||||||
|
if (count > 5) return uColor5;
|
||||||
|
if (count > 4) return uColor4;
|
||||||
|
if (count > 3) return uColor3;
|
||||||
|
if (count > 2) return uColor2;
|
||||||
|
return uColor1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mainImage( out vec4 fragColor, in vec2 fragCoord )
|
||||||
|
{
|
||||||
|
vec2 uv0 = fragCoord.xy / iResolution.xy;
|
||||||
|
|
||||||
|
float aspect = iResolution.x / iResolution.y;
|
||||||
|
vec2 p = uv0 * 2.0 - 1.0;
|
||||||
|
p.x *= aspect;
|
||||||
|
vec2 pr = rotate2D(p, uAngle);
|
||||||
|
pr.x /= aspect;
|
||||||
|
vec2 uv = pr * 0.5 + 0.5;
|
||||||
|
|
||||||
|
vec2 uvMod = uv;
|
||||||
|
if (uDistort > 0.0) {
|
||||||
|
float a = uvMod.y * 6.0;
|
||||||
|
float b = uvMod.x * 6.0;
|
||||||
|
float w = 0.01 * uDistort;
|
||||||
|
uvMod.x += sin(a) * w;
|
||||||
|
uvMod.y += cos(b) * w;
|
||||||
|
}
|
||||||
|
float t = uvMod.x;
|
||||||
|
if (uMirror > 0.5) {
|
||||||
|
t = 1.0 - abs(1.0 - 2.0 * fract(t));
|
||||||
|
}
|
||||||
|
vec3 base = getGradientColor(t);
|
||||||
|
|
||||||
|
vec2 offset = vec2(iMouse.x/iResolution.x, iMouse.y/iResolution.y);
|
||||||
|
float d = length(uv0 - offset);
|
||||||
|
float r = max(uSpotlightRadius, 1e-4);
|
||||||
|
float dn = d / r;
|
||||||
|
float spot = (1.0 - 2.0 * pow(dn, uSpotlightSoftness)) * uSpotlightOpacity;
|
||||||
|
vec3 cir = vec3(spot);
|
||||||
|
float stripe = fract(uvMod.x * max(uBlindCount, 1.0));
|
||||||
|
if (uShineFlip > 0.5) stripe = 1.0 - stripe;
|
||||||
|
vec3 ran = vec3(stripe);
|
||||||
|
|
||||||
|
vec3 col = cir + base - ran;
|
||||||
|
col += (rand(gl_FragCoord.xy + iTime) - 0.5) * uNoise;
|
||||||
|
|
||||||
|
fragColor = vec4(col, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 color;
|
||||||
|
mainImage(color, vUv * iResolution.xy);
|
||||||
|
gl_FragColor = color;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const { arr: colorArr, count: colorCount } = prepStops(gradientColors);
|
||||||
|
const uniforms: {
|
||||||
|
iResolution: { value: [number, number, number] };
|
||||||
|
iMouse: { value: [number, number] };
|
||||||
|
iTime: { value: number };
|
||||||
|
uAngle: { value: number };
|
||||||
|
uNoise: { value: number };
|
||||||
|
uBlindCount: { value: number };
|
||||||
|
uSpotlightRadius: { value: number };
|
||||||
|
uSpotlightSoftness: { value: number };
|
||||||
|
uSpotlightOpacity: { value: number };
|
||||||
|
uMirror: { value: number };
|
||||||
|
uDistort: { value: number };
|
||||||
|
uShineFlip: { value: number };
|
||||||
|
uColor0: { value: [number, number, number] };
|
||||||
|
uColor1: { value: [number, number, number] };
|
||||||
|
uColor2: { value: [number, number, number] };
|
||||||
|
uColor3: { value: [number, number, number] };
|
||||||
|
uColor4: { value: [number, number, number] };
|
||||||
|
uColor5: { value: [number, number, number] };
|
||||||
|
uColor6: { value: [number, number, number] };
|
||||||
|
uColor7: { value: [number, number, number] };
|
||||||
|
uColorCount: { value: number };
|
||||||
|
} = {
|
||||||
|
iResolution: {
|
||||||
|
value: [gl.drawingBufferWidth, gl.drawingBufferHeight, 1]
|
||||||
|
},
|
||||||
|
iMouse: { value: [0, 0] },
|
||||||
|
iTime: { value: 0 },
|
||||||
|
uAngle: { value: (angle * Math.PI) / 180 },
|
||||||
|
uNoise: { value: noise },
|
||||||
|
uBlindCount: { value: Math.max(1, blindCount) },
|
||||||
|
uSpotlightRadius: { value: spotlightRadius },
|
||||||
|
uSpotlightSoftness: { value: spotlightSoftness },
|
||||||
|
uSpotlightOpacity: { value: spotlightOpacity },
|
||||||
|
uMirror: { value: mirrorGradient ? 1 : 0 },
|
||||||
|
uDistort: { value: distortAmount },
|
||||||
|
uShineFlip: { value: shineDirection === 'right' ? 1 : 0 },
|
||||||
|
uColor0: { value: colorArr[0] },
|
||||||
|
uColor1: { value: colorArr[1] },
|
||||||
|
uColor2: { value: colorArr[2] },
|
||||||
|
uColor3: { value: colorArr[3] },
|
||||||
|
uColor4: { value: colorArr[4] },
|
||||||
|
uColor5: { value: colorArr[5] },
|
||||||
|
uColor6: { value: colorArr[6] },
|
||||||
|
uColor7: { value: colorArr[7] },
|
||||||
|
uColorCount: { value: colorCount }
|
||||||
|
};
|
||||||
|
|
||||||
|
const program = new Program(gl, {
|
||||||
|
vertex,
|
||||||
|
fragment,
|
||||||
|
uniforms
|
||||||
|
});
|
||||||
|
programRef.current = program;
|
||||||
|
|
||||||
|
const geometry = new Triangle(gl);
|
||||||
|
geometryRef.current = geometry;
|
||||||
|
const mesh = new Mesh(gl, { geometry, program });
|
||||||
|
meshRef.current = mesh;
|
||||||
|
|
||||||
|
const resize = () => {
|
||||||
|
const rect = container.getBoundingClientRect();
|
||||||
|
renderer.setSize(rect.width, rect.height);
|
||||||
|
uniforms.iResolution.value = [gl.drawingBufferWidth, gl.drawingBufferHeight, 1];
|
||||||
|
|
||||||
|
if (blindMinWidth && blindMinWidth > 0) {
|
||||||
|
const maxByMinWidth = Math.max(1, Math.floor(rect.width / blindMinWidth));
|
||||||
|
|
||||||
|
const effective = blindCount ? Math.min(blindCount, maxByMinWidth) : maxByMinWidth;
|
||||||
|
uniforms.uBlindCount.value = Math.max(1, effective);
|
||||||
|
} else {
|
||||||
|
uniforms.uBlindCount.value = Math.max(1, blindCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstResizeRef.current) {
|
||||||
|
firstResizeRef.current = false;
|
||||||
|
const cx = gl.drawingBufferWidth / 2;
|
||||||
|
const cy = gl.drawingBufferHeight / 2;
|
||||||
|
uniforms.iMouse.value = [cx, cy];
|
||||||
|
mouseTargetRef.current = [cx, cy];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
resize();
|
||||||
|
const ro = new ResizeObserver(resize);
|
||||||
|
ro.observe(container);
|
||||||
|
|
||||||
|
const onPointerMove = (e: PointerEvent) => {
|
||||||
|
const rect = canvas.getBoundingClientRect();
|
||||||
|
const scale = (renderer as unknown as { dpr?: number }).dpr || 1;
|
||||||
|
const x = (e.clientX - rect.left) * scale;
|
||||||
|
const y = (rect.height - (e.clientY - rect.top)) * scale;
|
||||||
|
mouseTargetRef.current = [x, y];
|
||||||
|
if (mouseDampening <= 0) {
|
||||||
|
uniforms.iMouse.value = [x, y];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
canvas.addEventListener('pointermove', onPointerMove);
|
||||||
|
|
||||||
|
const loop = (t: number) => {
|
||||||
|
rafRef.current = requestAnimationFrame(loop);
|
||||||
|
uniforms.iTime.value = t * 0.001;
|
||||||
|
if (mouseDampening > 0) {
|
||||||
|
if (!lastTimeRef.current) lastTimeRef.current = t;
|
||||||
|
const dt = (t - lastTimeRef.current) / 1000;
|
||||||
|
lastTimeRef.current = t;
|
||||||
|
const tau = Math.max(1e-4, mouseDampening);
|
||||||
|
let factor = 1 - Math.exp(-dt / tau);
|
||||||
|
if (factor > 1) factor = 1;
|
||||||
|
const target = mouseTargetRef.current;
|
||||||
|
const cur = uniforms.iMouse.value;
|
||||||
|
cur[0] += (target[0] - cur[0]) * factor;
|
||||||
|
cur[1] += (target[1] - cur[1]) * factor;
|
||||||
|
} else {
|
||||||
|
lastTimeRef.current = t;
|
||||||
|
}
|
||||||
|
if (!paused && programRef.current && meshRef.current) {
|
||||||
|
try {
|
||||||
|
renderer.render({ scene: meshRef.current });
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
rafRef.current = requestAnimationFrame(loop);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (rafRef.current) cancelAnimationFrame(rafRef.current);
|
||||||
|
canvas.removeEventListener('pointermove', onPointerMove);
|
||||||
|
ro.disconnect();
|
||||||
|
if (canvas.parentElement === container) {
|
||||||
|
container.removeChild(canvas);
|
||||||
|
}
|
||||||
|
const callIfFn = <T extends object, K extends keyof T>(obj: T | null, key: K) => {
|
||||||
|
if (obj && typeof obj[key] === 'function') {
|
||||||
|
(obj[key] as unknown as () => void).call(obj);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
callIfFn(programRef.current, 'remove');
|
||||||
|
callIfFn(geometryRef.current, 'remove');
|
||||||
|
callIfFn(meshRef.current as unknown as { remove?: () => void }, 'remove');
|
||||||
|
callIfFn(rendererRef.current as unknown as { destroy?: () => void }, 'destroy');
|
||||||
|
programRef.current = null;
|
||||||
|
geometryRef.current = null;
|
||||||
|
meshRef.current = null;
|
||||||
|
rendererRef.current = null;
|
||||||
|
};
|
||||||
|
}, [
|
||||||
|
dpr,
|
||||||
|
paused,
|
||||||
|
gradientColors,
|
||||||
|
angle,
|
||||||
|
noise,
|
||||||
|
blindCount,
|
||||||
|
blindMinWidth,
|
||||||
|
mouseDampening,
|
||||||
|
mirrorGradient,
|
||||||
|
spotlightRadius,
|
||||||
|
spotlightSoftness,
|
||||||
|
spotlightOpacity,
|
||||||
|
distortAmount,
|
||||||
|
shineDirection
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={containerRef}
|
||||||
|
className={`gradient-blinds-container ${className}`}
|
||||||
|
style={{
|
||||||
|
...(mixBlendMode && {
|
||||||
|
mixBlendMode: mixBlendMode as React.CSSProperties['mixBlendMode']
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(GradientBlinds);
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
.container {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container canvas {
|
|
||||||
touch-action: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fade out on smaller screens */
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.container {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
import { useRef, useMemo } from 'react';
|
|
||||||
import { Canvas, useFrame } from '@react-three/fiber';
|
|
||||||
import { Float, MeshDistortMaterial } from '@react-three/drei';
|
|
||||||
import * as THREE from 'three';
|
|
||||||
import styles from './Scene3D.module.css';
|
|
||||||
|
|
||||||
function FloatingShape() {
|
|
||||||
const meshRef = useRef<THREE.Mesh>(null);
|
|
||||||
|
|
||||||
useFrame((state) => {
|
|
||||||
if (meshRef.current) {
|
|
||||||
meshRef.current.rotation.x = state.clock.elapsedTime * 0.1;
|
|
||||||
meshRef.current.rotation.y = state.clock.elapsedTime * 0.15;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Float
|
|
||||||
speed={2}
|
|
||||||
rotationIntensity={0.5}
|
|
||||||
floatIntensity={1}
|
|
||||||
>
|
|
||||||
<mesh ref={meshRef} scale={2.5}>
|
|
||||||
<icosahedronGeometry args={[1, 1]} />
|
|
||||||
<MeshDistortMaterial
|
|
||||||
color="#7FD998"
|
|
||||||
emissive="#004D2A"
|
|
||||||
emissiveIntensity={0.3}
|
|
||||||
roughness={0.4}
|
|
||||||
metalness={0.8}
|
|
||||||
distort={0.3}
|
|
||||||
speed={2}
|
|
||||||
/>
|
|
||||||
</mesh>
|
|
||||||
</Float>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ParticleField() {
|
|
||||||
const count = 100;
|
|
||||||
|
|
||||||
const positions = useMemo(() => {
|
|
||||||
const pos = new Float32Array(count * 3);
|
|
||||||
for (let i = 0; i < count; i++) {
|
|
||||||
pos[i * 3] = (Math.random() - 0.5) * 20;
|
|
||||||
pos[i * 3 + 1] = (Math.random() - 0.5) * 20;
|
|
||||||
pos[i * 3 + 2] = (Math.random() - 0.5) * 20;
|
|
||||||
}
|
|
||||||
return pos;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const pointsRef = useRef<THREE.Points>(null);
|
|
||||||
|
|
||||||
useFrame((state) => {
|
|
||||||
if (pointsRef.current) {
|
|
||||||
pointsRef.current.rotation.y = state.clock.elapsedTime * 0.02;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<points ref={pointsRef}>
|
|
||||||
<bufferGeometry>
|
|
||||||
<bufferAttribute
|
|
||||||
attach="attributes-position"
|
|
||||||
args={[positions, 3]}
|
|
||||||
/>
|
|
||||||
</bufferGeometry>
|
|
||||||
<pointsMaterial
|
|
||||||
size={0.05}
|
|
||||||
color="#7FD998"
|
|
||||||
transparent
|
|
||||||
opacity={0.6}
|
|
||||||
sizeAttenuation
|
|
||||||
/>
|
|
||||||
</points>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Scene3D() {
|
|
||||||
return (
|
|
||||||
<div className={styles.container}>
|
|
||||||
<Canvas
|
|
||||||
camera={{ position: [0, 0, 8], fov: 45 }}
|
|
||||||
dpr={[1, 2]}
|
|
||||||
gl={{ antialias: true, alpha: true }}
|
|
||||||
>
|
|
||||||
<ambientLight intensity={0.5} />
|
|
||||||
<directionalLight position={[10, 10, 5]} intensity={1} />
|
|
||||||
<pointLight position={[-10, -10, -5]} intensity={0.5} color="#7FD998" />
|
|
||||||
|
|
||||||
<FloatingShape />
|
|
||||||
<ParticleField />
|
|
||||||
</Canvas>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1 +1 @@
|
|||||||
export { Scene3D } from './Scene3D';
|
export { default as GradientBlinds } from "./GradientBlinds";
|
||||||
|
|||||||
@@ -16,23 +16,13 @@
|
|||||||
.brand {
|
.brand {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
gap: var(--space-sm);
|
gap: var(--space-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logoImage {
|
||||||
display: flex;
|
height: 1.75rem;
|
||||||
align-items: center;
|
width: auto;
|
||||||
gap: 0.25em;
|
|
||||||
font-size: 1.25rem;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logoText {
|
|
||||||
color: var(--md-sys-color-on-surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
.logoAccent {
|
|
||||||
color: var(--md-sys-color-primary);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.copyright {
|
.copyright {
|
||||||
|
|||||||
@@ -9,10 +9,7 @@ export function Footer() {
|
|||||||
<footer className={styles.footer}>
|
<footer className={styles.footer}>
|
||||||
<div className={`${styles.content} container`}>
|
<div className={`${styles.content} container`}>
|
||||||
<div className={styles.brand}>
|
<div className={styles.brand}>
|
||||||
<span className={styles.logo}>
|
<img src="/logo.svg" alt="RagusaIT" className={styles.logoImage} />
|
||||||
<span className={styles.logoText}>Ragusa</span>
|
|
||||||
<span className={styles.logoAccent}>IT</span>
|
|
||||||
</span>
|
|
||||||
<p className={styles.copyright}>
|
<p className={styles.copyright}>
|
||||||
{t.footer.copyright.replace('{year}', String(currentYear))}
|
{t.footer.copyright.replace('{year}', String(currentYear))}
|
||||||
</p>
|
</p>
|
||||||
@@ -40,10 +37,7 @@ export function Footer() {
|
|||||||
|
|
||||||
<div className={styles.credit}>
|
<div className={styles.credit}>
|
||||||
<p>
|
<p>
|
||||||
{t.footer.madeWith}{' '}
|
{t.footer.madeIn} <span className={styles.heart}>{t.footer.love}</span>
|
||||||
<span className={styles.heart}>React</span>{' '}
|
|
||||||
{t.footer.and}{' '}
|
|
||||||
<span className={styles.heart}>TypeScript</span>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
z-index: 100;
|
z-index: 100;
|
||||||
padding: var(--space-md) 0;
|
padding: var(--space-md) 0;
|
||||||
transition: background-color var(--transition-normal), backdrop-filter var(--transition-normal);
|
transition: background-color var(--transition-normal), backdrop-filter var(--transition-normal);
|
||||||
|
/* Ensure visibility against bright backgrounds on homepage */
|
||||||
|
background-color: rgba(15, 20, 16, 0.4);
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.header.scrolled {
|
.header.scrolled {
|
||||||
@@ -24,19 +27,12 @@
|
|||||||
.logo {
|
.logo {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.25em;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
font-weight: 700;
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: var(--md-sys-color-on-surface);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.logoText {
|
.logoImage {
|
||||||
color: var(--md-sys-color-on-surface);
|
height: 2rem;
|
||||||
}
|
width: auto;
|
||||||
|
|
||||||
.logoAccent {
|
|
||||||
color: var(--md-sys-color-primary);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.navLinks {
|
.navLinks {
|
||||||
|
|||||||
@@ -67,8 +67,7 @@ export function Navbar() {
|
|||||||
>
|
>
|
||||||
<nav className={`${styles.nav} container`}>
|
<nav className={`${styles.nav} container`}>
|
||||||
<Link to="/" className={styles.logo}>
|
<Link to="/" className={styles.logo}>
|
||||||
<span className={styles.logoText}>Ragusa</span>
|
<img src="/logo.svg" alt="RagusaIT" className={styles.logoImage} />
|
||||||
<span className={styles.logoAccent}>IT</span>
|
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -9,6 +9,15 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.backgroundContainer {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from "react";
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from "react-router-dom";
|
||||||
import { motion } from 'motion/react';
|
import { motion } from "motion/react";
|
||||||
import { useTranslation } from '../../i18n';
|
import { useTranslation } from "../../i18n";
|
||||||
import { useTypingEffect } from '../../hooks';
|
import { useTypingEffect } from "../../hooks";
|
||||||
import { Scene3D } from '../effects';
|
import { GradientBlinds } from "../effects";
|
||||||
import { Button } from '../ui';
|
import { Button } from "../ui";
|
||||||
import styles from './Hero.module.css';
|
import styles from "./Hero.module.css";
|
||||||
|
|
||||||
|
const GRADIENT_COLORS = ["#26a269", "#8ff0a4"];
|
||||||
|
|
||||||
export function Hero() {
|
export function Hero() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -16,8 +18,8 @@ export function Hero() {
|
|||||||
setShowScrollIndicator(window.scrollY < 50);
|
setShowScrollIndicator(window.scrollY < 50);
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener('scroll', handleScroll, { passive: true });
|
window.addEventListener("scroll", handleScroll, { passive: true });
|
||||||
return () => window.removeEventListener('scroll', handleScroll);
|
return () => window.removeEventListener("scroll", handleScroll);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const { text } = useTypingEffect({
|
const { text } = useTypingEffect({
|
||||||
@@ -29,14 +31,29 @@ export function Hero() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={styles.hero}>
|
<section className={styles.hero}>
|
||||||
<Scene3D />
|
<div className={styles.backgroundContainer}>
|
||||||
|
<GradientBlinds
|
||||||
|
gradientColors={GRADIENT_COLORS}
|
||||||
|
angle={134}
|
||||||
|
noise={0.31}
|
||||||
|
blindCount={12}
|
||||||
|
blindMinWidth={50}
|
||||||
|
spotlightRadius={0.4}
|
||||||
|
spotlightSoftness={1}
|
||||||
|
spotlightOpacity={1}
|
||||||
|
mouseDampening={0.15}
|
||||||
|
distortAmount={13}
|
||||||
|
shineDirection="left"
|
||||||
|
mixBlendMode="lighten"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className={`${styles.content} container`}>
|
<div className={`${styles.content} container`}>
|
||||||
<motion.div
|
<motion.div
|
||||||
className={styles.text}
|
className={styles.text}
|
||||||
initial={{ opacity: 0, y: 30 }}
|
initial={{ opacity: 0, y: 30 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ duration: 0.6, ease: 'easeOut' }}
|
transition={{ duration: 0.6, ease: "easeOut" }}
|
||||||
>
|
>
|
||||||
<motion.p
|
<motion.p
|
||||||
className={styles.greeting}
|
className={styles.greeting}
|
||||||
@@ -100,7 +117,7 @@ export function Hero() {
|
|||||||
<motion.div
|
<motion.div
|
||||||
className={styles.scrollMouse}
|
className={styles.scrollMouse}
|
||||||
animate={{ y: [0, 8, 0] }}
|
animate={{ y: [0, 8, 0] }}
|
||||||
transition={{ repeat: Infinity, duration: 1.5, ease: 'easeInOut' }}
|
transition={{ repeat: Infinity, duration: 1.5, ease: "easeInOut" }}
|
||||||
>
|
>
|
||||||
<span className={styles.scrollWheel} />
|
<span className={styles.scrollWheel} />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|||||||
@@ -108,8 +108,8 @@ export const de = {
|
|||||||
// Footer
|
// Footer
|
||||||
footer: {
|
footer: {
|
||||||
copyright: '© {year} Ragusa IT-Consulting. Alle Rechte vorbehalten.',
|
copyright: '© {year} Ragusa IT-Consulting. Alle Rechte vorbehalten.',
|
||||||
madeWith: 'Entwickelt mit',
|
madeIn: 'Entwickelt in Deutschland mit',
|
||||||
and: 'und',
|
love: 'Liebe',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ export const en: Translations = {
|
|||||||
// Footer
|
// Footer
|
||||||
footer: {
|
footer: {
|
||||||
copyright: '© {year} Ragusa IT-Consulting. All rights reserved.',
|
copyright: '© {year} Ragusa IT-Consulting. All rights reserved.',
|
||||||
madeWith: 'Built with',
|
madeIn: 'Made in Germany with',
|
||||||
and: 'and',
|
love: 'love',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||