Skip to content

基础小项目(巩固知识点)

20.1 猜数字游戏

项目介绍

猜数字游戏是一个经典的小游戏,玩家需要在有限的次数内猜出计算机随机生成的数字。

实现思路

  1. 生成一个 1-100 之间的随机数字
  2. 让玩家输入猜测的数字
  3. 比较玩家输入的数字和随机数字
  4. 根据比较结果给出提示(太大、太小或正确)
  5. 限制猜测次数,超过次数则游戏结束

完整代码

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>猜数字游戏</title>
  <style>
    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }
    body {
      font-family: Arial, sans-serif;
      background-color: #f4f4f4;
      padding: 20px;
    }
    .container {
      max-width: 600px;
      margin: 0 auto;
      background-color: white;
      padding: 30px;
      border-radius: 8px;
      box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
    }
    h1 {
      text-align: center;
      margin-bottom: 20px;
      color: #333;
    }
    .instructions {
      text-align: center;
      margin-bottom: 30px;
      font-size: 18px;
    }
    .game-container {
      display: flex;
      flex-direction: column;
      gap: 20px;
    }
    .input-group {
      display: flex;
      gap: 10px;
    }
    input {
      flex: 1;
      padding: 10px;
      font-size: 16px;
      border: 1px solid #ddd;
      border-radius: 4px;
    }
    button {
      padding: 10px 20px;
      background-color: #4CAF50;
      color: white;
      border: none;
      border-radius: 4px;
      font-size: 16px;
      cursor: pointer;
    }
    button:hover {
      background-color: #45a049;
    }
    .message {
      padding: 15px;
      border-radius: 4px;
      text-align: center;
      font-size: 18px;
      font-weight: bold;
    }
    .message.success {
      background-color: #d4edda;
      color: #155724;
      border: 1px solid #c3e6cb;
    }
    .message.error {
      background-color: #f8d7da;
      color: #721c24;
      border: 1px solid #f5c6cb;
    }
    .message.info {
      background-color: #d1ecf1;
      color: #0c5460;
      border: 1px solid #bee5eb;
    }
    .history {
      margin-top: 20px;
    }
    .history h3 {
      margin-bottom: 10px;
    }
    .history-list {
      list-style: none;
      padding: 10px;
      border: 1px solid #ddd;
      border-radius: 4px;
      max-height: 200px;
      overflow-y: auto;
    }
    .history-list li {
      padding: 5px 0;
      border-bottom: 1px solid #eee;
    }
    .history-list li:last-child {
      border-bottom: none;
    }
  </style>
