React+TypeScript+Tailwind 实现圣诞祝福网页

ops/2024/12/26 2:28:37/

圣诞节快要到啦,提前祝大家圣诞节快乐!!!

项目完整源码在最后哦✨


视频


(一):项目环境搭建

在这个教程中,我们将一步步创建一个精美的圣诞祝福网页。本文是系列的第一部分,我们将完成项目的环境搭建。

环境准备

1. Node.js安装

  1. 访问 Node.js官网
  2. 下载并安装LTS(长期支持)版本
  3. 验证安装:
node --version
npm --version

2. 开发工具

  1. 安装VSCode:下载地址
  2. 安装必要的VSCode插件:
    • ESLint
    • Prettier
    • Tailwind CSS IntelliSense
    • TypeScript Vue Plugin (Volar)

项目创建

1. 使用Vite创建项目

打开终端,执行以下命令:

# 创建项目
npm create vite@latest christmas-wishes -- --template react-ts# 进入项目目录
cd christmas-wishes# 安装依赖
npm install

2. 安装必要依赖

# 安装Tailwind CSS
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p# 安装Framer Motion动画库
npm install framer-motion# 安装其他工具库
npm install classnames @types/node

项目配置

1. 配置TypeScript

创建/更新tsconfig.json

{"compilerOptions": {"target": "ES2020","useDefineForClassFields": true,"lib": ["ES2020", "DOM", "DOM.Iterable"],"module": "ESNext","skipLibCheck": true,"moduleResolution": "bundler","allowImportingTsExtensions": true,"resolveJsonModule": true,"isolatedModules": true,"noEmit": true,"jsx": "react-jsx","strict": true,"noUnusedLocals": true,"noUnusedParameters": true,"noFallthroughCasesInSwitch": true,"baseUrl": ".","paths": {"@/*": ["src/*"]}},"include": ["src"],"references": [{ "path": "./tsconfig.node.json" }]
}

2. 配置Tailwind CSS

  1. 更新tailwind.config.js
javascript">/** @type {import('tailwindcss').Config} */
export default {content: ["./index.html","./src/**/*.{js,ts,jsx,tsx}",],theme: {extend: {colors: {'christmas': {red: '#D42426',green: '#2F5233',gold: '#FFD700',},},animation: {'snow-fall': 'snow-fall 10s linear infinite','float': 'float 3s ease-in-out infinite','twinkle': 'twinkle 1.5s ease-in-out infinite','rotate': 'rotate 8s linear infinite','bounce-slow': 'bounce 3s infinite','sway': 'sway 6s ease-in-out infinite',},keyframes: {'snow-fall': {'0%': { transform: 'translateY(-10vh) translateX(0)' },'50%': { transform: 'translateY(45vh) translateX(20px)' },'100%': { transform: 'translateY(100vh) translateX(0)' },},'float': {'0%, 100%': { transform: 'translateY(0)' },'50%': { transform: 'translateY(-20px)' },},'twinkle': {'0%, 100%': { opacity: 1 },'50%': { opacity: 0.3 },},'rotate': {'0%': { transform: 'rotate(0deg)' },'100%': { transform: 'rotate(360deg)' },},'sway': {'0%, 100%': { transform: 'rotate(-3deg)' },'50%': { transform: 'rotate(3deg)' },},},},},plugins: [],
}
  1. 更新src/index.css
@tailwind base;
@tailwind components;
@tailwind utilities;@layer base {body {@apply bg-blue-950 text-white min-h-screen overflow-hidden;}
}@layer components {.christmas-button {@apply px-6 py-3 rounded-full bg-gradient-to-r from-christmas-red to-christmas-greentext-white font-bold shadow-lg transform transition-all duration-300hover:scale-105 hover:shadow-xl focus:outline-none focus:ring-2 focus:ring-christmas-gold focus:ring-opacity-50;}.christmas-input {@apply w-full px-4 py-2 rounded-lg bg-white/10 border border-white/20text-white placeholder-white/50 focus:outline-none focus:ring-2focus:ring-christmas-gold focus:border-transparenttransition-all duration-300;}
}

