50天50个前端项目 - HTML/CSS和JavaScript实战合集

79 阅读3分钟

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协议的功能)

使用步骤

  1. 克隆或下载项目到本地
  2. 进入任意项目文件夹
  3. 用浏览器打开HTML文件即可运行
  4. 或使用本地服务器:
    # 使用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集成、动画实现和本地存储等,为前端开发者提供了宝贵的学习资源和实践参考。