</head>
<body>
  <div class="container">
    <h1>猜数字游戏</h1>
    <div class="instructions">
      我已经想好了一个 1-100 之间的数字,你有 10 次机会来猜它!
    </div>
    
    <div class="game-container">
      <div class="input-group">
        <input type="number" id="guessInput" placeholder="请输入你猜测的数字" min="1" max="100">
        <button id="guessBtn">猜</button>
      </div>
      
      <div class="message" id="message"></div>
      
      <div class="history">
        <h3>猜测历史</h3>
        <ul class="history-list" id="historyList"></ul>
      </div>
      
      <button id="resetBtn">重新开始</button>
    </div>
  </div>

  <script>
    // 生成随机数字
    let randomNumber = Math.floor(Math.random() * 100) + 1;
    let attempts = 0;
    const maxAttempts = 10;
    const historyList = [];

    // 获取 DOM 元素
    const guessInput = document.getElementById("guessInput");
    const guessBtn = document.getElementById("guessBtn");
    const message = document.getElementById("message");
    const historyListEl = document.getElementById("historyList");
    const resetBtn = document.getElementById("resetBtn");

    // 猜数字函数
    function guessNumber() {
      const guess = parseInt(guessInput.value);
      
      // 验证输入
      if (isNaN(guess) || guess < 1 || guess > 100) {
        showMessage("请输入 1-100 之间的数字", "info");
        return;
      }
      
      attempts++;
      historyList.push(guess);
      updateHistory();
      
      // 比较猜测的数字和随机数字
      if (guess === randomNumber) {
        showMessage(`恭喜你!猜对了!你用了 ${attempts} 次机会。`, "success");
        guessBtn.disabled = true;
        guessInput.disabled = true;
      } else if (attempts >= maxAttempts) {
        showMessage(`游戏结束!正确数字是 ${randomNumber}。`, "error");
        guessBtn.disabled = true;
        guessInput.disabled = true;
      } else if (guess > randomNumber) {
        showMessage(`太大了!你还有 ${maxAttempts - attempts} 次机会。`, "info");
      } else {
        showMessage(`太小了!你还有 ${maxAttempts - attempts} 次机会。`, "info");
      }
      
      // 清空输入框
      guessInput.value = "";
      guessInput.focus();
    }

    // 显示消息
    function showMessage(text, type) {
      message.textContent = text;
      message.className = `message ${type}`;
    }

    // 更新历史记录
    function updateHistory() {
      historyListEl.innerHTML = "";
      historyList.forEach((item, index) => {
        const li = document.createElement("li");
        li.textContent = `第 ${index + 1} 次:${item}`;
        historyListEl.appendChild(li);
      });
    }

    // 重置游戏
    function resetGame() {
      randomNumber = Math.floor(Math.random() * 100) + 1;
      attempts = 0;
      historyList.length = 0;
      updateHistory();
      showMessage("游戏已重置,开始新的一轮吧!", "info");
      guessBtn.disabled = false;
      guessInput.disabled = false;
      guessInput.focus();
    }

    // 事件监听
    guessBtn.addEventListener("click", guessNumber);
    resetBtn.addEventListener("click", resetGame);
    
    // 按回车键也可以猜数字
    guessInput.addEventListener("keypress", function(event) {
      if (event.key === "Enter") {
        guessNumber();
      }
    });

    // 初始状态
    showMessage("准备好了吗?开始猜数字吧!", "info");
    guessInput.focus();
  </script>
</body>
</html>

20.2 简易计算器

项目介绍

简易计算器可以进行基本的加减乘除运算,帮助用户快速计算数学表达式。

实现思路

  1. 设计计算器的 UI 界面
  2. 实现数字和运算符的输入
  3. 处理计算逻辑
  4. 实现清除和删除功能

完整代码

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>简易计算器</title>
  <style>
    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }
    body {
      font-family: Arial, sans-serif;
      background-color: #f4f4f4;
      padding: 20px;
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 100vh;
    }
    .calculator {
      background-color: #333;
      border-radius: 10px;
      padding: 20px;
      box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
      width: 300px;
    }
    .display {
      background-color: #222;
      color: white;
      font-size: 24px;
      padding: 15px;
      border-radius: 5px;
      margin-bottom: 20px;
      text-align: right;
      min-height: 60px;
      word-wrap: break-word;
    }
    .buttons {
      display: grid;
      grid-template-columns: repeat(4, 1fr);
      gap: 10px;
    }
    button {
      padding: 20px;
      font-size: 18px;
      border: none;
      border-radius: 5px;
      cursor: pointer;
      transition: background-color 0.2s;
    }
    button:hover {
      opacity: 0.8;
    }
    .number {
      background-color: #555;
      color: white;
    }
    .operator {
      background-color: #ff9500;
      color: white;
    }
    .clear {
      background-color: #ff3b30;
      color: white;
    }
    .equals {
      background-color: #34c759;
      color: white;
      grid-column: span 2;
    }
    .decimal {
      background-color: #555;
      color: white;
    }
    .backspace {
      background-color: #555;
      color: white;
    }
  </style>
