Framer Motion动画实战:用Next.js打造炫酷登录页(附完整代码)

Framer Motion动画实战:用Next.js打造炫酷登录页(附完整代码)

在当今追求极致用户体验的前端开发领域,动画已不再是锦上添花的装饰,而是提升用户参与度和品牌专业度的关键要素。Framer Motion作为React生态中最强大的动画库之一,以其声明式API和流畅的性能表现,成为众多Next.js开发者的首选工具。本文将带您从零开始,构建一个具有专业水准的动画登录页面,涵盖页面加载、表单交互和状态过渡等核心场景,每个环节都配有可直接复用的代码示例。

1. 项目初始化与基础配置

1.1 创建Next.js项目

我们首先使用最新版本的Next.js初始化项目,推荐选择TypeScript模板以获得更好的开发体验:

npx create-next-app@latest motion-login --typescript
cd motion-login

安装Framer Motion核心库及其常用辅助工具:

npm install framer-motion @types/framer-motion

1.2 基础动画组件封装

创建一个可复用的动画组件能显著提升开发效率。在components/AnimatedBox.tsx中:

import { motion, Variants } from 'framer-motion'

const fadeInUp: Variants = {
  hidden: { opacity: 0, y: 20 },
  visible: { opacity: 1, y: 0 }
}

export function AnimatedBox({ children, delay = 0 }) {
  return (
    <motion.div
      initial="hidden"
      animate="visible"
      variants={fadeInUp}
      transition={{ duration: 0.6, delay }}
    >
      {children}
    </motion.div>
  )
}

这个组件实现了常见的淡入上移动画效果,通过delay参数可以控制动画的延迟时间。

2. 登录页骨架与入场动画

2.1 页面布局结构

pages/index.tsx中构建基础布局:

import { AnimatedBox } from '../components/AnimatedBox'

export default function LoginPage() {
  return (
    <div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
      <main className="container mx-auto px-4 py-16">
        <AnimatedBox>
          <div className="max-w-md mx-auto bg-white rounded-xl shadow-lg overflow-hidden">
            {/* 内容将在这里添加 */}
          </div>
        </AnimatedBox>
      </main>
    </div>
  )
}

2.2 分阶段入场动画

使用staggerChildren实现元素的顺序出现效果:

const containerVariants: Variants = {
  hidden: { opacity: 0 },
  visible: {
    opacity: 1,
    transition: {
      staggerChildren: 0.1
    }
  }
}

const itemVariants: Variants = {
  hidden: { opacity: 0, y: 20 },
  visible: { opacity: 1, y: 0 }
}

function LoginForm() {
  return (
    <motion.div 
      variants={containerVariants}
      initial="hidden"
      animate="visible"
      className="p-8"
    >
      <motion.div variants={itemVariants} className="text-center mb-8">
        <h1 className="text-3xl font-bold text-gray-800">欢迎回来</h1>
      </motion.div>
      
      {/* 表单元素将在这里添加 */}
    </motion.div>
  )
}

3. 表单交互动画实现

3.1 输入框焦点动画

为输入框添加聚焦和失焦时的动画反馈:

function AnimatedInput({ label, ...props }) {
  return (
    <motion.div className="mb-6">
      <label className="block text-sm font-medium text-gray-700 mb-1">
        {label}
      </label>
      <motion.div
        whileFocus={{ 
          boxShadow: "0 0 0 2px rgba(99, 102, 241, 0.5)",
          borderColor: "#6366f1"
        }}
        className="relative"
      >
        <input
          className="w-full px-4 py-2 rounded-lg border border-gray-300 focus:outline-none"
          {...props}
        />
      </motion.div>
    </motion.div>
  )
}

3.2 按钮点击效果

创建具有按压效果的登录按钮:

<motion.button
  whileHover={{ scale: 1.02 }}
  whileTap={{ scale: 0.98 }}
  className="w-full bg-indigo-600 text-white py-3 rounded-lg font-medium"
>
  登录
</motion.button>

4. 高级动画技巧应用

4.1 加载状态动画

为表单提交添加加载动画:

const [isLoading, setIsLoading] = useState(false)

<motion.button
  animate={isLoading ? "loading" : "idle"}
  variants={{
    idle: { width: "100%" },
    loading: { width: "48px" }
  }}
  onClick={() => setIsLoading(true)}
  className="relative h-12 bg-indigo-600 text-white rounded-lg font-medium overflow-hidden"
>
  <motion.span
    animate={isLoading ? "hidden" : "visible"}
    variants={{
      hidden: { opacity: 0 },
      visible: { opacity: 1 }
    }}
    className="absolute inset-0 flex items-center justify-center"
  >
    登录
  </motion.span>
  
  {isLoading && (
    <motion.div
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      className="absolute inset-0 flex items-center justify-center"
    >
      <div className="w-6 h-6 border-2 border-white border-t-transparent rounded-full animate-spin" />
    </motion.div>
  )}
</motion.button>

4.2 错误状态动画

表单验证错误的动画反馈:

const [error, setError] = useState("")

<motion.div
  animate={error ? "visible" : "hidden"}
  variants={{
    visible: { opacity: 1, height: "auto" },
    hidden: { opacity: 0, height: 0 }
  }}
  className="overflow-hidden"
>
  <div className="mt-2 text-sm text-red-600 bg-red-50 p-3 rounded-lg">
    {error}
  </div>
</motion.div>

5. 页面过渡与路由动画

5.1 页面切换过渡

_app.tsx中添加全局页面过渡效果:

import { AnimatePresence } from 'framer-motion'

function MyApp({ Component, pageProps, router }) {
  return (
    <AnimatePresence mode="wait">
      <motion.div
        key={router.route}
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        exit={{ opacity: 0 }}
        transition={{ duration: 0.3 }}
      >
        <Component {...pageProps} />
      </motion.div>
    </AnimatePresence>
  )
}

