perf(ui): optimize animations with hardware acceleration

- Replace transition-all with transition-transform for better performance
- Add will-change and transform properties to enable GPU acceleration
- Simplify marquee animation logic and remove unused hover state
- Remove unused skills and optimize animation variants
This commit is contained in:
joyzhao
2025-06-20 09:11:14 +08:00
parent c698d1ae45
commit 45299c1fa6
3 changed files with 32 additions and 57 deletions

View File

@@ -1,5 +1,4 @@
import { motion, useAnimation } from "framer-motion";
import { useState, useEffect } from "react";
import { motion, type Variants } from "framer-motion";
import { useTranslations } from "@/i18n/utils";
import { type SkillItem } from "@/types";
@@ -13,8 +12,6 @@ const allSkills: SkillItem[] = [
{ name: "JavaScript", icon: "javascript" },
{ name: "React", icon: "react" },
{ name: "Vue", icon: "vue" },
{ name: "微信小程序", icon: "wechat" },
{ name: "UniApp", icon: "vue" }, // Using vue icon as fallback
// Frontend Development
{ name: "Next.js", icon: "nextjs" },
@@ -29,7 +26,7 @@ const allSkills: SkillItem[] = [
{ name: "Node.js", icon: "nodejs" },
{ name: "Express.js", icon: "express" },
{ name: "Nest.js", icon: "nestjs" },
{ name: "Fastify.js", icon: "fastify" },
// { name: "Fastify.js", icon: "fastify" },
{ name: "Hono.js", icon: "nodejs" }, // Using nodejs icon as fallback
// Database & Storage
@@ -75,6 +72,7 @@ function SkillItem({ skill }: { skill: SkillItem }) {
/**
* Marquee row component that scrolls skills horizontally
* Optimized for performance with constant animation
*/
function MarqueeRow({ skills, direction = "left", speed = 50 }: {
skills: SkillItem[];
@@ -83,57 +81,34 @@ function MarqueeRow({ skills, direction = "left", speed = 50 }: {
}) {
// Duplicate skills array to create seamless infinite scroll
const duplicatedSkills = [...skills, ...skills];
// State to control animation play/pause
const [isHovered, setIsHovered] = useState(false);
const controls = useAnimation();
// Start animation on component mount
useEffect(() => {
const startAnimation = () => {
controls.start({
x: direction === "left" ? ["-50%", "0%"] : ["0%", "-50%"],
transition: {
x: {
repeat: Infinity,
repeatType: "loop",
duration: speed,
ease: "linear"
}
// Define animation variants to ensure type safety with hardware acceleration
const variants: Variants = {
animate: {
transform: direction === "left" ? ["translateX(0%)", "translateX(-50%)"] : ["translateX(-50%)", "translateX(0%)"],
transition: {
transform: {
repeat: Infinity,
repeatType: "loop" as const,
duration: speed,
ease: [0, 0, 1, 1] // 使用数组形式的 linear easing
}
});
};
startAnimation();
}, [controls, direction, speed]);
// Handle hover state changes
useEffect(() => {
if (isHovered) {
controls.stop();
} else {
controls.start({
x: direction === "left" ? ["-50%", "0%"] : ["0%", "-50%"],
transition: {
x: {
repeat: Infinity,
repeatType: "loop",
duration: speed,
ease: "linear"
}
}
});
}
}
}, [isHovered, controls, direction, speed]);
};
return (
<div
className="overflow-hidden"
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<div className="overflow-hidden">
<motion.div
className="flex gap-4 w-fit"
animate={controls}
variants={variants}
animate="animate"
style={{
willChange: "transform",
backfaceVisibility: "hidden",
WebkitBackfaceVisibility: "hidden",
WebkitFontSmoothing: "subpixel-antialiased"
}}
>
{duplicatedSkills.map((skill, index) => (
<SkillItem key={`${skill.name}-${index}`} skill={skill} />