</head>
<body>
  <div class="calculator">
    <div class="display" id="display">0</div>
    <div class="buttons">
      <button class="clear" id="clear">C</button>
      <button class="backspace" id="backspace">←</button>
      <button class="operator" data-value="/">÷</button>
      <button class="operator" data-value="*">×</button>
      <button class="number" data-value="7">7</button>
      <button class="number" data-value="8">8</button>
      <button class="number" data-value="9">9</button>
      <button class="operator" data-value="-">-</button>
      <button class="number" data-value="4">4</button>
      <button class="number" data-value="5">5</button>
      <button class="number" data-value="6">6</button>
      <button class="operator" data-value="+">+</button>
      <button class="number" data-value="1">1</button>
      <button class="number" data-value="2">2</button>
      <button class="number" data-value="3">3</button>
      <button class="equals" id="equals">=</button>
      <button class="number" data-value="0">0</button>
      <button class="decimal" data-value=".">.</button>
    </div>
  </div>

  <script>
    // 获取 DOM 元素
    const display = document.getElementById("display");
    const clearBtn = document.getElementById("clear");
    const backspaceBtn = document.getElementById("backspace");
    const equalsBtn = document.getElementById("equals");
    const numberBtns = document.querySelectorAll(".number");
    const operatorBtns = document.querySelectorAll(".operator");
    const decimalBtn = document.querySelector(".decimal");

    // 计算器状态
    let currentValue = "0";
    let previousValue = "";
    let operator = "";
    let resetDisplay = false;

    // 更新显示
    function updateDisplay() {
      display.textContent = currentValue;
    }

    // 处理数字输入
    function handleNumberInput(value) {
      if (resetDisplay) {
        currentValue = value;
        resetDisplay = false;
      } else {
        currentValue = currentValue === "0" ? value : currentValue + value;
      }
      updateDisplay();
    }

    // 处理运算符输入
    function handleOperatorInput(value) {
      if (operator && !resetDisplay) {
        calculate();
      }
      previousValue = currentValue;
      operator = value;
      resetDisplay = true;
    }

    // 处理小数点输入
    function handleDecimalInput() {
      if (!currentValue.includes(".")) {
        currentValue += ".";
        updateDisplay();
      }
    }

    // 处理清除操作
    function handleClear() {
      currentValue = "0";
      previousValue = "";
      operator = "";
      resetDisplay = false;
      updateDisplay();
    }

    // 处理退格操作
    function handleBackspace() {
      if (currentValue.length === 1) {
        currentValue = "0";
      } else {
        currentValue = currentValue.slice(0, -1);
      }
      updateDisplay();
    }

    // 计算结果
    function calculate() {
      let result;
      const prev = parseFloat(previousValue);
      const current = parseFloat(currentValue);

      if (isNaN(prev) || isNaN(current)) return;

      switch (operator) {
        case "+":
          result = prev + current;
          break;
        case "-":
          result = prev - current;
          break;
        case "*":
          result = prev * current;
          break;
        case "/":
          result = prev / current;
          break;
        default:
          return;
      }

      currentValue = result.toString();
      operator = "";
      previousValue = "";
      resetDisplay = true;
      updateDisplay();
    }

    // 事件监听
    numberBtns.forEach(btn => {
      btn.addEventListener("click", function() {
        handleNumberInput(this.dataset.value);
      });
    });

    operatorBtns.forEach(btn => {
      btn.addEventListener("click", function() {
        handleOperatorInput(this.dataset.value);
      });
    });

    decimalBtn.addEventListener("click", handleDecimalInput);
    clearBtn.addEventListener("click", handleClear);
    backspaceBtn.addEventListener("click", handleBackspace);
    equalsBtn.addEventListener("click", calculate);

    // 键盘支持
    document.addEventListener("keydown", function(event) {
      const key = event.key;
      
      if (key >= "0" && key <= "9") {
        handleNumberInput(key);
      } else if (key === ".") {
        handleDecimalInput();
      } else if (key === "+" || key === "-" || key === "*" || key === "/") {
        handleOperatorInput(key);
      } else if (key === "Enter" || key === "=") {
        calculate();
      } else if (key === "Backspace") {
        handleBackspace();
      } else if (key === "Escape") {
        handleClear();
      }
    });
  </script>
</body>
</html>