3. 创建项目结构

  1. 目录结构说明:
src/
├── assets/          # 静态资源
│   └── audio/       # 音频文件
├── components/      # React组件
├── utils/          # 工具函数
├── App.tsx         # 主应用组件
├── main.tsx        # 入口文件
└── index.css       # 全局样式

4. 添加音频资源

  1. 下载所需的音频文件:

    • 背景音乐:Merry Christmas Mr. Lawrence
  2. 将下载的文件放入src/assets/audio目录

5. 验证环境

  1. 启动开发服务器:
npm run dev
  1. 访问控制台输出的URL(通常是 http://localhost:5173)

  2. 确认是否看到Vite的默认页面

(二):核心组件实现

在第一部分中,我们完成了项目环境的搭建。现在,让我们开始实现核心组件。我们将一步步创建每个组件,并确保它们能够正常工作。

在这里插入图片描述

1. 交互式雪花效果

1.1 创建基础组件

创建文件src/components/InteractiveSnow.tsx

typescript">import { useEffect, useRef, useState } from 'react';
import { motion } from 'framer-motion';interface Snowflake {id: number;x: number;y: number;size: number;opacity: number;delay: number;
}export const InteractiveSnow = () => {const [snowflakes, setSnowflakes] = useState<Snowflake[]>([]);const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });const containerRef = useRef<HTMLDivElement>(null);const requestRef = useRef<number>();const previousTimeRef = useRef<number>();return (<div ref={containerRef}className="fixed inset-0 pointer-events-none overflow-hidden">{/* 雪花将在这里渲染 */}</div>);
};

1.2 生成雪花

添加雪花生成逻辑:

typescript">// 在InteractiveSnow组件中添加
useEffect(() => {// 生成50个雪花const flakes = Array.from({ length: 50 }, (_, i) => ({id: i,x: Math.random() * 100,y: Math.random() * 100,size: Math.random() * 8 + 10,opacity: Math.random() * 0.5 + 0.3,delay: Math.random() * 10,}));setSnowflakes(flakes);
}, []);

1.3 添加鼠标交互

实现鼠标跟踪:

typescript">// 在InteractiveSnow组件中添加
useEffect(() => {const handleMouseMove = (event: MouseEvent) => {if (containerRef.current) {const rect = containerRef.current.getBoundingClientRect();setMousePosition({x: event.clientX - rect.left,y: event.clientY - rect.top,});}};window.addEventListener('mousemove', handleMouseMove);return () => window.removeEventListener('mousemove', handleMouseMove);
}, []);

1.4 实现动画循环

添加动画更新逻辑:

