mirror of
https://github.com/shadoll/vancouver.git
synced 2026-08-28 19:43:22 +00:00
feat: enhance mobile 3D effects for investment area cards based on visibility and device orientation
This commit is contained in:
+103
-22
@@ -105,51 +105,132 @@
|
|||||||
el.style.textShadow = '';
|
el.style.textShadow = '';
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}; // Device orientation effect for mobile - only active card
|
||||||
|
|
||||||
// Device orientation effect for mobile
|
|
||||||
const handleDeviceOrientation = (e) => {
|
const handleDeviceOrientation = (e) => {
|
||||||
if (window.innerWidth <= 768) {
|
if (window.innerWidth <= 768) {
|
||||||
const gamma = e.gamma || 0; // left/right tilt
|
const gamma = e.gamma || 0; // left/right tilt (-90 to 90)
|
||||||
const beta = e.beta || 0; // front/back tilt
|
const beta = e.beta || 0; // front/back tilt (-180 to 180)
|
||||||
|
|
||||||
|
// Limit the range and make it more subtle
|
||||||
|
const maxTilt = 10; // Maximum degrees
|
||||||
|
const rotateY = Math.max(-maxTilt, Math.min(maxTilt, gamma * 0.15));
|
||||||
|
const rotateX = Math.max(-maxTilt, Math.min(maxTilt, (beta - 90) * 0.08));
|
||||||
|
|
||||||
|
// Find the most visible card
|
||||||
|
let mostVisibleCard = null;
|
||||||
|
let bestVisibilityScore = 0;
|
||||||
|
|
||||||
|
areaCards.forEach(card => {
|
||||||
|
const rect = card.getBoundingClientRect();
|
||||||
|
const isVisible = rect.top < window.innerHeight && rect.bottom > 0;
|
||||||
|
|
||||||
|
if (isVisible) {
|
||||||
|
const viewportCenter = window.innerHeight / 2;
|
||||||
|
const cardCenter = rect.top + rect.height / 2;
|
||||||
|
const distanceFromCenter = Math.abs(cardCenter - viewportCenter);
|
||||||
|
const maxDistance = window.innerHeight / 2;
|
||||||
|
const visibilityScore = 1 - (distanceFromCenter / maxDistance);
|
||||||
|
|
||||||
|
if (visibilityScore > bestVisibilityScore) {
|
||||||
|
bestVisibilityScore = visibilityScore;
|
||||||
|
mostVisibleCard = card;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reset all cards first
|
||||||
areaCards.forEach(card => {
|
areaCards.forEach(card => {
|
||||||
const textElements = card.querySelectorAll('h3, p');
|
const textElements = card.querySelectorAll('h3, p');
|
||||||
textElements.forEach(el => {
|
textElements.forEach(el => {
|
||||||
const rotateX = beta * 0.1;
|
if (!el.style.transform.includes('translateZ')) { // Don't override scroll effects
|
||||||
const rotateY = gamma * 0.1;
|
el.style.transform = '';
|
||||||
el.style.transform = `rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;
|
el.style.textShadow = '';
|
||||||
el.style.textShadow = `${gamma * 0.1}px ${beta * 0.1}px ${Math.abs(gamma + beta) * 0.05}px rgba(0,0,0,0.3)`;
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Scroll effect for mobile (alternative to device orientation)
|
// Apply orientation effect only to the most visible card
|
||||||
|
if (mostVisibleCard && bestVisibilityScore > 0.4) {
|
||||||
|
const textElements = mostVisibleCard.querySelectorAll('h3, p');
|
||||||
|
textElements.forEach(el => {
|
||||||
|
// Combine with existing transform if present
|
||||||
|
const currentTransform = el.style.transform;
|
||||||
|
const orientationTransform = `rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;
|
||||||
|
|
||||||
|
if (currentTransform && currentTransform.includes('perspective')) {
|
||||||
|
// Merge with scroll effect
|
||||||
|
el.style.transform = currentTransform.replace('perspective(1000px)', `perspective(1000px) ${orientationTransform}`);
|
||||||
|
} else {
|
||||||
|
el.style.transform = `perspective(1000px) ${orientationTransform} translateZ(5px)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!el.style.textShadow) {
|
||||||
|
el.style.textShadow = `${rotateY * 0.2}px ${rotateX * 0.2}px ${Math.abs(rotateX + rotateY) * 0.1}px rgba(0,0,0,0.3)`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};// Vertical scroll-based 3D effect for mobile - only active card
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
if (window.innerWidth <= 768) {
|
if (window.innerWidth <= 768) {
|
||||||
const scrollY = window.scrollY;
|
let mostVisibleCard = null;
|
||||||
|
let bestVisibilityScore = 0;
|
||||||
|
|
||||||
|
// Find the card that's most centered in the viewport
|
||||||
areaCards.forEach((card, index) => {
|
areaCards.forEach((card, index) => {
|
||||||
const rect = card.getBoundingClientRect();
|
const rect = card.getBoundingClientRect();
|
||||||
const isVisible = rect.top < window.innerHeight && rect.bottom > 0;
|
const isVisible = rect.top < window.innerHeight && rect.bottom > 0;
|
||||||
|
|
||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
const scrollProgress = (window.innerHeight - rect.top) / (window.innerHeight + rect.height);
|
const viewportCenter = window.innerHeight / 2;
|
||||||
const textElements = card.querySelectorAll('h3, p');
|
const cardCenter = rect.top + rect.height / 2;
|
||||||
textElements.forEach(el => {
|
const distanceFromCenter = Math.abs(cardCenter - viewportCenter);
|
||||||
const translateX = Math.sin(scrollProgress * Math.PI + index) * 2;
|
|
||||||
const translateY = Math.cos(scrollProgress * Math.PI + index) * 1;
|
// Calculate visibility score (closer to center = higher score)
|
||||||
el.style.transform = `translateX(${translateX}px) translateY(${translateY}px)`;
|
const maxDistance = window.innerHeight / 2;
|
||||||
el.style.textShadow = `${translateX}px ${translateY}px ${Math.abs(translateX + translateY) * 2}px rgba(0,0,0,0.2)`;
|
const visibilityScore = 1 - (distanceFromCenter / maxDistance);
|
||||||
});
|
|
||||||
|
if (visibilityScore > bestVisibilityScore) {
|
||||||
|
bestVisibilityScore = visibilityScore;
|
||||||
|
mostVisibleCard = { card, index, rect, visibilityScore };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Reset all cards first
|
||||||
|
areaCards.forEach(card => {
|
||||||
|
const textElements = card.querySelectorAll('h3, p');
|
||||||
|
textElements.forEach(el => {
|
||||||
|
el.style.transform = '';
|
||||||
|
el.style.textShadow = '';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Apply effect only to the most visible card
|
||||||
|
if (mostVisibleCard && mostVisibleCard.visibilityScore > 0.3) { // Only if reasonably visible
|
||||||
|
const { card, rect, visibilityScore } = mostVisibleCard;
|
||||||
|
const viewportCenter = window.innerHeight / 2;
|
||||||
|
const cardCenter = rect.top + rect.height / 2;
|
||||||
|
const distanceFromCenter = cardCenter - viewportCenter;
|
||||||
|
const normalizedDistance = distanceFromCenter / (window.innerHeight / 2);
|
||||||
|
|
||||||
|
const textElements = card.querySelectorAll('h3, p');
|
||||||
|
textElements.forEach(el => {
|
||||||
|
// Enhanced effect for the active card
|
||||||
|
const rotateX = normalizedDistance * 12; // More pronounced effect
|
||||||
|
const translateZ = visibilityScore * 25; // Forward based on visibility
|
||||||
|
const shadowIntensity = visibilityScore * 5;
|
||||||
|
|
||||||
|
el.style.transform = `perspective(1000px) rotateX(${rotateX}deg) translateZ(${translateZ}px)`;
|
||||||
|
el.style.textShadow = `0px ${shadowIntensity}px ${shadowIntensity * 2}px rgba(0,0,0,0.4)`;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add event listeners with optimized approach
|
// Add event listeners with optimized approach
|
||||||
cardData.forEach(cardInfo => {
|
cardData.forEach(cardInfo => {
|
||||||
// Desktop mouse events
|
// Desktop mouse events only
|
||||||
cardInfo.element.addEventListener('mouseenter', () => handleMouseEnter(cardInfo));
|
cardInfo.element.addEventListener('mouseenter', () => handleMouseEnter(cardInfo));
|
||||||
cardInfo.element.addEventListener('mousemove', (e) => handleMouseMove(e, cardInfo));
|
cardInfo.element.addEventListener('mousemove', (e) => handleMouseMove(e, cardInfo));
|
||||||
cardInfo.element.addEventListener('mouseleave', () => handleMouseLeave(cardInfo));
|
cardInfo.element.addEventListener('mouseleave', () => handleMouseLeave(cardInfo));
|
||||||
|
|||||||
Reference in New Issue
Block a user