春晚魔术程序复刻
- [🎩 时间魔术计算器 (Time Magic Calculator)](#🎩 时间魔术计算器 (Time Magic Calculator))
-
- 项目简介
- [✨ 核心特性](#✨ 核心特性)
- [🚀 演出指南(如何使用)](#🚀 演出指南(如何使用))
- [🧠 原理揭秘](#🧠 原理揭秘)
- [🛠️ 技术栈](#🛠️ 技术栈)
- [Show Me The Code:](#Show Me The Code:)
🎩 时间魔术计算器 (Time Magic Calculator)
项目简介
这是一个基于 HTML, CSS 和原生 JavaScript 开发的网页版"魔术"计算器。从表面上看,它拥有与普通计算器类似的 UI 界面,但它的核心是一个互动魔术:无论用户前两次输入什么数字,程序都会通过"幽灵打字"的方式自动补齐第三个数字,使得这三个数字的总和恰好等于当前的系统时间(月、日、时、分)。
✨ 核心特性
- 步骤级状态控制 :内置严格的状态机(
step 1-4),引导观众按照魔术剧本进行操作,防止乱按导致穿帮。 - 输入长度限制:根据魔术需求,严格限制第一个数字为最高5位,第二个数字为最高6位。
- 实时时间抓取 :利用 JavaScript 的
Date对象,实时抓取设备的当前时间,并精确格式化为特定的数字串(如 2月17日15:00 格式化为2171500)。 - 幽灵打字动画 :第三个数字并非瞬间出现,而是通过
setInterval模拟人手或系统计算的延迟,每150ms敲击一个数字,极大地增强了魔术的悬疑感和观赏性。
🚀 演出指南(如何使用)
你可以拿这个计算器给你的朋友变个魔术,请按照以下步骤引导他们:
- 第一轮输入 :让朋友随意输入一个不超过 5位数 的数字(例如:
10000)。 - 第一次相加 :让朋友按下
+号。 - 第二轮输入 :让朋友再次随意输入一个不超过 6位数 的数字(例如:
100000)。 - 触发魔法 :让朋友再次按下
+号。- 此时计算器接管控制权,屏幕上会像有幽灵在敲击键盘一样,逐个跳出第三个神秘数字。
- 揭晓奇迹 :让朋友按下
=号。计算器会得出这三个数字的总和。 - 点睛之笔:让朋友看看自己的手表或手机当前时间(例如:2月17号15:00),他们会惊奇地发现,总和数字完美契合当前时间!
🧠 原理揭秘
这个魔术的核心是一道简单的代数题。程序并不关心用户前两次输入了什么,它只做一件事:求差值。
- 记录用户的输入:设为
Num1和Num2。 - 抓取目标结果:获取当前系统时间,并转换为数字
Time。 - 倒推第三个数字:程序在后台迅速计算出
Num3 = Time - (Num1 + Num2)。 - 演出展示:假装第三个数字是随机生成的,并在最后相加时得出必然结果:
Num1 + Num2 + Num3 = Time。
🛠️ 技术栈
- 结构: HTML5
- 样式: CSS3 (Flexbox/Grid 布局, 拟物化阴影与交互动画)
- 逻辑: Vanilla JavaScript (原生 JS)
Show Me The Code:
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>
/* 基础页面重置与居中布局 */
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f0f2f5; /* 浅灰色背景,突出计算器 */
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
}
/* 计算器外壳样式 */
.calculator {
background-color: #333;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); /* 增加立体阴影 */
padding: 20px;
width: 320px;
}
/* 顶部显示屏样式 */
.display {
background-color: #e8f0f2;
color: #333;
font-size: 2.5rem;
text-align: right; /* 数字靠右对齐 */
padding: 20px;
border-radius: 10px;
margin-bottom: 20px;
overflow-x: auto; /* 数字过长时允许横向滚动 */
white-space: nowrap;
box-shadow: inset 0 3px 5px rgba(0,0,0,0.1); /* 内阴影增加真实感 */
}
/* 按键网格布局:4列等宽 */
.buttons {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 15px;
}
/* 通用按键样式 */
button {
border: none;
padding: 20px;
font-size: 1.5rem;
border-radius: 10px;
cursor: pointer;
background-color: #444;
color: white;
transition: background-color 0.1s, transform 0.1s; /* 点击动画过渡 */
}
/* 按键点击时的缩放效果 */
button:active {
transform: scale(0.95);
}
button:hover {
background-color: #555;
}
/* 运算符按键样式(橙色) */
.operator {
background-color: #ff9500;
}
.operator:hover {
background-color: #ffaa33;
}
/* 功能按键样式(灰色,如C键、说明键) */
.action {
background-color: #a5a5a5;
color: black;
}
.action:hover {
background-color: #d4d4d4;
}
</style>
</head>
<body>
<div class="calculator">
<div class="display" id="display">0</div>
<div class="buttons">
<button class="action" onclick="clearCalc()">C</button>
<button class="action" onclick="alert('操作指南:\n1. 输入5位数字按+\n2. 输入6位数字按+\n3. 观看自动模拟输入\n4. 按=揭晓魔法时间')">?</button>
<button class="action" onclick="appendNum('0')">0</button>
<button class="operator" onclick="handlePlus()">+</button>
<button onclick="appendNum('7')">7</button>
<button onclick="appendNum('8')">8</button>
<button onclick="appendNum('9')">9</button>
<button class="operator" onclick="handleEqual()">=</button>
<button onclick="appendNum('4')">4</button>
<button onclick="appendNum('5')">5</button>
<button onclick="appendNum('6')">6</button>
<button class="operator" style="grid-row: span 2" onclick="handleEqual()">=</button>
<button onclick="appendNum('1')">1</button>
<button onclick="appendNum('2')">2</button>
<button onclick="appendNum('3')">3</button>
</div>
</div>
<script>
// ===== 核心状态变量 =====
// step 用于追踪当前进行的步骤:
// 1: 等待输入第一个数字
// 2: 等待输入第二个数字
// 3: 正在/完成模拟输入第三个数字,等待按等于号
// 4: 已经显示最终结果
let step = 1;
let num1 = 0; // 存储第一个输入的数字
let num2 = 0; // 存储第二个输入的数字
let num3 = 0; // 存储由系统计算得出的第三个差值数字
let currentInput = ""; // 当前显示屏上正在拼接的字符串
const display = document.getElementById('display');
// ===== 核心功能函数 =====
/**
* 更新显示屏内容
* @param {string|number} val - 需要显示的值
*/
function updateDisplay(val) {
display.innerText = val;
}
/**
* 处理数字按键的输入
* @param {string} numStr - 用户点击的数字字符
*/
function appendNum(numStr) {
// 防止用户在魔术表演阶段(自动打字或已出结果)干扰程序
if (step === 3 || step === 4) return;
// 需求1:限制第一轮最高输入 5 位数,第二轮最高输入 6 位数
if (step === 1 && currentInput.length >= 5) return;
if (step === 2 && currentInput.length >= 6) return;
// 如果当前是初始状态0,则直接替换;否则在后面追加数字
if (currentInput === "0" || currentInput === "") {
currentInput = numStr;
} else {
currentInput += numStr;
}
updateDisplay(currentInput);
}
/**
* 处理加号 "+" 的点击事件
*/
function handlePlus() {
if (step === 1) {
// 第一轮输入完毕,按下加号
if (currentInput === "") return;
num1 = parseInt(currentInput); // 保存第一个数字
step = 2; // 状态切换到第二轮
currentInput = ""; // 清空当前输入缓存
updateDisplay("0"); // 屏幕重置为0
} else if (step === 2) {
// 第二轮输入完毕,按下加号,触发魔术核心逻辑
if (currentInput === "") return;
num2 = parseInt(currentInput); // 保存第二个数字
// 需求2:获取当前时间并格式化
const now = new Date();
const month = (now.getMonth() + 1).toString(); // 月份:无需补零(1-12)
const date = now.getDate().toString().padStart(2, '0'); // 日期:补齐2位
const hours = now.getHours().toString().padStart(2, '0'); // 小时:补齐2位
const minutes = now.getMinutes().toString().padStart(2, '0'); // 分钟:补齐2位
// 拼接时间字符串并转换为数字 (例如: 2月17日15:00 -> "2171500" -> 2171500)
const timeString = `${month}${date}${hours}${minutes}`;
const timeValue = parseInt(timeString);
// 魔术公式:第三个数字 = 目标时间总和 - (第一个数字 + 第二个数字)
num3 = timeValue - (num1 + num2);
step = 3; // 状态切换到第三轮(系统模拟输入)
simulateTyping(num3.toString());
}
}
/**
* 模拟键盘自动打字效果,逐个显示第三个数字
* @param {string} targetString - 需要自动打印的数字字符串
*/
function simulateTyping(targetString) {
let index = 0;
currentInput = "";
// 设置定时器,每 150 毫秒执行一次,营造一种"系统正在高速计算"的视觉效果
const typingInterval = setInterval(() => {
currentInput += targetString[index]; // 每次追加一个字符
updateDisplay(currentInput);
index++;
// 当所有字符都打印完毕时,清除定时器
if (index >= targetString.length) {
clearInterval(typingInterval);
// 此时模拟输入完成,等待用户按 "=" 键
}
}, 150);
}
/**
* 处理等号 "=" 的点击事件
*/
function handleEqual() {
// 只有在第三个数字模拟输入完毕后,按等号才有效
if (step === 3) {
// 需求3:展示最终加和结果。因为 num3 是根据差值倒推的,所以结果一定等于当前时间数值
const finalResult = num1 + num2 + num3;
updateDisplay(finalResult);
step = 4; // 流程结束,锁定按键
}
}
/**
* 清空重置计算器,恢复到初始状态
*/
function clearCalc() {
step = 1;
num1 = 0;
num2 = 0;
num3 = 0;
currentInput = "";
updateDisplay("0");
}
</script>
</body>
</html>