This commit is contained in:
2025-07-07 10:11:41 +02:00
parent 9296782fc1
commit 19c762fe67
19 changed files with 848 additions and 138 deletions
@@ -1,65 +1,68 @@
// src/assets/backgrounds/Background.jsx
// Kockás háttér, ami a mousemovera reagál
import React, { useEffect, useState } from "react";
import { motion } from "framer-motion";
import React, { useEffect, useState } from "react"
import { motion } from "framer-motion"
const Background = () => {
const [gridSize, setGridSize] = useState({ cols: 12, rows: 6 });
const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
const [path, setPath] = useState([]);
const [gridSize, setGridSize] = useState({ cols: 12, rows: 6 })
const [mousePos, setMousePos] = useState({ x: 0, y: 0 })
const [path, setPath] = useState([])
useEffect(() => {
const updateGrid = () => {
const width = window.innerWidth;
const height = window.innerHeight;
const cols = Math.max(8, Math.floor(width / 100)); // 100px-es cellák
const rows = Math.max(5, Math.floor(height / 100)); // 100px-es cellák
setGridSize({ cols, rows });
};
const width = window.innerWidth
const height = window.innerHeight
const cols = Math.max(8, Math.floor(width / 100)) // 100px-es cellák
const rows = Math.max(5, Math.floor(height / 100)) // 100px-es cellák
setGridSize({ cols, rows })
}
const handleMouseMove = (e) => {
setMousePos({ x: e.clientX, y: e.clientY });
};
setMousePos({ x: e.clientX, y: e.clientY })
}
updateGrid();
window.addEventListener("resize", updateGrid);
window.addEventListener("mousemove", handleMouseMove);
updateGrid()
window.addEventListener("resize", updateGrid)
window.addEventListener("mousemove", handleMouseMove)
return () => {
window.removeEventListener("resize", updateGrid);
window.removeEventListener("mousemove", handleMouseMove);
};
}, []);
window.removeEventListener("resize", updateGrid)
window.removeEventListener("mousemove", handleMouseMove)
}
}, [])
useEffect(() => {
const interval = setInterval(() => {
const newCol = Math.floor(Math.random() * gridSize.cols);
const newRow = Math.floor(Math.random() * gridSize.rows);
const newCol = Math.floor(Math.random() * gridSize.cols)
const newRow = Math.floor(Math.random() * gridSize.rows)
setPath((prevPath) => {
const newPath = [...prevPath, { col: newCol, row: newRow, opacity: 1 }]; // Új pont hozzáadása
const newPath = [...prevPath, { col: newCol, row: newRow, opacity: 1 }] // Új pont hozzáadása
if (newPath.length > 10) {
newPath.shift(); // Max 10 pont az útvonalon
newPath.shift() // Max 10 pont az útvonalon
}
return newPath;
});
}, 500); // Új pont hozzáadása minden 500ms-ben
return newPath
})
}, 500) // Új pont hozzáadása minden 500ms-ben
// Az útvonal pontjainak fokozatos eltűnése
const fadeInterval = setInterval(() => {
setPath((prevPath) =>
prevPath.map((point) => ({
...point,
opacity: Math.max(0, point.opacity - 0.05), // Fokozatosan csökkentjük az opacity-t
})).filter((point) => point.opacity > 0) // Eltávolítjuk a teljesen eltűnt pontokat
);
}, 100); // Opacity frissítése minden 100ms-ben
setPath(
(prevPath) =>
prevPath
.map((point) => ({
...point,
opacity: Math.max(0, point.opacity - 0.05), // Fokozatosan csökkentjük az opacity-t
}))
.filter((point) => point.opacity > 0) // Eltávolítjuk a teljesen eltűnt pontokat
)
}, 100) // Opacity frissítése minden 100ms-ben
return () => {
clearInterval(interval);
clearInterval(fadeInterval);
};
}, [gridSize]);
clearInterval(interval)
clearInterval(fadeInterval)
}
}, [gridSize])
return (
<div className="relative w-full h-screen bg-background flex items-center justify-center overflow-hidden">
@@ -71,19 +74,19 @@ const Background = () => {
}}
>
{[...Array(gridSize.cols * gridSize.rows)].map((_, i) => {
const col = i % gridSize.cols;
const row = Math.floor(i / gridSize.cols);
const cellX = (col + 0.5) * (window.innerWidth / gridSize.cols);
const cellY = (row + 0.5) * (window.innerHeight / gridSize.rows);
const col = i % gridSize.cols
const row = Math.floor(i / gridSize.cols)
const cellX = (col + 0.5) * (window.innerWidth / gridSize.cols)
const cellY = (row + 0.5) * (window.innerHeight / gridSize.rows)
const dx = cellX - mousePos.x;
const dy = cellY - mousePos.y;
const distance = Math.sqrt(dx * dx + dy * dy);
const distanceFactor = Math.max(0, 1 - distance / 300); // Egér hatótávolsága
const dx = cellX - mousePos.x
const dy = cellY - mousePos.y
const distance = Math.sqrt(dx * dx + dy * dy)
const distanceFactor = Math.max(0, 1 - distance / 300) // Egér hatótávolsága
// Az útvonalban lévő pontok opacitása
const pathPoint = path.find((p) => p.col === col && p.row === row);
const pathOpacity = pathPoint ? pathPoint.opacity : 0;
const pathPoint = path.find((p) => p.col === col && p.row === row)
const pathOpacity = pathPoint ? pathPoint.opacity : 0
return (
<motion.div
@@ -95,11 +98,11 @@ const Background = () => {
}}
transition={{ duration: 0.5, ease: "easeOut" }} // Lassú és sima átmenet
/>
);
)
})}
</div>
</div>
);
};
)
}
export default Background;
export default Background
@@ -0,0 +1,134 @@
import { useRef, useState } from "react"
import { motion, useMotionValue, useSpring } from "framer-motion"
const springValues = {
damping: 30,
stiffness: 100,
mass: 2,
}
export default function LogoCard({
imageSrc,
altText = "Tilted card image",
captionText = "",
containerHeight = "300px",
containerWidth = "100%",
imageHeight = "300px",
imageWidth = "300px",
scaleOnHover = 1.1,
rotateAmplitude = 14,
showMobileWarning = true,
showTooltip = true,
overlayContent = null,
displayOverlayContent = false,
}) {
const ref = useRef(null)
const x = useMotionValue(0)
const y = useMotionValue(0)
const rotateX = useSpring(useMotionValue(0), springValues)
const rotateY = useSpring(useMotionValue(0), springValues)
const scale = useSpring(1, springValues)
const opacity = useSpring(0)
const rotateFigcaption = useSpring(0, {
stiffness: 350,
damping: 30,
mass: 1,
})
const [lastY, setLastY] = useState(0)
function handleMouse(e) {
if (!ref.current) return
const rect = ref.current.getBoundingClientRect()
const offsetX = e.clientX - rect.left - rect.width / 2
const offsetY = e.clientY - rect.top - rect.height / 2
const rotationX = (offsetY / (rect.height / 2)) * -rotateAmplitude
const rotationY = (offsetX / (rect.width / 2)) * rotateAmplitude
rotateX.set(rotationX)
rotateY.set(rotationY)
x.set(e.clientX - rect.left)
y.set(e.clientY - rect.top)
const velocityY = offsetY - lastY
rotateFigcaption.set(-velocityY * 0.6)
setLastY(offsetY)
}
function handleMouseEnter() {
scale.set(scaleOnHover)
opacity.set(1)
}
function handleMouseLeave() {
opacity.set(0)
scale.set(1)
rotateX.set(0)
rotateY.set(0)
rotateFigcaption.set(0)
}
return (
<figure
ref={ref}
className="relative w-full h-full [perspective:800px] flex flex-col items-center justify-center"
style={{
height: containerHeight,
width: containerWidth,
}}
onMouseMove={handleMouse}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
{showMobileWarning && (
<div className="absolute top-4 text-center text-sm block sm:hidden">
This effect is not optimized for mobile. Check on desktop.
</div>
)}
<motion.div
className="relative [transform-style:preserve-3d]"
style={{
width: imageWidth,
height: imageHeight,
rotateX,
rotateY,
scale,
}}
>
<motion.img
src={imageSrc}
alt={altText}
className="absolute top-0 left-0 object-cover rounded-[15px] will-change-transform [transform:translateZ(0)]"
style={{
width: imageWidth,
height: imageHeight,
}}
/>
{displayOverlayContent && overlayContent && (
<motion.div className="absolute top-0 left-0 z-[2] will-change-transform [transform:translateZ(30px)]">
{overlayContent}
</motion.div>
)}
</motion.div>
{showTooltip && (
<motion.figcaption
className="pointer-events-none absolute left-0 top-0 rounded-[4px] bg-white px-[10px] py-[4px] text-[10px] text-[#2d2d2d] opacity-0 z-[3] hidden sm:block"
style={{
x,
y,
opacity,
rotate: rotateFigcaption,
}}
>
{captionText}
</motion.figcaption>
)}
</figure>
)
}