|
89 | 89 | } |
90 | 90 |
|
91 | 91 | .cert-card:hover { |
92 | | - transform: translateY(-5px); |
| 92 | + transform: translateY(5px); |
93 | 93 | box-shadow: 0 15px 40px rgba(255, 107, 71, 0.15); |
94 | 94 | } |
95 | 95 |
|
|
117 | 117 | color: white; |
118 | 118 | } |
119 | 119 |
|
| 120 | + /* Enhanced project card animations */ |
| 121 | + .project-card { |
| 122 | + transform-style: preserve-3d; |
| 123 | + perspective: 1000px; |
| 124 | + will-change: transform, opacity; |
| 125 | + } |
| 126 | + |
| 127 | + .project-card img { |
| 128 | + will-change: transform, scale; |
| 129 | + backface-visibility: hidden; |
| 130 | + } |
| 131 | + |
| 132 | + .project-card.in-view { |
| 133 | + /* Optimized rendering for cards in view */ |
| 134 | + } |
| 135 | + |
| 136 | + @media (prefers-reduced-motion: reduce) { |
| 137 | + .project-card { |
| 138 | + transform: none !important; |
| 139 | + } |
| 140 | + .project-card img { |
| 141 | + transform: none !important; |
| 142 | + } |
| 143 | + } |
| 144 | + |
| 145 | + /* Performance optimizations */ |
| 146 | + .project-card, .skill-card { |
| 147 | + contain: layout style paint; |
| 148 | + } |
| 149 | + |
120 | 150 | /* Custom scrollbar */ |
121 | 151 | ::-webkit-scrollbar { |
122 | 152 | width: 6px; |
@@ -785,13 +815,13 @@ <h4 class="font-medium mb-4">Follow Me</h4> |
785 | 815 | } |
786 | 816 |
|
787 | 817 | // Certificate filtering |
788 | | - function filterCertificates(category) { |
789 | | - const cards = document.querySelectorAll('.cert-card'); |
| 818 | + function filterCertificates(category, buttonElement) { |
| 819 | + const cards = document.querySelectorAll('[data-categories]'); |
790 | 820 | const filterButtons = document.querySelectorAll('.filter-btn'); |
791 | 821 |
|
792 | 822 | // Update active filter button |
793 | 823 | filterButtons.forEach(btn => btn.classList.remove('active')); |
794 | | - event.target.classList.add('active'); |
| 824 | + buttonElement.classList.add('active'); |
795 | 825 |
|
796 | 826 | cards.forEach(card => { |
797 | 827 | const cardCategory = card.dataset.categories; |
@@ -835,15 +865,15 @@ <h4 class="font-medium mb-4">Follow Me</h4> |
835 | 865 | function createCertificateCard(cert, index) { |
836 | 866 | return new Promise((resolve) => { |
837 | 867 | const card = document.createElement('div'); |
838 | | - card.className = 'cert-card opacity-0 transform translate-y-4 transition-all duration-300 ease-in-out'; |
| 868 | + card.className = 'opacity-0 transform translate-y-4 transition-all duration-300 ease-in-out cert-card'; |
839 | 869 | card.dataset.categories = cert.category; |
840 | 870 |
|
841 | 871 | card.innerHTML = ` |
842 | 872 | <div class="card rounded-lg overflow-hidden"> |
843 | 873 | <div class="relative h-64 overflow-hidden"> |
844 | 874 | <iframe class="cert-iframe" src="${cert.path}"></iframe> |
845 | 875 | <div class="absolute inset-0 bg-gradient-to-t from-black/70 via-black/0 to-transparent transform translate-y-full transition-transform duration-300 p-4 flex flex-col justify-end"> |
846 | | - <div class="text-emerald-500 font-medium text-sm">${cert.title}</div> |
| 876 | + <div class="text-black font-medium text-sm">${cert.title}</div> |
847 | 877 | </div> |
848 | 878 | </div> |
849 | 879 | <div class="p-4"> |
@@ -900,7 +930,8 @@ <h4 class="font-medium mb-4">Follow Me</h4> |
900 | 930 | // Filter button event listeners |
901 | 931 | document.querySelectorAll('.filter-btn').forEach(btn => { |
902 | 932 | btn.addEventListener('click', (e) => { |
903 | | - filterCertificates(e.target.dataset.filter); |
| 933 | + const category = e.target.dataset.filter; |
| 934 | + filterCertificates(category, e.target); |
904 | 935 | }); |
905 | 936 | }); |
906 | 937 | }); |
@@ -1000,34 +1031,181 @@ <h4 class="font-medium mb-4">Follow Me</h4> |
1000 | 1031 | }); |
1001 | 1032 | }); |
1002 | 1033 |
|
1003 | | - // Projects section animations |
| 1034 | + // Optimized Projects section animations |
| 1035 | + |
| 1036 | + // Enhanced title animation with text reveal effect |
| 1037 | + gsap.set('#projects-title', { opacity: 0, y: 50 }); |
1004 | 1038 | gsap.to('#projects-title', { |
1005 | 1039 | scrollTrigger: { |
1006 | 1040 | trigger: '#projects', |
1007 | 1041 | start: 'top 80%', |
| 1042 | + end: 'top 50%', |
1008 | 1043 | toggleActions: 'play none none reset' |
1009 | 1044 | }, |
1010 | 1045 | opacity: 1, |
1011 | 1046 | y: 0, |
1012 | | - duration: 0.8, |
1013 | | - ease: 'power2.out' |
| 1047 | + duration: 1, |
| 1048 | + ease: 'power3.out' |
1014 | 1049 | }); |
1015 | 1050 |
|
1016 | | - gsap.utils.toArray('.project-card').forEach((card, i) => { |
1017 | | - gsap.to(card, { |
1018 | | - scrollTrigger: { |
1019 | | - trigger: card, |
1020 | | - start: 'top 90%', |
1021 | | - toggleActions: 'play none none reset' |
1022 | | - }, |
| 1051 | + // Advanced project cards animation with improved performance |
| 1052 | + gsap.set('.project-card', { |
| 1053 | + opacity: 0, |
| 1054 | + y: 80, |
| 1055 | + scale: 0.8, |
| 1056 | + rotationX: 15 |
| 1057 | + }); |
| 1058 | + |
| 1059 | + // Create timeline for better performance and control |
| 1060 | + const projectTimeline = gsap.timeline({ |
| 1061 | + scrollTrigger: { |
| 1062 | + trigger: '#projects .grid', |
| 1063 | + start: 'top 85%', |
| 1064 | + end: 'bottom 20%', |
| 1065 | + toggleActions: 'play none none reset' |
| 1066 | + } |
| 1067 | + }); |
| 1068 | + |
| 1069 | + // Animate project cards in batches for better performance |
| 1070 | + const projectCards = gsap.utils.toArray('.project-card'); |
| 1071 | + const batchSize = 3; // Animate 3 cards at a time |
| 1072 | + |
| 1073 | + for (let i = 0; i < projectCards.length; i += batchSize) { |
| 1074 | + const batch = projectCards.slice(i, i + batchSize); |
| 1075 | + |
| 1076 | + projectTimeline.to(batch, { |
1023 | 1077 | opacity: 1, |
1024 | 1078 | y: 0, |
| 1079 | + scale: 1, |
| 1080 | + rotationX: 0, |
1025 | 1081 | duration: 0.8, |
1026 | | - delay: i * 0.2, |
1027 | | - ease: 'power2.out' |
| 1082 | + ease: 'power3.out', |
| 1083 | + stagger: { |
| 1084 | + amount: 0.3, |
| 1085 | + from: 'start', |
| 1086 | + ease: 'power2.out' |
| 1087 | + } |
| 1088 | + }, i === 0 ? 0 : '-=0.4'); // Overlap batches slightly |
| 1089 | + } |
| 1090 | + |
| 1091 | + // Enhanced individual card hover animations with better performance |
| 1092 | + projectCards.forEach((card, index) => { |
| 1093 | + const image = card.querySelector('img'); |
| 1094 | + const techTags = card.querySelectorAll('.tech-tag'); |
| 1095 | + |
| 1096 | + // Create hover timeline for each card |
| 1097 | + const hoverTl = gsap.timeline({ paused: true }); |
| 1098 | + |
| 1099 | + hoverTl |
| 1100 | + .to(card, { |
| 1101 | + scale: 1.05, |
| 1102 | + rotationX: -2, |
| 1103 | + rotationY: 2, |
| 1104 | + z: 50, |
| 1105 | + duration: 0.4, |
| 1106 | + ease: 'power2.out' |
| 1107 | + }, 0) |
| 1108 | + .to(image, { |
| 1109 | + scale: 1.1, |
| 1110 | + duration: 0.4, |
| 1111 | + ease: 'power2.out' |
| 1112 | + }, 0) |
| 1113 | + .to(techTags, { |
| 1114 | + y: -5, |
| 1115 | + duration: 0.3, |
| 1116 | + ease: 'back.out(1.7)', |
| 1117 | + stagger: 0.05 |
| 1118 | + }, 0.1); |
| 1119 | + |
| 1120 | + // Mouse events with debouncing for performance |
| 1121 | + let hoverTimeout; |
| 1122 | + |
| 1123 | + card.addEventListener('mouseenter', () => { |
| 1124 | + clearTimeout(hoverTimeout); |
| 1125 | + hoverTl.play(); |
| 1126 | + }); |
| 1127 | + |
| 1128 | + card.addEventListener('mouseleave', () => { |
| 1129 | + hoverTimeout = setTimeout(() => { |
| 1130 | + hoverTl.reverse(); |
| 1131 | + }, 50); |
| 1132 | + }); |
| 1133 | + |
| 1134 | + // Add parallax effect on mouse move |
| 1135 | + card.addEventListener('mousemove', (e) => { |
| 1136 | + const rect = card.getBoundingClientRect(); |
| 1137 | + const centerX = rect.left + rect.width / 2; |
| 1138 | + const centerY = rect.top + rect.height / 2; |
| 1139 | + |
| 1140 | + const deltaX = (e.clientX - centerX) / rect.width; |
| 1141 | + const deltaY = (e.clientY - centerY) / rect.height; |
| 1142 | + |
| 1143 | + gsap.to(card, { |
| 1144 | + rotationX: deltaY * -10, |
| 1145 | + rotationY: deltaX * 10, |
| 1146 | + duration: 0.3, |
| 1147 | + ease: 'power2.out', |
| 1148 | + overwrite: 'auto' |
| 1149 | + }); |
| 1150 | + }); |
| 1151 | + |
| 1152 | + card.addEventListener('mouseleave', () => { |
| 1153 | + gsap.to(card, { |
| 1154 | + rotationX: 0, |
| 1155 | + rotationY: 0, |
| 1156 | + duration: 0.5, |
| 1157 | + ease: 'power2.out' |
| 1158 | + }); |
1028 | 1159 | }); |
1029 | 1160 | }); |
1030 | 1161 |
|
| 1162 | + // Add intersection observer for performance optimization |
| 1163 | + const observerOptions = { |
| 1164 | + root: null, |
| 1165 | + rootMargin: '100px', |
| 1166 | + threshold: 0.1 |
| 1167 | + }; |
| 1168 | + |
| 1169 | + const projectObserver = new IntersectionObserver((entries) => { |
| 1170 | + entries.forEach(entry => { |
| 1171 | + if (entry.isIntersecting) { |
| 1172 | + entry.target.classList.add('in-view'); |
| 1173 | + } else { |
| 1174 | + entry.target.classList.remove('in-view'); |
| 1175 | + } |
| 1176 | + }); |
| 1177 | + }, observerOptions); |
| 1178 | + |
| 1179 | + // Observe all project cards |
| 1180 | + projectCards.forEach(card => { |
| 1181 | + projectObserver.observe(card); |
| 1182 | + }); |
| 1183 | + |
| 1184 | + // Add scroll-triggered parallax for project images |
| 1185 | + projectCards.forEach((card, index) => { |
| 1186 | + const image = card.querySelector('img'); |
| 1187 | + if (image) { |
| 1188 | + gsap.to(image, { |
| 1189 | + scrollTrigger: { |
| 1190 | + trigger: card, |
| 1191 | + start: 'top bottom', |
| 1192 | + end: 'bottom top', |
| 1193 | + scrub: 1, |
| 1194 | + onUpdate: self => { |
| 1195 | + // Only animate if card is in view for performance |
| 1196 | + if (card.classList.contains('in-view')) { |
| 1197 | + const progress = self.progress; |
| 1198 | + gsap.set(image, { |
| 1199 | + y: (progress - 0.5) * 20, |
| 1200 | + scale: 1 + (Math.sin(progress * Math.PI) * 0.05) |
| 1201 | + }); |
| 1202 | + } |
| 1203 | + } |
| 1204 | + } |
| 1205 | + }); |
| 1206 | + } |
| 1207 | + }); |
| 1208 | + |
1031 | 1209 | // Certificates section animations |
1032 | 1210 | gsap.to('#certificates-title', { |
1033 | 1211 | scrollTrigger: { |
@@ -1071,13 +1249,23 @@ <h4 class="font-medium mb-4">Follow Me</h4> |
1071 | 1249 | }); |
1072 | 1250 | }); |
1073 | 1251 |
|
1074 | | - // Enhanced hover animations |
1075 | | - document.querySelectorAll('.card').forEach(card => { |
| 1252 | + // Enhanced hover animations for non-project cards |
| 1253 | + document.querySelectorAll('.card:not(.project-card)').forEach(card => { |
1076 | 1254 | card.addEventListener('mouseenter', () => { |
1077 | | - gsap.to(card, { scale: 1.02, duration: 0.3, ease: 'power2.out' }); |
| 1255 | + gsap.to(card, { |
| 1256 | + scale: 1.02, |
| 1257 | + y: -5, |
| 1258 | + duration: 0.3, |
| 1259 | + ease: 'power2.out' |
| 1260 | + }); |
1078 | 1261 | }); |
1079 | 1262 | card.addEventListener('mouseleave', () => { |
1080 | | - gsap.to(card, { scale: 1, duration: 0.3, ease: 'power2.out' }); |
| 1263 | + gsap.to(card, { |
| 1264 | + scale: 1, |
| 1265 | + y: 0, |
| 1266 | + duration: 0.3, |
| 1267 | + ease: 'power2.out' |
| 1268 | + }); |
1081 | 1269 | }); |
1082 | 1270 | }); |
1083 | 1271 |
|
|
0 commit comments