5.2 成功登录后的跳转动画

登录成功后的动画过渡:

const [isSuccess, setIsSuccess] = useState(false)

if (isSuccess) {
  return (
    <motion.div
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      className="text-center py-16"
    >
      <motion.div
        animate={{ 
          scale: [1, 1.1, 1],
          rotate: [0, 5, -5, 0]
        }}
        transition={{ duration: 0.6 }}
        className="inline-block mb-6"
      >
        <CheckCircleIcon className="h-16 w-16 text-green-500" />
      </motion.div>
      <h2 className="text-2xl font-bold text-gray-800 mb-2">登录成功</h2>
      <p className="text-gray-600">正在跳转到仪表盘...</p>
    </motion.div>
  )
}

6. 性能优化与最佳实践

6.1 动画性能优化技巧

  • 优先使用transformopacity属性进行动画,它们不会触发重排
  • 对于复杂动画,使用will-change: transform提示浏览器优化
  • 避免同时动画过多元素,可以使用staggerChildren控制节奏

6.2 移动端适配

const isMobile = useMediaQuery("(max-width: 768px)")

<motion.div
  animate={{
    x: isMobile ? 0 : 100,
    transition: { type: "spring", damping: 10 }
  }}
/>

7. 完整代码整合

将所有组件整合到完整的登录页面中:

import { useState } from 'react'
import { motion, AnimatePresence, Variants } from 'framer-motion'
import { CheckCircleIcon } from '@heroicons/react/24/outline'

const containerVariants: Variants = {
  hidden: { opacity: 0 },
  visible: {
    opacity: 1,
    transition: {
      staggerChildren: 0.1
    }
  }
}

const itemVariants: Variants = {
  hidden: { opacity: 0, y: 20 },
  visible: { opacity: 1, y: 0 }
}

export default function LoginPage() {
  const [isLoading, setIsLoading] = useState(false)
  const [error, setError] = useState("")
  const [isSuccess, setIsSuccess] = useState(false)

  const handleSubmit = (e) => {
    e.preventDefault()
    setIsLoading(true)
    
    // 模拟API调用
    setTimeout(() => {
      setIsLoading(false)
      setIsSuccess(true)
    }, 1500)
  }

  if (isSuccess) {
    return (
      <motion.div
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        className="text-center py-16"
      >
        {/* 成功状态动画 */}
      </motion.div>
    )
  }

  return (
    <div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
      <main className="container mx-auto px-4 py-16">
        <motion.div
          initial={{ opacity: 0, y: 20 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.6 }}
          className="max-w-md mx-auto bg-white rounded-xl shadow-lg overflow-hidden"
        >
          <motion.form 
            onSubmit={handleSubmit}
            variants={containerVariants}
            initial="hidden"
            animate="visible"
            className="p-8"
          >
            <motion.div variants={itemVariants} className="text-center mb-8">
              <h1 className="text-3xl font-bold text-gray-800">欢迎回来</h1>
              <p className="text-gray-500 mt-2">请登录您的账户</p>
            </motion.div>

            <motion.div variants={itemVariants}>
              <AnimatedInput 
                label="邮箱地址" 
                type="email" 
                placeholder="your@email.com" 
                required 
              />
            </motion.div>

            <motion.div variants={itemVariants}>
              <AnimatedInput 
                label="密码" 
                type="password" 
                placeholder="••••••••" 
                required 
              />
            </motion.div>

            <motion.div variants={itemVariants} className="mt-6">
              <button
                type="submit"
                className="w-full bg-indigo-600 text-white py-3 rounded-lg font-medium"
                disabled={isLoading}
              >
                {isLoading ? '登录中...' : '登录'}
              </button>
            </motion.div>

            <AnimatePresence>
              {error && (
                <motion.div
                  initial={{ opacity: 0, height: 0 }}
                  animate={{ opacity: 1, height: 'auto' }}
                  exit={{ opacity: 0, height: 0 }}
                  className="mt-4"
                >
                  <div className="text-sm text-red-600 bg-red-50 p-3 rounded-lg">
                    {error}
                  </div>
                </motion.div>
              )}
            </AnimatePresence>
          </motion.form>
        </motion.div>
      </main>
    </div>
  )
}
内容概要:本文围绕“基于改进秃鹰算法的微电网群经济优化调度”展开研究,提出了一种改进的秃鹰搜索算法(BES),旨在解决微电网群在复杂运行环境下的多目标、强约束、非线性及高维经济调度问题。通过引入特定优化策略,增强了基础算法的全局搜索能力和收敛效率,克服了传统智能算法易陷入局部最优的缺陷。研究构建了一个包含分布式电源、储能系统与多元负荷的微电网群调度模型,以最小化系统综合运行成本为核心目标,综合考虑功率平衡、设备出力能力、储能运行特性等多重约束条件。通过仿真实验验证了所提算法在调度精度、稳定性和计算效率方面相较于传统方法具有明显优势,并进一步展示了其在降低能源开支、提升可再生能源消纳水平方面的实际应用价值。; 适合人群:具备一定电力系统基础知识或优化算法背景,从事新能源调度、智能优化算法研究与应用等相关领域的研究生、科研人员及工程技术人员。; 使用场景及目标:①应用于微电网群、综合能源系统等场景下的经济调度优化;②为秃鹰算法及其他群体智能算法的改进、复现与性能对比提供参考范例;③服务于科研仿真、算法验证及工程化应用需求。; 阅读建议:建议读者结合文中提供的Matlab代码实现进行实践操作,重点关注算法改进机制与调度模型的构建逻辑,同时可借助网盘资源获取完整资料,以加深对算法性能表现与应用场景的理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值