news 2026/5/5 17:36:30

前端动画的多种实现方式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
前端动画的多种实现方式

动画的本质是把内容的两个状态做平滑的过渡(中间状态的展示)

import React, { useEffect, useRef, useState } from 'react'; import './styles.css'; // 动画实践 // 1.1 translation const TranslationDemo = () => { return ( <div style={{ padding: '20px', border: '1px solid #ccc', margin: '10px' }}> <h3>1.1 CSS Translation</h3> <div className="translation-box" style={{ width: '100px', height: '100px', backgroundColor: '#3498db', transition: 'transform 1s ease-in-out', cursor: 'pointer' }} onMouseEnter={(e) => { e.currentTarget.style.transform = 'translateX(200px)'; }} onMouseLeave={(e) => { e.currentTarget.style.transform = 'translateX(0)'; }} > Hover me </div> </div> ); }; // 1.2 animation + @keyframes const KeyframesDemo = () => { return ( <div style={{ padding: '20px', border: '1px solid #ccc', margin: '10px' }}> <h3>1.2 CSS Animation + @keyframes</h3> <div className="keyframes-box" style={{ width: '100px', height: '100px', backgroundColor: '#e74c3c', animation: 'bounce 2s infinite' }} > Bouncing </div> <style>{` @keyframes bounce { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-50px); } } `}</style> </div> ); }; // 2.1 requestAnimationFrame const RequestAnimationFrameDemo = () => { const boxRef = useRef<HTMLDivElement>(null); const [isAnimating, setIsAnimating] = useState(false); const animationRef = useRef<number>(); const animate = () => { if (boxRef.current) { const currentX = parseFloat(boxRef.current.style.left || '0'); if (currentX < 300) { boxRef.current.style.left = `${currentX + 2}px`; animationRef.current = requestAnimationFrame(animate); } else { boxRef.current.style.left = '0px'; setIsAnimating(false); } } }; const startAnimation = () => { if (!isAnimating) { setIsAnimating(true); animate(); } }; useEffect(() => { return () => { if (animationRef.current) { cancelAnimationFrame(animationRef.current); } }; }, []); return ( <div style={{ padding: '20px', border: '1px solid #ccc', margin: '10px' }}> <h3>2.1 requestAnimationFrame</h3> <div style={{ position: 'relative', height: '120px' }}> <div ref={boxRef} style={{ position: 'absolute', left: '0px', width: '100px', height: '100px', backgroundColor: '#2ecc71' }} > RAF </div> </div> <button onClick={startAnimation} disabled={isAnimating}> Start Animation </button> </div> ); }; // 3.1 Canvas + requestAnimationFrame const CanvasDemo = () => { const canvasRef = useRef<HTMLCanvasElement>(null); const animationRef = useRef<number>(); const xRef = useRef(0); const [isRunning, setIsRunning] = useState(false); const draw = () => { const canvas = canvasRef.current; if (!canvas) return; const ctx = canvas.getContext('2d'); if (!ctx) return; // 清空画布 ctx.clearRect(0, 0, canvas.width, canvas.height); // 绘制圆形 ctx.fillStyle = '#9b59b6'; ctx.beginPath(); ctx.arc(xRef.current, 50, 20, 0, Math.PI * 2); ctx.fill(); // 更新位置 xRef.current += 2; if (xRef.current > canvas.width) { xRef.current = 0; } animationRef.current = requestAnimationFrame(draw); }; const toggleAnimation = () => { if (isRunning) { if (animationRef.current) { cancelAnimationFrame(animationRef.current); } setIsRunning(false); } else { draw(); setIsRunning(true); } }; useEffect(() => { return () => { if (animationRef.current) { cancelAnimationFrame(animationRef.current); } }; }, []); return ( <div style={{ padding: '20px', border: '1px solid #ccc', margin: '10px' }}> <h3>3.1 Canvas + requestAnimationFrame</h3> <canvas ref={canvasRef} width={400} height={100} style={{ border: '1px solid #000' }} /> <div> <button onClick={toggleAnimation}> {isRunning ? 'Pause' : 'Start'} </button> </div> </div> ); }; // 4.1 SVG + CSS / SMIL / JS const SVGDemo = () => { const [rotate, setRotate] = useState(0); useEffect(() => { const interval = setInterval(() => { setRotate(prev => (prev + 5) % 360); }, 50); return () => clearInterval(interval); }, []); return ( <div style={{ padding: '20px', border: '1px solid #ccc', margin: '10px' }}> <h3>4.1 SVG Animation</h3> <svg width="200" height="200"> {/* CSS Animation */} <circle cx="50" cy="50" r="20" fill="#e67e22"> <animate attributeName="r" values="20;30;20" dur="2s" repeatCount="indefinite" /> </circle> {/* JS Animation */} <rect x="100" y="30" width="40" height="40" fill="#1abc9c" transform={`rotate(${rotate} 120 50)`} /> </svg> </div> ); }; // 5.1 GSAP const GSAPDemo = () => { const gsapRef = useRef<HTMLDivElement>(null); const animateWithGSAP = () => { // 注意: 需要安装 gsap 库: npm install gsap // import gsap from 'gsap'; // gsap.to(gsapRef.current, { x: 200, duration: 1, ease: 'power2.inOut' }); // 简化版演示 (不依赖GSAP库) if (gsapRef.current) { gsapRef.current.style.transition = 'transform 1s ease-in-out'; gsapRef.current.style.transform = 'translateX(200px)'; setTimeout(() => { if (gsapRef.current) { gsapRef.current.style.transform = 'translateX(0)'; } }, 1000); } }; return ( <div style={{ padding: '20px', border: '1px solid #ccc', margin: '10px' }}> <h3>5.1 GSAP (简化演示)</h3> <div ref={gsapRef} style={{ width: '100px', height: '100px', backgroundColor: '#f39c12' }} > GSAP </div> <button onClick={animateWithGSAP}>Animate</button> </div> ); }; // 5.2 Framer Motion const FramerMotionDemo = () => { const [isVisible, setIsVisible] = useState(true); return ( <div style={{ padding: '20px', border: '1px solid #ccc', margin: '10px' }}> <h3>5.2 Framer Motion (简化演示)</h3> {/* 注意: 需要安装 framer-motion: npm install framer-motion */} {/* import { motion } from 'framer-motion'; */} <div style={{ width: '100px', height: '100px', backgroundColor: '#c0392b', opacity: isVisible ? 1 : 0, transform: isVisible ? 'scale(1)' : 'scale(0.5)', transition: 'all 0.5s ease-in-out' }} > Framer </div> <button onClick={() => setIsVisible(!isVisible)}> Toggle </button> </div> ); }; // 6.1 Web Animations API const WebAnimationsAPIDemo = () => { const wapiRef = useRef<HTMLDivElement>(null); const animateWithWAPI = () => { if (wapiRef.current) { wapiRef.current.animate([ { transform: 'translateX(0) rotate(0deg)', backgroundColor: '#16a085' }, { transform: 'translateX(200px) rotate(360deg)', backgroundColor: '#27ae60' } ], { duration: 1000, easing: 'ease-in-out', iterations: 1, fill: 'forwards' }); setTimeout(() => { if (wapiRef.current) { wapiRef.current.animate([ { transform: 'translateX(200px) rotate(360deg)', backgroundColor: '#27ae60' }, { transform: 'translateX(0) rotate(0deg)', backgroundColor: '#16a085' } ], { duration: 1000, easing: 'ease-in-out', fill: 'forwards' }); } }, 1000); } }; return ( <div style={{ padding: '20px', border: '1px solid #ccc', margin: '10px' }}> <h3>6.1 Web Animations API</h3> <div ref={wapiRef} style={{ width: '100px', height: '100px', backgroundColor: '#16a085' }} > WAAPI </div> <button onClick={animateWithWAPI}>Animate</button> </div> ); }; // 主组件 const AnimationPractice = () => { return ( <div style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}> <h1>动画实践合集</h1> <TranslationDemo /> <KeyframesDemo /> <RequestAnimationFrameDemo /> <CanvasDemo /> <SVGDemo /> <GSAPDemo /> <FramerMotionDemo /> <WebAnimationsAPIDemo /> </div> ); }; export default AnimationPractice;