typescript">// 在InteractiveSnow组件中添加
const animate = (time: number) => {if (previousTimeRef.current !== undefined) {setSnowflakes(prevFlakes => prevFlakes.map(flake => {let newX = flake.x;let newY = flake.y;// 基础下落运动newY = (newY + 0.2) % 100;// 计算与鼠标的距离const dx = (mousePosition.x / (containerRef.current?.clientWidth || 1)) * 100 - flake.x;const dy = (mousePosition.y / (containerRef.current?.clientHeight || 1)) * 100 - flake.y;const distance = Math.sqrt(dx * dx + dy * dy);// 鼠标交互效果if (distance < 30) {const angle = Math.atan2(dy, dx);const force = (30 - distance) / 30 * 0.15;newX += Math.cos(angle) * force;newY += Math.sin(angle) * force;}// 随机漂移newX += Math.sin(time / 2000 + flake.delay) * 0.05;// 确保在视口内newX = ((newX + 100) % 100);newY = ((newY + 100) % 100);return {...flake,x: newX,y: newY,};}));}previousTimeRef.current = time;requestRef.current = requestAnimationFrame(animate);
};useEffect(() => {requestRef.current = requestAnimationFrame(animate);return () => {if (requestRef.current) {cancelAnimationFrame(requestRef.current);}};
}, [mousePosition]);

1.5 渲染雪花

更新渲染部分:

typescript">// 在InteractiveSnow组件的return语句中
return (<div ref={containerRef}className="fixed inset-0 pointer-events-none overflow-hidden">{snowflakes.map((flake) => (<motion.divkey={flake.id}className="absolute text-white select-none"style={{left: `${flake.x}%`,top: `${flake.y}%`,fontSize: `${flake.size}px`,opacity: flake.opacity,filter: 'blur(0.3px)',}}>❄️</motion.div>))}</div>
);

2. 音频控制器

2.1 创建基础组件

创建文件src/components/AudioController.tsx

typescript">import { useEffect, useRef, useState } from 'react';export const AudioController: React.FC = () => {const [isPlaying, setIsPlaying] = useState(false);const audioRef = useRef<HTMLAudioElement | null>(null);return (<buttononClick={() => setIsPlaying(!isPlaying)}className="fixed top-4 right-4 z-50 p-2 rounded-full bg-white/10 hover:bg-white/20 transition-colors duration-300">{isPlaying ? '🔊' : '🔈'}</button>);
};

2.2 添加音频控制

更新音频控制逻辑:

typescript">// 在AudioController组件中添加
useEffect(() => {audioRef.current = new Audio('/src/assets/audio/Merry Christmas Mr. Lawrence.mp3');audioRef.current.loop = true;return () => {if (audioRef.current) {audioRef.current.pause();audioRef.current = null;}};
}, []);useEffect(() => {if (audioRef.current) {if (isPlaying) {audioRef.current.play().catch(() => {setIsPlaying(false);});} else {audioRef.current.pause();}}
}, [isPlaying]);

2.3 添加音效功能

创建文件src/utils/audio.ts

typescript">export const playBellSound = () => {const audio = new Audio('/src/assets/audio/bell-sound.mp3');audio.volume = 0.5;audio.play().catch(console.error);
};

3. 圣诞装饰元素

3.1 创建基础组件

创建文件src/components/ChristmasElements.tsx

typescript">import { motion } from 'framer-motion';export const Decorations = () => {return (<div className="fixed inset-0 pointer-events-none"><ChristmasTree /><Santa /><Gifts /><Stars /><Bells /><CandyCanes /><ChristmasSocks /></div>);
};

3.2 实现装饰元素

在同一文件中添加各个装饰元素:

typescript">const ChristmasTree = () => (<motion.divclassName="absolute bottom-10 left-10 text-6xl"animate={{y: [0, -10, 0],rotate: [-2, 2, -2],}}transition={{duration: 4,repeat: Infinity,ease: "easeInOut"}}>🎄</motion.div>
);const Santa = () => (<motion.divclassName="absolute top-20 right-20 text-5xl"animate={{x: [0, -100, 0],y: [0, 20, 0],}}transition={{duration: 10,repeat: Infinity,ease: "linear"}}>🎅</motion.div>
);const Gifts = () => (<motion.divclassName="absolute bottom-20 right-20 text-4xl"animate={{scale: [1, 1.1, 1],rotate: [-5, 5, -5],}}transition={{duration: 3,repeat: Infinity,ease: "easeInOut"}}>🎁</motion.div>
);// ... 其他装饰元素

3.3 添加更多装饰

继续添加其他装饰元素:

typescript">const Stars = () => (<motion.divclassName="absolute top-10 left-20 text-3xl"animate={{scale: [1, 1.2, 1],opacity: [1, 0.5, 1],}}transition={{duration: 2,repeat: Infinity,ease: "easeInOut"}}></motion.div>
);const Bells = () => (<motion.divclassName="absolute top-40 left-40 text-4xl"animate={{rotate: [-10, 10, -10],}}transition={{duration: 2,repeat: Infinity,ease: "easeInOut"}}>🔔</motion.div>
);const CandyCanes = () => (<motion.divclassName="absolute bottom-40 left-30 text-4xl"animate={{y: [0, -5, 0],}}transition={{duration: 3,repeat: Infinity,ease: "easeInOut"}}>🍬</motion.div>
);const ChristmasSocks = () => (<motion.divclassName="absolute bottom-30 right-40 text-4xl"animate={{rotate: [-5, 5, -5],}}transition={{duration: 4,repeat: Infinity,ease: "easeInOut"}}>🧦</motion.div>
);

