Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Enhanced HomePage and DetailsModal UI/UX #26

Merged
merged 1 commit into from
Dec 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"react-scripts": "5.0.1",
"react-slick": "^0.29.0",
"react-spinners": "^0.13.8",
"react-spring": "^9.7.3",
"react-stripe-checkout": "^2.6.3",
"react-swipeable-views": "^0.14.0",
"slick-carousel": "^1.8.1",
Expand Down
108 changes: 108 additions & 0 deletions src/assets/animations/CardRowsAnimation.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import React, { useEffect, useRef } from 'react';
import * as THREE from 'three';
import placeholder from '../images/placeholder.jpeg';

const CardRowsAnimation = () => {
const containerRef = useRef(null);

useEffect(() => {
// Scene setup
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);

// Append renderer to the container ref
if (containerRef.current) {
containerRef.current.appendChild(renderer.domElement);
}

// Camera position
camera.position.z = 5;

// Texture loader
const textureLoader = new THREE.TextureLoader();
const cardTexture = textureLoader.load(placeholder);

// Define card geometry and material
const geometry = new THREE.PlaneGeometry(1, 1.4);
const material = new THREE.MeshBasicMaterial({ map: cardTexture });

// Rows and columns configuration
const rows = 4;
const cardSpacing = 1.8;
let cardsPerRow = Math.floor(
window.innerWidth / (geometry.parameters.width * cardSpacing)
);

// Create card meshes and add to scene
const cardRows = [];
for (let i = 0; i < rows; i++) {
const row = new THREE.Group();
for (let j = 0; j < cardsPerRow; j++) {
const card = new THREE.Mesh(geometry, material);
card.position.x = (j - cardsPerRow / 2) * cardSpacing;
card.position.y = (i - rows / 2) * cardSpacing;
row.add(card);
}
// Alternate direction for each row
row.userData = { direction: i % 2 === 0 ? 0.005 : -0.005 };
cardRows.push(row);
scene.add(row);
}

// Animation loop
const animate = () => {
requestAnimationFrame(animate);

// Update card positions
cardRows.forEach((row) => {
row.children.forEach((card) => {
card.position.x += row.userData.direction;
// Reset position if it's off-screen
if (
card.position.x > window.innerWidth / 2 ||
card.position.x < -window.innerWidth / 2
) {
card.position.x = -card.position.x;
}
});
});

renderer.render(scene, camera);
};

// Handle window resize
window.addEventListener('resize', onWindowResize, false);
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
// Recalculate number of cards per row
cardsPerRow = Math.floor(
window.innerWidth / (geometry.parameters.width * cardSpacing)
);
}

animate();

// Cleanup
return () => {
if (containerRef.current) {
containerRef.current.removeChild(renderer.domElement);
}
window.removeEventListener('resize', onWindowResize, false);
};
}, []);

return (
<div ref={containerRef} style={{ width: '100vw', height: '100vh' }}></div>
);
};

export default CardRowsAnimation;
140 changes: 140 additions & 0 deletions src/assets/animations/CardStormAnimation.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import React, { useRef, useEffect } from 'react';
import * as THREE from 'three';
import placeholder from '../images/placeholder.jpeg';

const CardStormAnimation = () => {
const mountRef = useRef(null);
const cards = [];
const textureLoader = new THREE.TextureLoader();
// Configuration
const initialPullRange = 1;
const orderlinessThreshold = 300;
const gravitationalPull = 0.01;
const rotationSpeed = 0.005; // Uniform rotation speed for all cards

useEffect(() => {
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
mountRef.current.appendChild(renderer.domElement);

// Lighting
const ambientLight = new THREE.AmbientLight(0x999999);
scene.add(ambientLight);
const pointLight = new THREE.PointLight(0xffffff, 1, 100);
pointLight.position.set(10, 10, 10);
pointLight.castShadow = true;
scene.add(pointLight);

// Card creation
for (let i = 0; i < 100; i++) {
const geometry = new THREE.BoxGeometry(0.5, 0.7, 0.01);
const material = new THREE.MeshStandardMaterial({
map: textureLoader.load(placeholder),
});
const card = new THREE.Mesh(geometry, material);
card.position.set(
(Math.random() - 0.5) * 10,
(Math.random() - 0.5) * 10,
(Math.random() - 0.5) * 10
);
card.castShadow = true;
card.receiveShadow = true;
cards.push(card);
scene.add(card);
}

camera.position.z = 5;

// Mouse position
let mouseX = 0,
mouseY = 0;
const onDocumentMouseMove = (event) => {
const vector = new THREE.Vector3(
(event.clientX / window.innerWidth) * 2 - 1,
-(event.clientY / window.innerHeight) * 2 + 1,
0.5
);
vector.unproject(camera);
const dir = vector.sub(camera.position).normalize();
const distance = -camera.position.z / dir.z;
const pos = camera.position.clone().add(dir.multiplyScalar(distance));
mouseX = pos.x;
mouseY = pos.y;
};
document.addEventListener('mousemove', onDocumentMouseMove, false);

const animate = () => {
requestAnimationFrame(animate);

cards.forEach((card) => {
const dx = card.position.x - mouseX;
const dy = card.position.y - mouseY;
const distance = Math.sqrt(dx * dx + dy * dy);
const angle = Math.atan2(dy, dx);

if (distance < initialPullRange || card.isInfluenced) {
card.isInfluenced = true;
card.timeInfluenced = card.timeInfluenced || 0;
card.timeInfluenced++;

if (card.timeInfluenced > orderlinessThreshold) {
const targetRotation = Math.atan2(
mouseY - card.position.y,
mouseX - card.position.x
);
card.rotation.z = THREE.MathUtils.lerp(
card.rotation.z,
targetRotation,
0.01
);
}

// Velocity for orbital movement
card.velocityX = card.velocityX || 0.01 * (Math.random() - 0.5);
card.velocityY = card.velocityY || 0.01 * (Math.random() - 0.5);

card.velocityX -= gravitationalPull * Math.cos(angle);
card.velocityY -= gravitationalPull * Math.sin(angle);

card.position.x += card.velocityX;
card.position.y += card.velocityY;

// Reducing gravitational pull gradually
if (card.timeInfluenced > orderlinessThreshold) {
const pullReductionFactor =
1 -
(card.timeInfluenced - orderlinessThreshold) /
orderlinessThreshold;
card.velocityX *= pullReductionFactor;
card.velocityY *= pullReductionFactor;
}
}

// Applying continuous rotation for a smooth effect
card.rotation.x += rotationSpeed;
card.rotation.y += rotationSpeed;
});

renderer.render(scene, camera);
};
animate();

// Clean up
return () => {
mountRef?.current?.removeChild(renderer.domElement);
document?.removeEventListener('mousemove', onDocumentMouseMove, false);
};
}, []); // Only re-run the effect if mountRef changes

return <div ref={mountRef} />;
};

