Files
2026-01-16 20:53:33 +08:00

512 lines
16 KiB
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>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Arial', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
color: white;
}
.header {
text-align: center;
margin-bottom: 30px;
}
h1 {
font-size: 2.5rem;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
}
.animation-container {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 30px;
max-width: 1200px;
margin: 0 auto;
}
.animation-card {
background: rgba(255, 255, 255, 0.1);
border-radius: 20px;
padding: 20px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
backdrop-filter: blur(10px);
text-align: center;
transition: transform 0.3s ease;
}
.animation-card:hover {
transform: translateY(-5px);
}
.animation-title {
font-size: 1.4rem;
margin-bottom: 15px;
color: #ffeb3b;
}
.canvas-container {
position: relative;
width: 300px;
height: 300px;
margin: 0 auto;
}
canvas {
background: #2c3e50;
border-radius: 15px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}
.controls {
margin-top: 20px;
display: flex;
justify-content: center;
gap: 10px;
}
button {
background: linear-gradient(45deg, #ff6b6b, #ff8e53);
border: none;
padding: 10px 20px;
border-radius: 50px;
color: white;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
button:hover {
transform: translateY(-2px);
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.3);
}
.description {
margin-top: 15px;
font-size: 0.9rem;
color: #e0e0e0;
}
@media (max-width: 768px) {
.animation-container {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="header">
<h1>跑步机测试动画集</h1>
<p>四个不同的测试场景动画演示</p>
</div>
<div class="animation-container">
<div class="animation-card">
<h2 class="animation-title">1. 保持头部静止走路</h2>
<div class="canvas-container">
<canvas id="animation1" width="300" height="300"></canvas>
</div>
<div class="controls">
<button onclick="startAnimation(1)">开始</button>
<button onclick="stopAnimation(1)">停止</button>
</div>
<p class="description">头部保持静止,脚在跑步机上正常行走</p>
</div>
<div class="animation-card">
<h2 class="animation-title">2. 左右摇头测试</h2>
<div class="canvas-container">
<canvas id="animation2" width="300" height="300"></canvas>
</div>
<div class="controls">
<button onclick="startAnimation(2)">开始</button>
<button onclick="stopAnimation(2)">停止</button>
</div>
<p class="description">脚在跑步机上走路,同时做左右摇头动作</p>
</div>
<div class="animation-card">
<h2 class="animation-title">3. 上下点头测试</h2>
<div class="canvas-container">
<canvas id="animation3" width="300" height="300"></canvas>
</div>
<div class="controls">
<button onclick="startAnimation(3)">开始</button>
<button onclick="stopAnimation(3)">停止</button>
</div>
<p class="description">脚在跑步机上走路,同时做上下点头动作</p>
</div>
<div class="animation-card">
<h2 class="animation-title">4. 大声说话测试</h2>
<div class="canvas-container">
<canvas id="animation4" width="300" height="300"></canvas>
</div>
<div class="controls">
<button onclick="startAnimation(4)">开始</button>
<button onclick="stopAnimation(4)">停止</button>
</div>
<p class="description">脚在跑步机上走路,同时做大声说话动作</p>
</div>
</div>
<script>
// 动画控制变量
const animations = {
1: { running: false, frame: null },
2: { running: false, frame: null },
3: { running: false, frame: null },
4: { running: false, frame: null }
};
// 获取Canvas上下文
const canvases = {
1: document.getElementById('animation1').getContext('2d'),
2: document.getElementById('animation2').getContext('2d'),
3: document.getElementById('animation3').getContext('2d'),
4: document.getElementById('animation4').getContext('2d')
};
// 开始动画
function startAnimation(num) {
if (animations[num].running) return;
animations[num].running = true;
const ctx = canvases[num];
const canvas = ctx.canvas;
// 初始化变量
let frameCount = 0;
let treadmillOffset = 0;
let legAngle = 0;
let headAngle = 0;
let headDirection = 1;
let speechBubble = 0;
function draw() {
if (!animations[num].running) return;
// 清除画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 绘制背景
drawBackground(ctx, canvas.width, canvas.height);
// 绘制跑步机
drawTreadmill(ctx, canvas.width, canvas.height, treadmillOffset);
// 根据不同的动画类型绘制不同的人物动作
switch (num) {
case 1:
drawPersonNormal(ctx, canvas.width, canvas.height, frameCount, legAngle);
break;
case 2:
drawPersonHeadShake(ctx, canvas.width, canvas.height, frameCount, legAngle, headAngle);
break;
case 3:
drawPersonHeadNod(ctx, canvas.width, canvas.height, frameCount, legAngle, headAngle);
break;
case 4:
drawPersonTalking(ctx, canvas.width, canvas.height, frameCount, legAngle, speechBubble);
break;
}
// 更新动画变量
frameCount++;
treadmillOffset = (treadmillOffset + 5) % 40;
legAngle = Math.sin(frameCount * 0.2) * 0.5;
if (num === 2 || num === 3) {
headAngle += 0.1 * headDirection;
if (Math.abs(headAngle) > 0.5) headDirection *= -1;
}
if (num === 4) {
speechBubble = (speechBubble + 1) % 60;
}
// 继续动画循环
animations[num].frame = requestAnimationFrame(draw);
}
draw();
}
// 停止动画
function stopAnimation(num) {
if (animations[num].frame) {
cancelAnimationFrame(animations[num].frame);
}
animations[num].running = false;
// 清除画布
const ctx = canvases[num];
const canvas = ctx.canvas;
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBackground(ctx, canvas.width, canvas.height);
}
// 绘制背景
function drawBackground(ctx, width, height) {
// 绘制渐变背景
const gradient = ctx.createLinearGradient(0, 0, 0, height);
gradient.addColorStop(0, '#2c3e50');
gradient.addColorStop(1, '#3498db');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, width, height);
// 绘制地板
ctx.fillStyle = '#7f8c8d';
ctx.fillRect(0, height - 50, width, 50);
// 绘制地板纹理
ctx.strokeStyle = '#95a5a6';
ctx.lineWidth = 1;
for (let i = 0; i < width; i += 20) {
ctx.beginPath();
ctx.moveTo(i, height - 50);
ctx.lineTo(i, height);
ctx.stroke();
}
}
// 绘制跑步机
function drawTreadmill(ctx, width, height, offset) {
const treadmillY = height - 120;
// 绘制跑步机底座
ctx.fillStyle = '#e74c3c';
ctx.fillRect(width / 2 - 100, treadmillY, 200, 20);
// 绘制跑步机传送带
ctx.fillStyle = '#34495e';
ctx.fillRect(width / 2 - 90, treadmillY - 5, 180, 5);
// 绘制传送带纹理
ctx.strokeStyle = '#2c3e50';
ctx.lineWidth = 1;
for (let i = 0; i < 180; i += 10) {
ctx.beginPath();
ctx.moveTo(width / 2 - 90 + ((i + offset) % 180), treadmillY - 5);
ctx.lineTo(width / 2 - 90 + ((i + offset) % 180), treadmillY);
ctx.stroke();
}
// 绘制跑步机扶手
ctx.strokeStyle = '#e74c3c';
ctx.lineWidth = 5;
ctx.beginPath();
ctx.moveTo(width / 2 - 80, treadmillY - 5);
ctx.lineTo(width / 2 - 80, treadmillY - 60);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(width / 2 + 80, treadmillY - 5);
ctx.lineTo(width / 2 + 80, treadmillY - 60);
ctx.stroke();
ctx.strokeStyle = '#c0392b';
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(width / 2 - 80, treadmillY - 60);
ctx.lineTo(width / 2 + 80, treadmillY - 60);
ctx.stroke();
}
// 绘制正常走路的人物
function drawPersonNormal(ctx, width, height, frameCount, legAngle) {
const personX = width / 2;
const personY = height - 150;
// 绘制身体
ctx.fillStyle = '#3498db';
ctx.beginPath();
ctx.arc(personX, personY - 20, 15, 0, Math.PI * 2);
ctx.fill();
// 绘制躯干
ctx.fillStyle = '#2980b9';
ctx.fillRect(personX - 10, personY - 20, 20, 40);
// 绘制头部
ctx.fillStyle = '#ffdbac';
ctx.beginPath();
ctx.arc(personX, personY - 40, 15, 0, Math.PI * 2);
ctx.fill();
// 绘制面部特征
ctx.fillStyle = '#000';
ctx.beginPath();
ctx.arc(personX - 5, personY - 43, 2, 0, Math.PI * 2);
ctx.arc(personX + 5, personY - 43, 2, 0, Math.PI * 2);
ctx.fill();
// 绘制嘴巴
ctx.beginPath();
ctx.arc(personX, personY - 38, 3, 0, Math.PI);
ctx.stroke();
// 绘制腿部
ctx.fillStyle = '#34495e';
drawLeg(ctx, personX - 8, personY + 20, legAngle);
drawLeg(ctx, personX + 8, personY + 20, -legAngle);
// 绘制手臂
ctx.fillStyle = '#2980b9';
drawArm(ctx, personX - 15, personY - 15, Math.sin(frameCount * 0.2) * 0.3);
drawArm(ctx, personX + 15, personY - 15, -Math.sin(frameCount * 0.2) * 0.3);
}
// 绘制摇头的人物
function drawPersonHeadShake(ctx, width, height, frameCount, legAngle, headAngle) {
drawPersonNormal(ctx, width, height, frameCount, legAngle);
const personX = width / 2;
const personY = height - 150;
// 覆盖头部绘制摇头效果
ctx.fillStyle = '#ffdbac';
ctx.save();
ctx.translate(personX, personY - 40);
ctx.rotate(headAngle);
ctx.beginPath();
ctx.arc(0, 0, 15, 0, Math.PI * 2);
ctx.fill();
// 绘制面部特征(随头部旋转)
ctx.fillStyle = '#000';
ctx.beginPath();
ctx.arc(-5, -3, 2, 0, Math.PI * 2);
ctx.arc(5, -3, 2, 0, Math.PI * 2);
ctx.fill();
// 绘制嘴巴
ctx.beginPath();
ctx.arc(0, 2, 3, 0, Math.PI);
ctx.stroke();
ctx.restore();
}
// 绘制点头的人物
function drawPersonHeadNod(ctx, width, height, frameCount, legAngle, headAngle) {
drawPersonNormal(ctx, width, height, frameCount, legAngle);
const personX = width / 2;
const personY = height - 150;
// 覆盖头部绘制点头效果
ctx.fillStyle = '#ffdbac';
ctx.save();
ctx.translate(personX, personY - 40);
ctx.rotate(headAngle * 0.5);
ctx.beginPath();
ctx.arc(0, 0, 15, 0, Math.PI * 2);
ctx.fill();
// 绘制面部特征(随头部旋转)
ctx.fillStyle = '#000';
ctx.beginPath();
ctx.arc(-5, -3, 2, 0, Math.PI * 2);
ctx.arc(5, -3, 2, 0, Math.PI * 2);
ctx.fill();
// 绘制嘴巴
ctx.beginPath();
ctx.arc(0, 2, 3, 0, Math.PI);
ctx.stroke();
ctx.restore();
}
// 绘制说话的人物
function drawPersonTalking(ctx, width, height, frameCount, legAngle, speechBubble) {
drawPersonNormal(ctx, width, height, frameCount, legAngle);
const personX = width / 2;
const personY = height - 150;
// 绘制说话的嘴巴
ctx.fillStyle = '#000';
if (speechBubble < 30) {
ctx.beginPath();
ctx.arc(personX, personY - 38, 4, 0, Math.PI * 2);
ctx.fill();
} else {
ctx.beginPath();
ctx.arc(personX, personY - 38, 3, 0, Math.PI);
ctx.stroke();
}
// 绘制对话气泡
if (speechBubble > 40) {
ctx.fillStyle = 'rgba(255, 255, 255, 0.9)';
ctx.beginPath();
ctx.ellipse(personX + 30, personY - 70, 20, 15, 0, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#000';
ctx.font = '12px Arial';
ctx.fillText('Hello!', personX + 20, personY - 68);
// 绘制气泡指针
ctx.beginPath();
ctx.moveTo(personX + 20, personY - 60);
ctx.lineTo(personX + 10, personY - 50);
ctx.lineTo(personX + 25, personY - 58);
ctx.fill();
}
}
// 绘制腿部
function drawLeg(ctx, x, y, angle) {
ctx.save();
ctx.translate(x, y);
ctx.rotate(angle);
ctx.fillRect(-3, 0, 6, 30);
// 绘制脚
ctx.fillStyle = '#16a085';
ctx.fillRect(-5, 30, 10, 5);
ctx.restore();
}
// 绘制手臂
function drawArm(ctx, x, y, angle) {
ctx.save();
ctx.translate(x, y);
ctx.rotate(angle);
ctx.fillRect(-3, 0, 6, 25);
ctx.restore();
}
// 初始化所有画布
function initCanvases() {
for (let i = 1; i <= 4; i++) {
const ctx = canvases[i];
const canvas = ctx.canvas;
drawBackground(ctx, canvas.width, canvas.height);
drawTreadmill(ctx, canvas.width, canvas.height, 0);
}
}
// 页面加载时初始化
window.onload = initCanvases;
</script>
</body>
</html>