Merge version_4_1783202008767 into main #3
@@ -5,15 +5,15 @@
|
||||
|
||||
:root {
|
||||
/* @colorThemes/lightTheme/grayNavyBlue */
|
||||
--background: #ffffff;
|
||||
--card: #f8f9fa;
|
||||
--foreground: #111827;
|
||||
--primary-cta: #6366f1;
|
||||
--background: #050505;
|
||||
--card: #111111;
|
||||
--foreground: #ffffff;
|
||||
--primary-cta: #7c3aed;
|
||||
--primary-cta-text: #ffffff;
|
||||
--secondary-cta: #e0e7ff;
|
||||
--secondary-cta-text: #4f46e5;
|
||||
--accent: #6b7280;
|
||||
--background-accent: #f3f4f6;
|
||||
--secondary-cta: #1a1a1a;
|
||||
--secondary-cta-text: #7c3aed;
|
||||
--accent: #a3a3a3;
|
||||
--background-accent: #0f0f0f;
|
||||
|
||||
/* @layout/border-radius/rounded */
|
||||
--radius: 1.5rem;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
// become component refs.
|
||||
|
||||
import React from 'react';
|
||||
import HeroSection from './HomePage/sections/Hero';
|
||||
import AboutSection from './HomePage/sections/About';
|
||||
import ServicesSection from './HomePage/sections/Services';
|
||||
import TestimonialsSection from './HomePage/sections/Testimonials';
|
||||
@@ -14,10 +13,11 @@ import FaqSection from './HomePage/sections/Faq';
|
||||
import ContactSection from './HomePage/sections/Contact';
|
||||
|
||||
|
||||
import MetricsSection from './HomePage/sections/Metrics';export default function HomePage(): React.JSX.Element {
|
||||
import MetricsSection from './HomePage/sections/Metrics';
|
||||
import HeroWebglSection from './HomePage/sections/HeroWebgl';export default function HomePage(): React.JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<HeroSection />
|
||||
<HeroWebglSection />
|
||||
|
||||
<AboutSection />
|
||||
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck — generated by catalog-eject; runtime-correct but TS strict-mode false-positives on inlined catalog body
|
||||
import Button from "@/components/ui/Button";
|
||||
import HeroBackgroundSlot from "@/components/ui/HeroBackgroundSlot";
|
||||
import TextAnimation from "@/components/ui/TextAnimation";
|
||||
import ImageOrVideo from "@/components/ui/ImageOrVideo";
|
||||
import AvatarGroup from "@/components/ui/AvatarGroup";
|
||||
|
||||
const avatarsSrc = [
|
||||
"http://img.b2bpic.net/free-photo/smiling-man-wearing-white-shirt_23-2152009541.jpg",
|
||||
"http://img.b2bpic.net/free-photo/young-businesswoman-portrait-office_1262-1506.jpg",
|
||||
"http://img.b2bpic.net/free-photo/emotional-young-bearded-man-asking-something-isolated-dark-wall_140725-97014.jpg",
|
||||
"http://img.b2bpic.net/free-photo/crazy-hero-happy-expression_1194-4140.jpg"
|
||||
];
|
||||
const primaryButton = {
|
||||
text: "Zistiť viac",
|
||||
href: "#services"
|
||||
};
|
||||
const secondaryButton = {
|
||||
text: "Kontaktovať nás",
|
||||
href: "#contact"
|
||||
};
|
||||
const names = [
|
||||
"Google",
|
||||
"Microsoft",
|
||||
"Amazon",
|
||||
"Apple",
|
||||
"Tesla",
|
||||
"Adobe"
|
||||
];
|
||||
|
||||
type HeroCenteredLogosProps = {
|
||||
avatarsSrc: string[];
|
||||
avatarText: string;
|
||||
title: string;
|
||||
description: string;
|
||||
primaryButton: { text: string; href: string };
|
||||
secondaryButton: { text: string; href: string };
|
||||
names: string[];
|
||||
hideMedia?: boolean;
|
||||
textAnimation: "slide-up" | "fade-blur" | "fade";
|
||||
} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never });
|
||||
|
||||
const HeroInline = () => {
|
||||
return (
|
||||
<section aria-label="Hero section" className="relative h-svh flex flex-col mb-20">
|
||||
<HeroBackgroundSlot />
|
||||
{!undefined && (
|
||||
<div className="absolute inset-0 z-0">
|
||||
<ImageOrVideo imageSrc={"http://img.b2bpic.net/free-photo/color-year-purple-tones-abstract-landscape-with-fantasy-aesthetic_23-2151394052.jpg"} className="size-full object-cover" />
|
||||
<div className="absolute inset-0 bg-background/80" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="relative z-10 flex-1 flex items-center justify-center">
|
||||
<div className="flex flex-col items-center gap-3 pt-8 w-content-width mx-auto text-center">
|
||||
<AvatarGroup avatarsSrc={avatarsSrc} label={"Spolupracujeme s lídrami"} size="lg" />
|
||||
|
||||
<TextAnimation
|
||||
text={"UvaWeb: Budúcnosť vášho marketingu začína dnes"}
|
||||
variant={"fade-blur"}
|
||||
gradientText={true}
|
||||
tag="h1"
|
||||
className="md:max-w-8/10 text-7xl 2xl:text-8xl leading-[1.15] font-semibold text-center text-balance"
|
||||
/>
|
||||
|
||||
<TextAnimation
|
||||
text={"Vytvárame pohlcujúce digitálne zážitky, ktoré definujú značky. Špičková kreativita spojená s dátami a špičkovou technológiou."}
|
||||
variant={"fade-blur"}
|
||||
gradientText={false}
|
||||
tag="p"
|
||||
className="md:max-w-7/10 text-lg md:text-xl leading-snug text-balance"
|
||||
/>
|
||||
|
||||
<div className="flex flex-wrap justify-center gap-3 mt-2 md:mt-3">
|
||||
<Button text={primaryButton.text} href={primaryButton.href} variant="primary" />
|
||||
<Button text={secondaryButton.text} href={secondaryButton.href} variant="secondary" animationDelay={0.1} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 w-content-width mx-auto pb-8 overflow-hidden mask-fade-x">
|
||||
<div className="flex w-max animate-marquee-horizontal" style={{ animationDuration: "30s" }}>
|
||||
{[...names, ...names, ...names, ...names].map((name, index) => (
|
||||
<div key={index} className="shrink-0 mx-3 px-4 py-2 card rounded">
|
||||
<span className="text-xl font-semibold whitespace-nowrap text-foreground/75">{name}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default function HeroSection() {
|
||||
return (
|
||||
<div data-webild-section="hero" data-section="hero" id="hero">
|
||||
<HeroInline />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
212
src/pages/HomePage/sections/HeroWebgl.tsx
Normal file
212
src/pages/HomePage/sections/HeroWebgl.tsx
Normal file
@@ -0,0 +1,212 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { motion } from "motion/react";
|
||||
import Button from "@/components/ui/Button";
|
||||
|
||||
export default function HeroWebglSection() {
|
||||
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const mouseRef = useRef({ x: typeof window !== 'undefined' ? window.innerWidth / 2 : 0, y: typeof window !== 'undefined' ? window.innerHeight / 2 : 0 });
|
||||
|
||||
useEffect(() => {
|
||||
const updateMousePosition = (e: MouseEvent) => {
|
||||
setMousePosition({ x: e.clientX, y: e.clientY });
|
||||
mouseRef.current = { x: e.clientX, y: e.clientY };
|
||||
};
|
||||
window.addEventListener("mousemove", updateMousePosition);
|
||||
return () => window.removeEventListener("mousemove", updateMousePosition);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
const gl = canvas.getContext("webgl");
|
||||
if (!gl) return;
|
||||
|
||||
const vertexShaderSource = `
|
||||
attribute vec2 position;
|
||||
void main() {
|
||||
gl_Position = vec4(position, 0.0, 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
const fragmentShaderSource = `
|
||||
precision highp float;
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
uniform vec2 u_mouse;
|
||||
|
||||
void main() {
|
||||
vec2 st = gl_FragCoord.xy / u_resolution.xy;
|
||||
vec2 mouse = u_mouse / u_resolution.xy;
|
||||
mouse.y = 1.0 - mouse.y;
|
||||
|
||||
float dist = distance(st, mouse);
|
||||
float blob = smoothstep(0.8, 0.0, dist);
|
||||
|
||||
vec3 bg = vec3(0.02, 0.02, 0.02);
|
||||
vec3 accent = vec3(0.48, 0.22, 0.92); // #7c3aed
|
||||
|
||||
float noise = fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453) * 0.03;
|
||||
|
||||
float wave = sin(st.x * 5.0 + u_time * 0.5) * cos(st.y * 5.0 + u_time * 0.5) * 0.15;
|
||||
|
||||
vec3 color = mix(bg, accent, blob * 0.4 + wave);
|
||||
color += noise;
|
||||
|
||||
gl_FragColor = vec4(color, 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
const compileShader = (type: number, source: string) => {
|
||||
const shader = gl.createShader(type);
|
||||
if (!shader) return null;
|
||||
gl.shaderSource(shader, source);
|
||||
gl.compileShader(shader);
|
||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||||
console.error(gl.getShaderInfoLog(shader));
|
||||
gl.deleteShader(shader);
|
||||
return null;
|
||||
}
|
||||
return shader;
|
||||
};
|
||||
|
||||
const vertexShader = compileShader(gl.VERTEX_SHADER, vertexShaderSource);
|
||||
const fragmentShader = compileShader(gl.FRAGMENT_SHADER, fragmentShaderSource);
|
||||
|
||||
if (!vertexShader || !fragmentShader) return;
|
||||
|
||||
const program = gl.createProgram();
|
||||
if (!program) return;
|
||||
gl.attachShader(program, vertexShader);
|
||||
gl.attachShader(program, fragmentShader);
|
||||
gl.linkProgram(program);
|
||||
|
||||
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
||||
console.error(gl.getProgramInfoLog(program));
|
||||
return;
|
||||
}
|
||||
|
||||
gl.useProgram(program);
|
||||
|
||||
const positionBuffer = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
||||
const positions = new Float32Array([
|
||||
-1.0, -1.0,
|
||||
1.0, -1.0,
|
||||
-1.0, 1.0,
|
||||
-1.0, 1.0,
|
||||
1.0, -1.0,
|
||||
1.0, 1.0,
|
||||
]);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
|
||||
|
||||
const positionLocation = gl.getAttribLocation(program, "position");
|
||||
gl.enableVertexAttribArray(positionLocation);
|
||||
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
|
||||
|
||||
const resolutionLocation = gl.getUniformLocation(program, "u_resolution");
|
||||
const timeLocation = gl.getUniformLocation(program, "u_time");
|
||||
const mouseLocation = gl.getUniformLocation(program, "u_mouse");
|
||||
|
||||
let animationFrameId: number;
|
||||
const startTime = performance.now();
|
||||
|
||||
const render = () => {
|
||||
const width = canvas.clientWidth;
|
||||
const height = canvas.clientHeight;
|
||||
if (canvas.width !== width || canvas.height !== height) {
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
gl.viewport(0, 0, width, height);
|
||||
}
|
||||
|
||||
gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
|
||||
gl.uniform1f(timeLocation, (performance.now() - startTime) / 1000.0);
|
||||
gl.uniform2f(mouseLocation, mouseRef.current.x, mouseRef.current.y);
|
||||
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
||||
animationFrameId = requestAnimationFrame(render);
|
||||
};
|
||||
|
||||
render();
|
||||
|
||||
return () => {
|
||||
cancelAnimationFrame(animationFrameId);
|
||||
gl.deleteProgram(program);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const headline = "WE BUILD LOUD IDEAS";
|
||||
const letters = headline.split("");
|
||||
|
||||
return (
|
||||
<div data-webild-section="hero-webgl" id="hero-webgl" className="relative h-screen w-full flex flex-col items-center justify-center overflow-hidden bg-background cursor-none">
|
||||
{/* WebGL Background */}
|
||||
<canvas ref={canvasRef} className="absolute inset-0 w-full h-full z-0" />
|
||||
|
||||
{/* Grain Overlay */}
|
||||
<div className="pointer-events-none absolute inset-0 z-10 opacity-[0.15] mix-blend-overlay" style={{ backgroundImage: 'url("data:image/svg+xml,%3Csvg viewBox=%220 0 200 200%22 xmlns=%22http://www.w3.org/2000/svg%22%3E%3Cfilter id=%22noiseFilter%22%3E%3CfeTurbulence type=%22fractalNoise%22 baseFrequency=%220.8%22 numOctaves=%223%22 stitchTiles=%22stitch%22/%3E%3C/filter%3E%3Crect width=%22100%25%22 height=%22100%25%22 filter=%22url(%23noiseFilter)%22/%3E%3C/svg%3E")' }}></div>
|
||||
|
||||
{/* Custom Cursor */}
|
||||
<motion.div
|
||||
className="fixed top-0 left-0 w-3 h-3 bg-primary-cta rounded-full pointer-events-none z-50 mix-blend-difference"
|
||||
animate={{ x: mousePosition.x - 6, y: mousePosition.y - 6 }}
|
||||
transition={{ type: "spring", stiffness: 1000, damping: 40, mass: 0.1 }}
|
||||
/>
|
||||
<motion.div
|
||||
className="fixed top-0 left-0 w-10 h-10 border border-primary-cta rounded-full pointer-events-none z-50 mix-blend-difference"
|
||||
animate={{ x: mousePosition.x - 20, y: mousePosition.y - 20 }}
|
||||
transition={{ type: "spring", stiffness: 250, damping: 20, mass: 0.8 }}
|
||||
/>
|
||||
|
||||
<div className="relative z-20 flex flex-col items-center justify-center w-content-width mx-auto text-center pointer-events-none">
|
||||
<h1 className="text-[12vw] md:text-[9vw] font-bold text-foreground tracking-tighter leading-[0.85] flex flex-wrap justify-center uppercase">
|
||||
{letters.map((letter, i) => (
|
||||
<motion.span
|
||||
key={i}
|
||||
initial={{ opacity: 0, y: 100, rotateX: -90 }}
|
||||
animate={{ opacity: 1, y: 0, rotateX: 0 }}
|
||||
transition={{ duration: 1, delay: i * 0.05, type: "spring", damping: 15, stiffness: 100 }}
|
||||
style={{ display: letter === " " ? "inline" : "inline-block", width: letter === " " ? "0.3em" : "auto", transformOrigin: "bottom" }}
|
||||
>
|
||||
{letter}
|
||||
</motion.span>
|
||||
))}
|
||||
</h1>
|
||||
|
||||
<motion.p
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 1, delay: 1.5 }}
|
||||
className="mt-8 text-xl md:text-2xl text-accent max-w-2xl"
|
||||
>
|
||||
A flagship digital experience. We craft award-winning brands and high-performance platforms.
|
||||
</motion.p>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 1, delay: 1.8 }}
|
||||
className="mt-12 pointer-events-auto"
|
||||
>
|
||||
<Button text="Explore Our Work" href="#work" variant="primary" className="rounded-full px-8 py-4 text-lg" />
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Scroll Down Indicator */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 1, delay: 2.5 }}
|
||||
className="absolute bottom-10 left-1/2 -translate-x-1/2 flex flex-col items-center gap-2 z-20 pointer-events-none"
|
||||
>
|
||||
<span className="text-xs uppercase tracking-widest text-accent">Scroll</span>
|
||||
<motion.div
|
||||
animate={{ y: [0, 10, 0] }}
|
||||
transition={{ repeat: Infinity, duration: 1.5, ease: "easeInOut" }}
|
||||
className="w-[1px] h-12 bg-gradient-to-b from-primary-cta to-transparent"
|
||||
/>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user