CSS

JS

Canvas(脱离DOM)

SVG(结构化+矢量)

动画库

浏览器Web Animations API

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/30 22:50:06

【AI编程】5分钟用AI复刻有BOSS战的《坦克大战》

友友们&#xff0c;又到了周末AI整活时间了现在AI编程工具已经能做到一句话的需求生成一个完整的项目了&#xff0c;今天我们用Trae solo来试着复刻小时候的游戏《坦克大战》&#xff0c;并且部署上线&#xff0c;下面是演示效果和地址&#xff1a;https://tanke-orpin.vercel.…

作者头像 李华
网站建设 2026/5/5 12:55:38

AutoGPT在碳排放计算工具开发中的自动化支持

AutoGPT在碳排放计算工具开发中的自动化支持 如今&#xff0c;企业在应对气候变化、履行ESG责任的过程中&#xff0c;碳排放核算早已不再是可有可无的“附加项”&#xff0c;而是关乎合规性、融资能力甚至品牌声誉的关键环节。然而&#xff0c;现实却令人沮丧&#xff1a;大多数…

作者头像 李华
网站建设 2026/5/4 6:01:15

69、Ubuntu与Linux网络资源全攻略

Ubuntu与Linux网络资源全攻略 一、Ubuntu安装与开发参与 Ubuntu的安装方式较为多样。你可以使用标准的Ubuntu CD进行安装,也能从官网下载ISO9660格式(文件名以 .iso 结尾)的镜像文件,然后将其刻录到700MB的CD - R或DVD上。若不想自行下载和刻录,还能通过 http://shipit.…

作者头像 李华
网站建设 2026/5/5 2:39:08

AutoGPT与Google Calendar联动:智能提醒系统构建

AutoGPT与Google Calendar联动&#xff1a;智能提醒系统构建 在信息过载、任务繁杂的现代工作环境中&#xff0c;很多人每天都在“追赶时间”——明明列了待办清单&#xff0c;却总是在最后一刻才开始准备会议&#xff1b;计划好要学习新技能&#xff0c;但总是被临时事务打断。…

作者头像 李华
网站建设 2026/5/1 0:33:20

NVIDIA Llama Nemotro 推理模型构建企业级 AI 智能体

总结分析&#xff1a;使用先进的开放式 NVIDIA Llama Nemotron 推理模型构建企业级 AI 智能体 核心主旨&#xff1a; 本文旨在宣布并详细介绍 NVIDIA 新推出的 Llama Nemotron 推理模型系列。该系列的核心目标是解决企业级 AI 智能体对强大推理能力的迫切需求&#xff0c;通过…

作者头像 李华