50天50个前端项目 - HTML/CSS和JavaScript实战合集
项目概述
这是一个全面的前端项目集合,包含50个独立的Web开发项目,每个项目都专注于不同的前端技术和交互效果。从基础的DOM操作到复杂的动画实现,这个合集涵盖了现代前端开发的各个方面。
功能特性
- 丰富的动画效果:包含3D盒子背景、旋转导航、模糊加载等多种动画
- 实用的交互组件:进度步骤、自定义滑块、待办列表、笔记应用等
- 响应式设计:所有项目都适配不同屏幕尺寸
- 纯前端实现:仅使用HTML、CSS和JavaScript,无后端依赖
- 模块化结构:每个项目独立,便于学习和复用
- 现代API应用:包含Fetch API、Canvas绘图、本地存储等现代Web技术
安装指南
环境要求
- 现代Web浏览器(Chrome、Firefox、Safari、Edge等)
- 文本编辑器(VS Code、Sublime Text等)
- 本地服务器(可选,用于某些需要HTTP协议的功能)
使用步骤
- 克隆或下载项目到本地
- 进入任意项目文件夹
- 用浏览器打开HTML文件即可运行
- 或使用本地服务器:
# 使用Python python -m http.server 8000 # 使用Node.js npx http-server
使用说明
基础项目示例
扩展卡片(Expanding Cards)
点击不同的卡片实现展开和收缩效果:
<div class="container">
<div class="panel active" style="background-image: url('image1.jpg')">
<h3>标题1</h3>
</div>
<div class="panel" style="background-image: url('image2.jpg')">
<h3>标题2</h3>
</div>
</div>
const panels = document.querySelectorAll('.panel')
panels.forEach(panel => {
panel.addEventListener('click', () => {
removeActiveClasses()
panel.classList.add('active')
})
})
function removeActiveClasses() {
panels.forEach(panel => {
panel.classList.remove('active')
})
}
进度步骤(Progress Steps)
实现多步骤表单的进度指示:
const progress = document.getElementById('progress')
const prev = document.getElementById('prev')
const next = document.getElementById('next')
const circles = document.querySelectorAll('.circle')
let currentActive = 1
next.addEventListener('click', () => {
currentActive++
if(currentActive > circles.length) {
currentActive = circles.length
}
update()
})
function update() {
circles.forEach((circle, idx) => {
if(idx < currentActive) {
circle.classList.add('active')
} else {
circle.classList.remove('active')
}
})
const actives = document.querySelectorAll('.active')
progress.style.width = (actives.length - 1) / (circles.length - 1) * 100 + '%'
}
API集成项目
GitHub个人资料搜索
集成GitHub API实现用户搜索:
const APIURL = 'https://api.github.com/users/'
async function getUser(username) {
try {
const { data } = await axios(APIURL + username)
createUserCard(data)
getRepos(username)
} catch(err) {
if(err.response.status == 404) {
createErrorCard('未找到该用户名')
}
}
}
function createUserCard(user) {
const userID = user.name || user.login
const userBio = user.bio ? `<p>${user.bio}</p>` : ''
const cardHTML = `
<div class="card">
<div>
<img src="${user.avatar_url}" alt="${user.name}" class="avatar">
</div>
<div class="user-info">
<h2>${userID}</h2>
${userBio}
<ul>
<li>${user.followers} <strong>关注者</strong></li>
<li>${user.following} <strong>关注中</strong></li>
<li>${user.public_repos} <strong>仓库</strong></li>
</ul>
<div id="repos"></div>
</div>
</div>
`
main.innerHTML = cardHTML
}
核心代码详解
1. 动画倒计时实现
const nums = document.querySelectorAll('.nums span')
const counter = document.querySelector('.counter')
const finalMessage = document.querySelector('.final')
function runAnimation() {
nums.forEach((num, idx) => {
const nextToLast = nums.length - 1
num.addEventListener('animationend', (e) => {
// 处理进入动画结束
if(e.animationName === 'goIn' && idx !== nextToLast) {
num.classList.remove('in')
num.classList.add('out')
}
// 处理离开动画结束
else if(e.animationName === 'goOut' && num.nextElementSibling) {
num.nextElementSibling.classList.add('in')
}
// 所有动画完成
else {
counter.classList.add('hide')
finalMessage.classList.add('show')
}
})
})
}
2. 自定义范围滑块
const range = document.getElementById('range')
range.addEventListener('input', (e) => {
const value = +e.target.value
const label = e.target.nextElementSibling
// 获取元素宽度进行计算
const range_width = getComputedStyle(e.target).getPropertyValue('width')
const label_width = getComputedStyle(label).getPropertyValue('width')
const num_width = +range_width.substring(0, range_width.length - 2)
const num_label_width = +label_width.substring(0, label_width.length - 2)
const max = +e.target.max
const min = +e.target.min
// 计算标签位置
const left = value * (num_width / max) - num_label_width / 2 + scale(value, min, max, 10, -10)
label.style.left = `${left}px`
label.innerHTML = value
})
// 数值映射函数
const scale = (num, in_min, in_max, out_min, out_max) => {
return (num - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
}
3. 本地存储笔记应用
const notes = JSON.parse(localStorage.getItem('notes'))
// 初始化已有笔记
if(notes) {
notes.forEach(note => addNewNote(note))
}
function addNewNote(text = '') {
const note = document.createElement('div')
note.classList.add('note')
note.innerHTML = `
<div class="tools">
<button class="edit"><i class="fas fa-edit"></i></button>
<button class="delete"><i class="fas fa-trash-alt"></i></button>
</div>
<div class="main ${text ? "" : "hidden"}"></div>
<textarea class="${text ? "hidden" : ""}"></textarea>
`
// 编辑功能
const editBtn = note.querySelector('.edit')
editBtn.addEventListener('click', () => {
main.classList.toggle('hidden')
textArea.classList.toggle('hidden')
})
// 自动保存到本地存储
textArea.addEventListener('input', (e) => {
const { value } = e.target
main.innerHTML = marked(value)
updateLS()
})
}
function updateLS() {
const notesText = document.querySelectorAll('textarea')
const notes = []
notesText.forEach(note => notes.push(note.value))
localStorage.setItem('notes', JSON.stringify(notes))
}
4. 电影搜索应用
const API_URL = 'https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=YOUR_API_KEY&page=1'
const IMG_PATH = 'https://image.tmdb.org/t/p/w1280'
async function getMovies(url) {
const res = await fetch(url)
const data = await res.json()
showMovies(data.results)
}
function showMovies(movies) {
main.innerHTML = ''
movies.forEach((movie) => {
const { title, poster_path, vote_average, overview } = movie
const movieEl = document.createElement('div')
movieEl.classList.add('movie')
movieEl.innerHTML = `
<img src="${IMG_PATH + poster_path}" alt="${title}">
<div class="movie-info">
<h3>${title}</h3>
<span class="${getClassByRate(vote_average)}">${vote_average}</span>
</div>
<div class="overview">
<h3>剧情简介</h3>
${overview}
</div>
`
main.appendChild(movieEl)
})
}
// 根据评分设置颜色
function getClassByRate(vote) {
if(vote >= 8) return 'green'
else if(vote >= 5) return 'orange'
else return 'red'
}
这些项目展示了现代前端开发的核心技术,包括DOM操作、事件处理、API集成、动画实现和本地存储等,为前端开发者提供了宝贵的学习资源和实践参考。