20.3 待办事项清单(TODO List)

项目介绍

待办事项清单可以帮助用户管理日常任务,添加、完成和删除任务。

实现思路

  1. 设计待办事项的 UI 界面
  2. 实现添加新任务的功能
  3. 实现标记任务为已完成的功能
  4. 实现删除任务的功能
  5. 使用本地存储保存任务数据

完整代码

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>待办事项清单</title>
  <style>
    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }
    body {
      font-family: Arial, sans-serif;
      background-color: #f4f4f4;
      padding: 20px;
    }
    .container {
      max-width: 600px;
      margin: 0 auto;
      background-color: white;
      padding: 30px;
      border-radius: 8px;
      box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
    }
    h1 {
      text-align: center;
      margin-bottom: 30px;
      color: #333;
    }
    .add-task {
      display: flex;
      gap: 10px;
      margin-bottom: 30px;
    }
    input[type="text"] {
      flex: 1;
      padding: 12px;
      font-size: 16px;
      border: 1px solid #ddd;
      border-radius: 4px;
    }
    button {
      padding: 12px 20px;
      background-color: #4CAF50;
      color: white;
      border: none;
      border-radius: 4px;
      font-size: 16px;
      cursor: pointer;
    }
    button:hover {
      background-color: #45a049;
    }
    .task-list {
      list-style: none;
    }
    .task-item {
      display: flex;
      align-items: center;
      padding: 12px;
      border-bottom: 1px solid #eee;
    }
    .task-item:last-child {
      border-bottom: none;
    }
    .task-item.completed {
      background-color: #f9f9f9;
    }
    .task-item.completed .task-text {
      text-decoration: line-through;
      color: #999;
    }
    .task-checkbox {
      margin-right: 15px;
      transform: scale(1.2);
    }
    .task-text {
      flex: 1;
      font-size: 16px;
    }
    .delete-btn {
      background-color: #f44336;
      padding: 6px 12px;
      font-size: 14px;
    }
    .delete-btn:hover {
      background-color: #da190b;
    }
    .empty-state {
      text-align: center;
      padding: 40px 20px;
      color: #999;
      font-size: 18px;
    }
  </style>
</head>
<body>
  <div class="container">
    <h1>待办事项清单</h1>
    
    <div class="add-task">
      <input type="text" id="taskInput" placeholder="请输入新的任务...">
      <button id="addTaskBtn">添加</button>
    </div>
    
    <ul class="task-list" id="taskList">
      <!-- 任务项将通过 JavaScript 动态添加 -->
    </ul>
  </div>

  <script>
    // 获取 DOM 元素
    const taskInput = document.getElementById("taskInput");
    const addTaskBtn = document.getElementById("addTaskBtn");
    const taskList = document.getElementById("taskList");

    // 从本地存储加载任务
    function loadTasks() {
      const tasks = JSON.parse(localStorage.getItem("tasks")) || [];
      tasks.forEach(task => {
        addTaskToDOM(task.text, task.completed);
      });
      updateEmptyState();
    }

    // 保存任务到本地存储
    function saveTasks() {
      const tasks = [];
      document.querySelectorAll(".task-item").forEach(item => {
        tasks.push({
          text: item.querySelector(".task-text").textContent,
          completed: item.classList.contains("completed")
        });
      });
      localStorage.setItem("tasks", JSON.stringify(tasks));
    }

    // 添加任务到 DOM
    function addTaskToDOM(text, completed = false) {
      const li = document.createElement("li");
      li.className = "task-item";
      if (completed) {
        li.classList.add("completed");
      }
      
      li.innerHTML = `
        <input type="checkbox" class="task-checkbox" ${completed ? "checked" : ""}>
        <span class="task-text">${text}</span>
        <button class="delete-btn">删除</button>
      `;
      
      taskList.appendChild(li);
      
      // 添加事件监听
      const checkbox = li.querySelector(".task-checkbox");
      const deleteBtn = li.querySelector(".delete-btn");
      
      checkbox.addEventListener("change", function() {
        li.classList.toggle("completed");
        saveTasks();
      });
      
      deleteBtn.addEventListener("click", function() {
        li.remove();
        saveTasks();
        updateEmptyState();
      });
    }

    // 更新空状态
    function updateEmptyState() {
      if (taskList.children.length === 0) {
        const emptyState = document.createElement("li");
        emptyState.className = "empty-state";
        emptyState.textContent = "还没有待办事项,添加一个吧!";
        taskList.appendChild(emptyState);
      } else {
        const emptyState = taskList.querySelector(".empty-state");
        if (emptyState) {
          emptyState.remove();
        }
      }
    }

    // 添加新任务
    function addTask() {
      const text = taskInput.value.trim();
      if (text) {
        addTaskToDOM(text);
        saveTasks();
        taskInput.value = "";
        updateEmptyState();
      }
    }

    // 事件监听
    addTaskBtn.addEventListener("click", addTask);
    
    // 按回车键添加任务
    taskInput.addEventListener("keypress", function(event) {
      if (event.key === "Enter") {
        addTask();
      }
    });

    // 初始加载
    loadTasks();
  </script>
