This commit is contained in:
@@ -1,16 +1,16 @@
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useRef, useLayoutEffect } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { CalendarOutlined } from '@ant-design/icons';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import styles from './activity.module.less';
|
||||
import { hoverScale } from '../../animation';
|
||||
|
||||
//
|
||||
const ActivityCard = ({ activity }) => {
|
||||
const navigate = useNavigate();
|
||||
const [isLoaded, setIsLoaded] = useState(false);
|
||||
const [hasError, setHasError] = useState(false);
|
||||
const imgRef = useRef(null);
|
||||
|
||||
const handleCardClick = () => {
|
||||
navigate(`/activity/${activity.id}`);
|
||||
@@ -33,6 +33,13 @@ const ActivityCard = ({ activity }) => {
|
||||
? 'https://via.placeholder.com/600x400?text=No+Image'
|
||||
: (activity.display_banner_url || activity.banner_url || activity.cover_image || 'https://via.placeholder.com/600x400');
|
||||
|
||||
// Check if image is already loaded (cached) to prevent flashing
|
||||
useLayoutEffect(() => {
|
||||
if (imgRef.current && imgRef.current.complete) {
|
||||
setIsLoaded(true);
|
||||
}
|
||||
}, [imgSrc]);
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
className={styles.activityCard}
|
||||
@@ -43,7 +50,7 @@ const ActivityCard = ({ activity }) => {
|
||||
style={{ willChange: 'transform' }}
|
||||
>
|
||||
<div className={styles.imageContainer}>
|
||||
{/* Placeholder / Skeleton Background */}
|
||||
{/* Placeholder Background - Always visible behind the image */}
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
@@ -51,26 +58,32 @@ const ActivityCard = ({ activity }) => {
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
backgroundColor: '#f0f0f0',
|
||||
opacity: isLoaded ? 0 : 1,
|
||||
transition: 'opacity 0.5s ease'
|
||||
backgroundColor: '#f5f5f5',
|
||||
zIndex: 0,
|
||||
}}
|
||||
/>
|
||||
|
||||
<motion.img
|
||||
<img
|
||||
ref={imgRef}
|
||||
src={imgSrc}
|
||||
alt={activity.title}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: isLoaded ? 1 : 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
style={{
|
||||
position: 'relative',
|
||||
zIndex: 1,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
objectFit: 'cover',
|
||||
opacity: isLoaded ? 1 : 0,
|
||||
transition: 'opacity 0.3s ease-out'
|
||||
}}
|
||||
onLoad={() => setIsLoaded(true)}
|
||||
onError={() => {
|
||||
setHasError(true);
|
||||
setIsLoaded(true); // Show placeholder image
|
||||
setIsLoaded(true);
|
||||
}}
|
||||
loading="lazy"
|
||||
/>
|
||||
<div className={styles.overlay}>
|
||||
<div className={styles.overlay} style={{ zIndex: 2 }}>
|
||||
<div className={styles.statusTag}>
|
||||
{activity.status || getStatus(activity.start_time)}
|
||||
</div>
|
||||
|
||||
@@ -66,20 +66,25 @@ const ActivityList = () => {
|
||||
User said: "Activity only shows one, and in the form of a sliding page"
|
||||
*/}
|
||||
<div className={styles.desktopCarousel}>
|
||||
<AnimatePresence mode='wait'>
|
||||
<AnimatePresence>
|
||||
<motion.div
|
||||
key={currentIndex}
|
||||
initial={{ opacity: 0, x: 50 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
exit={{ opacity: 0, x: -50 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
style={{ width: '100%' }}
|
||||
initial={{ x: '100%' }}
|
||||
animate={{ x: 0, zIndex: 1 }}
|
||||
exit={{ x: '-100%', zIndex: 0 }}
|
||||
transition={{ duration: 0.5, ease: "easeInOut" }}
|
||||
style={{
|
||||
width: '100%',
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
}}
|
||||
>
|
||||
<ActivityCard activity={activities[currentIndex]} />
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
|
||||
<div className={styles.dots}>
|
||||
<div className={styles.dots} style={{ position: 'absolute', bottom: '10px', width: '100%', zIndex: 10 }}>
|
||||
{activities.map((_, idx) => (
|
||||
<span
|
||||
key={idx}
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
.desktopCarousel {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 440px; /* 400px card + space for dots */
|
||||
overflow: hidden;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
|
||||
Reference in New Issue
Block a user