(三):主应用实现

在前两个部分中,我们完成了项目环境搭建和核心组件的实现。现在,让我们实现主应用逻辑,将所有组件组合在一起,并添加祝福语生成功能。
在这里插入图片描述

1. 祝福语生成器

1.1 创建祝福语工具

创建文件src/utils/wishesGenerator.ts

typescript">const wishes = ["愿平安夜的星光为你点亮前行的道路,愿圣诞节的祝福为你带来温暖与希望。🌟✨","在这个飘雪的季节,愿圣诞老人把最美好的祝福悄悄放进你的梦里,伴你度过一个温馨难忘的圣诞节。🎅❄️","愿圣诞的钟声带给你欢乐,愿圣诞的雪花带给你纯净,愿圣诞的烛光带给你温暖。🔔❄️🕯️","圣诞快乐!愿你的生活如圣诞树般绚丽多彩,如圣诞铃铛般充满欢乐。🎄🔔","愿这个圣诞节带给你甜蜜的祝福和温馨的问候,让你感受到节日的快乐与温暖。🎁💝",
];export const getRandomWish = (): string => {const randomIndex = Math.floor(Math.random() * wishes.length);return wishes[randomIndex];
};export const playBellSound = () => {const audio = new Audio('/src/assets/audio/bell-sound.mp3');audio.volume = 0.5;audio.play().catch(console.error);
};

2. 主应用组件

2.1 创建主应用

更新src/App.tsx

typescript">import { useState } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import { Decorations } from './components/ChristmasElements'
import { AudioController } from './components/AudioController'
import { InteractiveSnow } from './components/InteractiveSnow'
import { getRandomWish, playBellSound } from './utils/wishesGenerator'function ChristmasWishes() {const [name, setName] = useState('')const [showWishes, setShowWishes] = useState(false)const [currentWish, setCurrentWish] = useState('')const handleSubmit = (e: React.FormEvent) => {e.preventDefault()if (name.trim()) {setCurrentWish(getRandomWish())setShowWishes(true)playBellSound()}}return (<div className="min-h-screen flex flex-col items-center justify-center p-4 relative overflow-hidden bg-gradient-to-br from-blue-950 via-blue-900 to-blue-950">{/* 背景效果 */}<div className="absolute inset-0 bg-[radial-gradient(circle_at_50%_50%,rgba(255,255,255,0.05),transparent)] pointer-events-none" /><div className="absolute inset-0 bg-gradient-to-b from-transparent via-blue-950/30 to-blue-950/50" />{/* 组件 */}<AudioController /><InteractiveSnow /><Decorations />{/* 主要内容 */}<div className="text-center z-10 max-w-2xl mx-auto px-4"><AnimatePresence mode="wait">{!showWishes ? (<motion.divkey="input"initial={{ opacity: 0, y: 20 }}animate={{ opacity: 1, y: 0 }}exit={{ opacity: 0, y: -20 }}><motion.h1className="text-4xl md:text-5xl font-bold mb-8 text-white"animate={{ scale: [1, 1.05, 1] }}transition={{ duration: 2, repeat: Infinity }}>🎄 圣诞快乐 🎄</motion.h1><p className="text-lg text-white/80 mb-8">分享你的圣诞祝福</p><form onSubmit={handleSubmit} className="space-y-6"><inputtype="text"value={name}onChange={(e) => setName(e.target.value)}placeholder="输入你的名字"className="christmas-input"required/><buttontype="submit"className="christmas-button">生成祝福</button></form></motion.div>) : (<motion.divkey="wishes"className="text-center"initial={{ opacity: 0, scale: 0.8 }}animate={{ opacity: 1, scale: 1 }}exit={{ opacity: 0, scale: 1.2 }}><div className="bg-white/10 backdrop-blur-sm rounded-xl p-8 shadow-xl"><p className="text-xl mb-4">{name}</p><p className="text-2xl mb-6">{currentWish}</p><buttononClick={() => setShowWishes(false)}className="christmas-button">返回</button></div></motion.div>)}</AnimatePresence></div></div>)
}export default ChristmasWishes