</body>
</html>

20.4 图片轮播器

项目介绍

图片轮播器可以自动播放图片,也可以手动切换,是网站中常见的组件。

实现思路

  1. 设计轮播器的 UI 界面
  2. 实现图片的自动切换
  3. 实现手动切换功能
  4. 实现指示器功能

完整代码

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>图片轮播器</title>
  <style>
    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }
    body {
      font-family: Arial, sans-serif;
      background-color: #f4f4f4;
      padding: 20px;
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 100vh;
    }
    .slider {
      position: relative;
      width: 800px;
      height: 400px;
      overflow: hidden;
      border-radius: 10px;
      box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
    }
    .slides {
      display: flex;
      transition: transform 0.5s ease;
    }
    .slide {
      width: 800px;
      height: 400px;
      flex-shrink: 0;
      position: relative;
    }
    .slide img {
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
    .slide-content {
      position: absolute;
      bottom: 0;
      left: 0;
      right: 0;
      background: rgba(0, 0, 0, 0.6);
      color: white;
      padding: 20px;
    }
    .slide-content h3 {
      margin-bottom: 10px;
      font-size: 24px;
    }
    .slide-content p {
      font-size: 16px;
    }
    .controls {
      position: absolute;
      top: 50%;
      left: 0;
      right: 0;
      transform: translateY(-50%);
      display: flex;
      justify-content: space-between;
      padding: 0 20px;
    }
    .control-btn {
      background-color: rgba(255, 255, 255, 0.5);
      color: #333;
      border: none;
      border-radius: 50%;
      width: 50px;
      height: 50px;
      font-size: 24px;
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      transition: background-color 0.2s;
    }
    .control-btn:hover {
      background-color: rgba(255, 255, 255, 0.8);
    }
    .indicators {
      position: absolute;
      bottom: 20px;
      left: 0;
      right: 0;
      display: flex;
      justify-content: center;
      gap: 10px;
    }
    .indicator {
      width: 12px;
      height: 12px;
      border-radius: 50%;
      background-color: rgba(255, 255, 255, 0.5);
      cursor: pointer;
      transition: background-color 0.2s;
    }
    .indicator.active {
      background-color: white;
    }
    .autoplay-control {
      position: absolute;
      top: 20px;
      right: 20px;
    }
    .autoplay-btn {
      background-color: rgba(0, 0, 0, 0.5);
      color: white;
      border: none;
      border-radius: 4px;
      padding: 8px 16px;
      font-size: 14px;
      cursor: pointer;
      transition: background-color 0.2s;
    }
    .autoplay-btn:hover {
      background-color: rgba(0, 0, 0, 0.7);
    }
  </style>
</head>
<body>
  <div class="slider">
    <div class="slides" id="slides">
      <div class="slide">
        <img src="https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=beautiful%20nature%20landscape%20with%20mountains%20and%20lake&image_size=landscape_16_9" alt="风景 1">
        <div class="slide-content">
          <h3>美丽的自然风光</h3>
          <p>壮观的山脉和湖泊景色</p>
        </div>
      </div>
      <div class="slide">
        <img src="https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=modern%20city%20skyline%20at%20night&image_size=landscape_16_9" alt="城市 1">
        <div class="slide-content">
          <h3>现代城市夜景</h3>
          <p>繁华的都市天际线</p>
        </div>
      </div>
      <div class="slide">
        <img src="https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=tropical%20beach%20with%20palm%20trees&image_size=landscape_16_9" alt="海滩 1">
        <div class="slide-content">
          <h3>热带海滩</h3>
          <p>阳光、沙滩和棕榈树</p>
        </div>
      </div>
      <div class="slide">
        <img src="https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=autumn%20forest%20with%20colorful%20leaves&image_size=landscape_16_9" alt="森林 1">
        <div class="slide-content">
          <h3>秋日森林</h3>
          <p>色彩斑斓的秋叶</p>
        </div>
      </div>
    </div>
    
    <div class="controls">
      <button class="control-btn" id="prevBtn">←</button>
      <button class="control-btn" id="nextBtn">→</button>
    </div>
    
    <div class="indicators" id="indicators">
      <!-- 指示器将通过 JavaScript 动态添加 -->
    </div>
    
    <div class="autoplay-control">
      <button class="autoplay-btn" id="autoplayBtn">暂停</button>
    </div>
  </div>

  <script>
    // 获取 DOM 元素
    const slides = document.getElementById("slides");
    const prevBtn = document.getElementById("prevBtn");
    const nextBtn = document.getElementById("nextBtn");
    const indicators = document.getElementById("indicators");
    const autoplayBtn = document.getElementById("autoplayBtn");

    // 轮播器状态
    const slideCount = slides.children.length;
    let currentIndex = 0;
    let autoplayInterval;
    let isAutoplay = true;

    // 创建指示器
    function createIndicators() {
      indicators.innerHTML = "";
      for (let i = 0; i < slideCount; i++) {
        const indicator = document.createElement("div");
        indicator.className = "indicator";
        if (i === currentIndex) {
          indicator.classList.add("active");
        }
        indicator.addEventListener("click", function() {
          goToSlide(i);
        });
        indicators.appendChild(indicator);
      }
    }

    // 更新指示器
    function updateIndicators() {
      const indicatorItems = indicators.children;
      for (let i = 0; i < indicatorItems.length; i++) {
        if (i === currentIndex) {
          indicatorItems[i].classList.add("active");
        } else {
          indicatorItems[i].classList.remove("active");
        }
      }
    }

    // 切换到指定幻灯片
    function goToSlide(index) {
      currentIndex = index;
      slides.style.transform = `translateX(-${currentIndex * 100}%)`;
      updateIndicators();
    }

    // 下一张幻灯片
    function nextSlide() {
      currentIndex = (currentIndex + 1) % slideCount;
      goToSlide(currentIndex);
    }

    // 上一张幻灯片
    function prevSlide() {
      currentIndex = (currentIndex - 1 + slideCount) % slideCount;
      goToSlide(currentIndex);
    }

    // 开始自动播放
    function startAutoplay() {
      autoplayInterval = setInterval(nextSlide, 3000);
      autoplayBtn.textContent = "暂停";
      isAutoplay = true;
    }

    // 停止自动播放
    function stopAutoplay() {
      clearInterval(autoplayInterval);
      autoplayBtn.textContent = "播放";
      isAutoplay = false;
    }

    // 切换自动播放状态
    function toggleAutoplay() {
      if (isAutoplay) {
        stopAutoplay();
      } else {
        startAutoplay();
      }
    }

    // 事件监听
    prevBtn.addEventListener("click", prevSlide);
    nextBtn.addEventListener("click", nextSlide);
    autoplayBtn.addEventListener("click", toggleAutoplay);

    // 初始设置
    createIndicators();
    startAutoplay();
  </script>
</body>
</html>

20.5 随机颜色生成器

项目介绍

随机颜色生成器可以生成随机的颜色,并显示颜色的十六进制代码,方便用户在设计中使用。

实现思路

  1. 设计颜色生成器的 UI 界面
  2. 实现随机颜色生成功能
  3. 显示颜色的十六进制代码
  4. 实现复制颜色代码的功能
  5. 实现保存喜欢的颜色的功能

完整代码

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>随机颜色生成器</title>
  <style>
    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }
    body {
      font-family: Arial, sans-serif;
      background-color: #f4f4f4;
      padding: 20px;
      display: flex;
      flex-direction: column;
      align-items: center;
      min-height: 100vh;
    }
    .container {
      max-width: 800px;
      width: 100%;
      background-color: white;
      border-radius: 10px;
      box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
      overflow: hidden;
    }
    .color-display {
      height: 300px;
      background-color: #3498db;
      display: flex;
      align-items: center;
      justify-content: center;
      position: relative;
    }
    .color-code {
      background-color: rgba(255, 255, 255, 0.9);
      padding: 15px 25px;
      border-radius: 50px;
      font-size: 24px;
      font-weight: bold;
      color: #333;
      box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
    }
    .controls {
      padding: 30px;
      display: flex;
      gap: 15px;
      flex-wrap: wrap;
      justify-content: center;
    }
    button {
      padding: 12px 24px;
      border: none;
      border-radius: 4px;
      font-size: 16px;
      cursor: pointer;
      transition: background-color 0.2s;
    }
    .generate-btn {
      background-color: #4CAF50;
      color: white;
    }
    .generate-btn:hover {
      background-color: #45a049;
    }
    .copy-btn {
      background-color: #3498db;
      color: white;
    }
    .copy-btn:hover {
      background-color: #2980b9;
    }
    .save-btn {
      background-color: #f39c12;
      color: white;
    }
    .save-btn:hover {
      background-color: #e67e22;
    }
    .saved-colors {
      padding: 0 30px 30px;
    }
    .saved-colors h3 {
      margin-bottom: 15px;
      color: #333;
    }
    .color-grid {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
      gap: 15px;
    }
    .color-item {
      width: 80px;
      height: 80px;
      border-radius: 8px;
      box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
      position: relative;
      cursor: pointer;
      transition: transform 0.2s;
    }
    .color-item:hover {
      transform: scale(1.05);
    }
    .color-item .color-code-small {
      position: absolute;
      bottom: 0;
      left: 0;
      right: 0;
      background-color: rgba(255, 255, 255, 0.9);
      padding: 5px;
      font-size: 12px;
      font-weight: bold;
      text-align: center;
      border-bottom-left-radius: 8px;
      border-bottom-right-radius: 8px;
    }
    .toast {
      position: fixed;
      bottom: 20px;
      right: 20px;
      background-color: #333;
      color: white;
      padding: 15px 20px;
      border-radius: 4px;
      box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
      opacity: 0;
      transition: opacity 0.3s;
    }
    .toast.show {
      opacity: 1;
    }
  </style>