export default CardStormAnimation;
120 changes: 120 additions & 0 deletions src/assets/animations/SingleCardAnimation.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import React, { useRef } from 'react';
import { useSpring, animated } from 'react-spring';
import placeholder from '../../assets/images/placeholder.jpeg';

const SingleCardAnimation = ({ cardImage }) => {
const containerRef = useRef(null);

// Default the card image to placeholder if it's null/undefined
const frontImage = cardImage || placeholder;

const [tilt, setTilt] = useSpring(() => ({
xys: [0, 0, 1], // Initial values for tilt [rotateX, rotateY, scale]
config: { mass: 5, tension: 350, friction: 40 },
}));

const calcTilt = (x, y) => {
const rect = containerRef.current.getBoundingClientRect();
const relX = x - rect.left - rect.width / 2;
const relY = y - rect.top - rect.height / 2;
// Update tilt based on mouse position over the card
return [relY / 10, relX / 10, 1.1]; // Adjust sensitivity and scale here
};

const handleMouseMove = (event) => {
setTilt({ xys: calcTilt(event.clientX, event.clientY) });
};

const handleMouseLeave = () => {
setTilt({ xys: [0, 0, 1] });
};

return (
<animated.div
ref={containerRef}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
style={{
width: '20rem',
height: '29rem',
backgroundImage: `url(${frontImage})`,
backgroundSize: 'cover',
borderRadius: '5px',
transform: tilt.xys.to(
(x, y, s) =>
`perspective(1000px) rotateX(${x}deg) rotateY(${y}deg) scale(${s})`
),
position: 'relative',
}}
/>
);
};

export default SingleCardAnimation;

// import React, { useRef } from 'react';
// import { useSpring, animated } from 'react-spring';
// import placeholder from '../../assets/images/placeholder.jpeg';

// const SingleCardAnimation = ({ cardImage }) => {
// const containerRef = useRef(null);

// // Default the card image to placeholder if it's null/undefined
// const frontImage = cardImage || placeholder;

// const [props, set] = useSpring(() => ({
// to: { opacity: 1, transform: 'rotateY(0deg)' },
// from: { opacity: 0.5, transform: 'rotateY(180deg)' },
// config: { mass: 5, tension: 500, friction: 80 },
// }));

// // Handle mouse enter and leave for tilt effect
// const handleMouseEnter = () => set({ transform: 'rotateY(0deg)' });
// const handleMouseLeave = () => set({ transform: 'rotateY(180deg)' });

// return (
// <div style={{ perspective: '1000px', width: '20rem', height: '29rem' }}>
// <animated.div
// ref={containerRef}
// onMouseMove={handleMouseEnter}
// onMouseLeave={handleMouseLeave}
// style={{
// width: '100%',
// height: '100%',
// position: 'relative',
// transformStyle: 'preserve-3d', // Needed for 3D effect
// ...props,
// }}
// >
// {/* Front Side */}
// <animated.div
// style={{
// width: '100%',
// height: '100%',
// backgroundImage: `url(${frontImage})`,
// backgroundSize: 'cover',
// borderRadius: '5px',
// position: 'absolute',
// backfaceVisibility: 'hidden', // hide when flipped
// }}
// />

// {/* Back Side */}
// <animated.div
// style={{
// width: '100%',
// height: '100%',
// backgroundImage: `url(${placeholder})`,
// backgroundSize: 'cover',
// borderRadius: '5px',
// position: 'absolute',
// backfaceVisibility: 'hidden', // hide when flipped
// transform: 'rotateY(180deg)', // rotate backside by 180deg
// }}
// />
// </animated.div>
// </div>
// );
// };

// export default SingleCardAnimation;
Loading
Loading