2.2 更新入口文件

更新src/main.tsx

typescript">import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'ReactDOM.createRoot(document.getElementById('root')!).render(<React.StrictMode><App /></React.StrictMode>,
)

总结

恭喜!你已经成功创建了一个完整的圣诞祝福网页应用。这个应用包含:

  1. 交互式雪花效果
  2. 背景音乐控制
  3. 圣诞装饰动画
  4. 祝福语生成功能
  5. 平滑的页面过渡
  6. 响应式设计
  7. 错误处理机制

项目源码:
GitHub链接
Gitee链接

祝你圣诞快乐!🎄✨


http://www.ppmy.cn/ops/144998.html

相关文章

【LuaFramework】服务器模块相关知识

目录 一、客户端代码 二、本地服务器代码 三、解决服务器无法多次接收客户端消息问题 一、客户端代码 连接本地服务器127.0.0.1:2012端口&#xff08;如何创本地服务器&#xff0c;放最后说&#xff09;&#xff0c;连接成功后会回调 协议号Connect是101&#xff0c;其他如下…

音视频学习(二十七):SRT协议

SRT&#xff08;Secure Reliable Transport&#xff09;是一种开源的网络传输协议&#xff0c;专为实时音视频数据传输设计&#xff0c;具有低延迟、高可靠性和安全性等特点。 核心功能 SRT协议旨在解决实时音视频传输中的网络抖动、丢包、延迟和安全问题&#xff0c;提供以下…

gitlab window如何设置ssh

在GitLab中设置SSH需要以下步骤&#xff1a; 在GitLab账户中&#xff0c;导航到“用户设置”下的“SSH密钥”部分。 生成SSH密钥对&#xff08;如果你还没有的话&#xff09;。在Windows上&#xff0c;你可以使用ssh-keygen命令来生成密钥。 在命令提示符或PowerShell中运行以…

自动控制系统综合与LabVIEW实现

自动控制系统综合是为了优化系统性能&#xff0c;确保其可靠性、稳定性和灵活性。常用方法包括动态性能优化、稳态误差分析、鲁棒性设计等。结合LabVIEW&#xff0c;可以通过图形化编程、高效数据采集与处理来实现系统综合。本文将阐述具体方法&#xff0c;并结合硬件选型提供实…

Pycharm配置PyQt 5

Pycharm配置PyQt 5 一、配置 打开Pycharm,点击File(文件)->Settings(设置): 弹出Settings(设置)配置框,在框中选择 Tools(工具)->External Tools(外部工具) 点击”+“号 在Name(名称)中输入一个名字 Program(程序)中选择安装的designer.exe 我的路径是D:\…

8.Java内置排序算法

10. Java内置排序算法 排序算法的分类 如何写一个通用的排序算法排序元素比较分治算法思想 排序算法选择的建议 O(n)排序算法的选择 1.插入排序性能最好、其次是选择排序&#xff0c;冒泡排序性能最差 2.选择排序不是稳定的排序算法 3.插入排序是最好的选择 4.对于大规模…

流量主微信小程序工具类去水印

工具类微信小程序流量主带后台管理&#xff0c;可开通广告&#xff0c;带自有后台管理&#xff0c;不借助第三方接口 介绍 支持抖音&#xff0c;小红书&#xff0c;哔哩哔哩视频水印去除&#xff0c;功能实现不借助第三方平台。可实现微信小程序流量主广告变现功能&#xff0c…

美国加州房价数据分析02

5. 特征工程 5.1重构数据集 承接上文提到的相似度排名&#xff0c;去掉部分无关的特征。 train_set.corr()["median_house_value"].sort_values(ascendingFalse)为了提高模型训练后的鲁棒性&#xff0c;即防止过拟合&#xff0c;不建议删除关联度最低几项特征&#…