</head>
<body>
  <h1>随机颜色生成器</h1>
  
  <div class="container">
    <div class="color-display" id="colorDisplay">
      <div class="color-code" id="colorCode">#3498db</div>
    </div>
    
    <div class="controls">
      <button class="generate-btn" id="generateBtn">生成随机颜色</button>
      <button class="copy-btn" id="copyBtn">复制颜色代码</button>
      <button class="save-btn" id="saveBtn">保存颜色</button>
    </div>
    
    <div class="saved-colors">
      <h3>保存的颜色</h3>
      <div class="color-grid" id="colorGrid">
        <!-- 保存的颜色将通过 JavaScript 动态添加 -->
      </div>
    </div>
  </div>
  
  <div class="toast" id="toast">颜色代码已复制到剪贴板</div>

  <script>
    // 获取 DOM 元素
    const colorDisplay = document.getElementById("colorDisplay");
    const colorCode = document.getElementById("colorCode");
    const generateBtn = document.getElementById("generateBtn");
    const copyBtn = document.getElementById("copyBtn");
    const saveBtn = document.getElementById("saveBtn");
    const colorGrid = document.getElementById("colorGrid");
    const toast = document.getElementById("toast");

    // 保存的颜色
    let savedColors = JSON.parse(localStorage.getItem("savedColors")) || [];

    // 生成随机颜色
    function generateRandomColor() {
      const letters = "0123456789ABCDEF";
      let color = "#";
      for (let i = 0; i < 6; i++) {
        color += letters[Math.floor(Math.random() * 16)];
      }
      return color;
    }

    // 更新颜色显示
    function updateColorDisplay(color) {
      colorDisplay.style.backgroundColor = color;
      colorCode.textContent = color;
      
      // 根据颜色亮度调整文本颜色
      const brightness = getBrightness(color);
      colorCode.style.color = brightness > 128 ? "#333" : "#fff";
    }

    // 计算颜色亮度
    function getBrightness(color) {
      const r = parseInt(color.substring(1, 3), 16);
      const g = parseInt(color.substring(3, 5), 16);
      const b = parseInt(color.substring(5, 7), 16);
      return (r * 299 + g * 587 + b * 114) / 1000;
    }

    // 复制颜色代码
    function copyColorCode() {
      const color = colorCode.textContent;
      navigator.clipboard.writeText(color)
        .then(() => {
          showToast("颜色代码已复制到剪贴板");
        })
        .catch(err => {
          console.error("复制失败:", err);
          showToast("复制失败,请手动复制");
        });
    }

    // 显示提示消息
    function showToast(message) {
      toast.textContent = message;
      toast.classList.add("show");
      setTimeout(() => {
        toast.classList.remove("show");
      }, 2000);
    }

    // 保存颜色
    function saveColor() {
      const color = colorCode.textContent;
      if (!savedColors.includes(color)) {
        savedColors.push(color);
        localStorage.setItem("savedColors", JSON.stringify(savedColors));
        updateSavedColors();
        showToast("颜色已保存");
      } else {
        showToast("颜色已存在");
      }
    }

    // 更新保存的颜色
    function updateSavedColors() {
      colorGrid.innerHTML = "";
      savedColors.forEach(color => {
        const colorItem = document.createElement("div");
        colorItem.className = "color-item";
        colorItem.style.backgroundColor = color;
        
        const colorCodeSmall = document.createElement("div");
        colorCodeSmall.className = "color-code-small";
        colorCodeSmall.textContent = color;
        colorCodeSmall.style.color = getBrightness(color) > 128 ? "#333" : "#fff";
        
        colorItem.appendChild(colorCodeSmall);
        
        // 点击复制颜色代码
        colorItem.addEventListener("click", function() {
          navigator.clipboard.writeText(color)
            .then(() => {
              showToast("颜色代码已复制到剪贴板");
            });
        });
        
        colorGrid.appendChild(colorItem);
      });
    }

    // 事件监听
    generateBtn.addEventListener("click", function() {
      const color = generateRandomColor();
      updateColorDisplay(color);
    });

    copyBtn.addEventListener("click", copyColorCode);
    saveBtn.addEventListener("click", saveColor);

    // 初始设置
    updateColorDisplay("#3498db");
    updateSavedColors();
  </script>
</body>
</html>

小结

  • 猜数字游戏:练习了随机数生成、用户输入处理、条件判断和循环
  • 简易计算器:练习了事件处理、状态管理和数学计算
  • 待办事项清单:练习了DOM操作、本地存储和事件处理
  • 图片轮播器:练习了定时器、DOM操作和动画效果
  • 随机颜色生成器:练习了颜色生成、本地存储和用户交互

这些基础小项目可以帮助你巩固JavaScript的核心知识点,为更复杂的项目打下基础。

© 2026 编程马·菜鸟教程 版权所有