汉阳造步枪 - 真实比例 + 摄像机系统
by TurboKoala83377 lines11.8 KB
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>汉阳造步枪 - 真实比例 + 摄像机系统</title>
<style>
body { margin: 0; background: #1a1a2e; display: flex; justify-content: center; align-items: center; height: 100vh; overflow: hidden; font-family: 'Microsoft YaHei', sans-serif; }
canvas { background: linear-gradient(180deg, #16213e 0%, #0f3460 100%); border-radius: 12px; box-shadow: 0 0 40px rgba(196, 30, 58, 0.3); cursor: grab; }
canvas:active { cursor: grabbing; }
.info { position: absolute; top: 20px; left: 50%; transform: translateX(-50%); color: #c41e3a; font-size: 14px; letter-spacing: 4px; text-shadow: 0 0 10px rgba(196, 30, 58, 0.8); pointer-events: none; }
.controls { position: absolute; bottom: 20px; color: #888; font-size: 12px; letter-spacing: 2px; text-align: center; width: 100%; pointer-events: none; }
</style>
</head>
<body>
<div class="info">★ 汉阳造步枪 · 真实比例 + 全方位观察 ★</div>
<div class="controls">左键拖拽平移 | 右键拖拽旋转 | 滚轮缩放 | 双击重置视角</div>
<canvas id="game" width="900" height="600"></canvas>
<script>
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
// ========== 摄像机系统 ==========
const camera = {
x: canvas.width / 2, // 摄像机中心 X
y: canvas.height / 2, // 摄像机中心 Y
zoom: 1.0, // 缩放比例
rotation: 0, // 旋转角度(弧度)
isDragging: false,
dragButton: 0, // 0: 左键平移, 2: 右键旋转
lastX: 0,
lastY: 0
};
// ========== 枪械配色 ==========
const GUN_COLORS = {
wood: '#5E3A20', // 枪托木质主色(深栗色)
woodDark: '#3A2210', // 木质阴影
woodLite: '#8A5A3A', // 木质高光
metal: '#4A4A4A', // 金属主色
metalDark: '#2A2A2A', // 金属阴影
metalLite: '#9A9A9A', // 金属高光
strap: '#6A5A3A', // 枪背带(皮革/帆布)
};
// ========== 绘制工具 ==========
function roundRect(ctx, x, y, w, h, r) {
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.lineTo(x + w - r, y);
ctx.quadraticCurveTo(x + w, y, x + w, y + r);
ctx.lineTo(x + w, y + h - r);
ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
ctx.lineTo(x + r, y + h);
ctx.quadraticCurveTo(x, y + h, x, y + h - r);
ctx.lineTo(x, y + r);
ctx.quadraticCurveTo(x, y, x + r, y);
ctx.closePath();
}
// =====================================================================
// 汉阳造步枪 - 真实比例建模(基于照片结构)
// 原点 (0,0) 在【扳机位置】
// x 轴正方向为枪口方向(向右),y 轴正方向向下
// =====================================================================
function drawHanyangRifle(ctx) {
const C = GUN_COLORS;
// ---------------------------------------------------------
// 1. 枪托后部(木质)- 带有优雅的向下弯曲
// ---------------------------------------------------------
ctx.fillStyle = C.wood;
ctx.beginPath();
ctx.moveTo(-30, -8); // 顶部靠前(机匣后方)
ctx.lineTo(-150, -5); // 顶部靠后(枪托上沿)
ctx.quadraticCurveTo(-170, 5, -165, 25); // 枪托后部弧形向下
ctx.lineTo(-140, 35); // 枪托下沿后部
ctx.quadraticCurveTo(-80, 45, -40, 25); // 底部平滑弧线(握把处)
ctx.lineTo(-30, 10); // 底部靠前
ctx.closePath();
ctx.fill();
// 枪托木质纹理与立体感
ctx.fillStyle = C.woodDark;
ctx.fillRect(-160, -5, 120, 3); // 顶部阴影
ctx.fillStyle = C.woodLite;
ctx.fillRect(-160, 30, 100, 2); // 底部高光
// 枪托后部的金属托底板
ctx.fillStyle = C.metalDark;
ctx.beginPath();
ctx.moveTo(-165, 25);
ctx.quadraticCurveTo(-175, 10, -170, -5);
ctx.lineTo(-162, -5);
ctx.quadraticCurveTo(-168, 10, -160, 30);
ctx.closePath();
ctx.fill();
// 握把凹陷处(更符合人体工学)
ctx.fillStyle = C.woodDark;
ctx.beginPath();
ctx.arc(-70, 32, 15, 0, Math.PI, true);
ctx.fill();
// ---------------------------------------------------------
// 2. 机匣与枪栓(金属结构)
// ---------------------------------------------------------
// 机匣底座
ctx.fillStyle = C.metalDark;
ctx.fillRect(-30, -12, 50, 24);
// 枪栓拉柄 - 直拉栓 + 向下弯曲的柄
ctx.fillStyle = C.metal;
ctx.beginPath();
ctx.moveTo(-10, -10);
ctx.lineTo(-15, -25);
ctx.lineTo(-5, -25);
ctx.lineTo(0, -10);
ctx.closePath();
ctx.fill();
// 枪栓球状把手
ctx.fillStyle = C.metalLite;
ctx.beginPath();
ctx.arc(-10, -25, 6, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = C.metal;
ctx.beginPath();
ctx.arc(-9, -26, 3, 0, Math.PI * 2);
ctx.fill();
// 扳机护圈
ctx.strokeStyle = C.metal;
ctx.lineWidth = 3;
ctx.beginPath();
ctx.arc(0, 15, 10, 0, Math.PI);
ctx.stroke();
// 扳机
ctx.fillStyle = C.metalDark;
ctx.fillRect(-2, 10, 4, 12);
// ---------------------------------------------------------
// 3. 枪身中部 - 木质前护木(包裹枪管)
// ---------------------------------------------------------
ctx.fillStyle = C.wood;
ctx.beginPath();
ctx.moveTo(-30, -12);
ctx.lineTo(180, -12); // 护木顶部延伸到枪管箍
ctx.lineTo(180, 12); // 护木底部
ctx.lineTo(-30, 12);
ctx.closePath();
ctx.fill();
// 护木立体感
ctx.fillStyle = C.woodLite;
ctx.fillRect(-30, -12, 210, 3); // 顶部高光
ctx.fillStyle = C.woodDark;
ctx.fillRect(-30, 9, 210, 3); // 底部阴影
// 护木上的金属箍(老式步枪特征,固定护木)
ctx.fillStyle = C.metalDark;
ctx.fillRect(80, -14, 8, 28); // 前箍
ctx.fillRect(160, -14, 8, 28); // 后箍
// ---------------------------------------------------------
// 4. 枪管(金属)- 从护木前端伸出
// ---------------------------------------------------------
const barrelStart = 180;
const barrelLength = 220;
ctx.fillStyle = C.metal;
ctx.fillRect(barrelStart, -7, barrelLength, 14);
// 枪管高光与阴影(圆柱感)
ctx.fillStyle = C.metalLite;
ctx.fillRect(barrelStart, -6, barrelLength, 3);
ctx.fillStyle = C.metalDark;
ctx.fillRect(barrelStart, 5, barrelLength, 2);
// ---------------------------------------------------------
// 5. 表尺照门(汉阳造特征:立框式表尺)
// ---------------------------------------------------------
ctx.fillStyle = C.metalDark;
ctx.fillRect(20, -20, 15, 10); // 表尺底座
ctx.fillStyle = C.metal;
ctx.fillRect(25, -35, 4, 15); // 表尺立框
// 表尺游标
ctx.fillStyle = C.metalLite;
ctx.fillRect(22, -30, 10, 6);
// 照门缺口
ctx.fillStyle = '#1A1A1A';
ctx.fillRect(26, -35, 2, 4);
// ---------------------------------------------------------
// 6. 枪口与准星
// ---------------------------------------------------------
const muzzleX = barrelStart + barrelLength;
// 枪口
ctx.fillStyle = C.metalDark;
ctx.fillRect(muzzleX - 8, -9, 8, 18);
// 准星底座
ctx.fillStyle = C.metal;
ctx.fillRect(muzzleX - 25, -18, 10, 10);
// 刀片形准星
ctx.fillStyle = C.metalLite;
ctx.beginPath();
ctx.moveTo(muzzleX - 20, -18);
ctx.lineTo(muzzleX - 15, -18);
ctx.lineTo(muzzleX - 17.5, -28);
ctx.closePath();
ctx.fill();
// ---------------------------------------------------------
// 7. 刺刀(带刀柄和卡扣结构)
// ---------------------------------------------------------
const bayonetLength = 110;
// 刺刀座(连接枪口)
ctx.fillStyle = C.metalDark;
ctx.fillRect(muzzleX - 4, -5, 10, 14);
// 刺刀刀柄(木质/金属)
ctx.fillStyle = C.wood;
ctx.fillRect(muzzleX + 6, -4, 30, 10);
ctx.fillStyle = C.metalDark;
ctx.fillRect(muzzleX + 36, -4, 10, 10); // 刀格
// 刺刀刀身
ctx.fillStyle = C.metalLite;
ctx.beginPath();
ctx.moveTo(muzzleX + 46, -3);
ctx.lineTo(muzzleX + bayonetLength, -1);
ctx.lineTo(muzzleX + bayonetLength + 5, 0);
ctx.lineTo(muzzleX + bayonetLength, 1);
ctx.lineTo(muzzleX + 46, 3);
ctx.closePath();
ctx.fill();
// 血槽
ctx.fillStyle = '#FFFFFF';
ctx.fillRect(muzzleX + 55, -1, bayonetLength - 65, 1);
// 刀柄上的固定环
ctx.fillStyle = C.metalDark;
ctx.fillRect(muzzleX + 15, -7, 4, 15);
// ---------------------------------------------------------
// 8. 枪背带(帆布/皮革,自然下垂)
// ---------------------------------------------------------
ctx.strokeStyle = C.strap;
ctx.lineWidth = 5;
ctx.beginPath();
ctx.moveTo(-140, 35); // 从枪托后部挂载
ctx.quadraticCurveTo(-50, 70, 50, 40); // 自然下垂
ctx.lineTo(160, 12); // 连接到前护木箍
ctx.stroke();
// 背带环
ctx.fillStyle = C.metalDark;
ctx.beginPath();
ctx.arc(-140, 35, 4, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(160, 12, 4, 0, Math.PI * 2);
ctx.fill();
}
// =====================================================================
// 主循环 + 摄像机变换
// =====================================================================
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 背景网格(用于参照平移和旋转)
ctx.strokeStyle = 'rgba(255, 255, 255, 0.05)';
ctx.lineWidth = 1;
for (let i = 0; i < canvas.width; i += 60) {
ctx.beginPath(); ctx.moveTo(i, 0); ctx.lineTo(i, canvas.height); ctx.stroke();
}
for (let i = 0; i < canvas.height; i += 60) {
ctx.beginPath(); ctx.moveTo(0, i); ctx.lineTo(canvas.width, i); ctx.stroke();
}
// ===== 应用摄像机变换 =====
ctx.save();
ctx.translate(camera.x, camera.y); // 1. 平移到摄像机中心
ctx.rotate(camera.rotation); // 2. 旋转
ctx.scale(camera.zoom, camera.zoom); // 3. 缩放
ctx.translate(-camera.x, -camera.y); // 4. 偏移回来,确保在摄像机中心缩放旋转
// 在画布中央绘制枪械
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
ctx.save();
ctx.translate(centerX - 60, centerY); // 将枪械原点偏移到屏幕中间偏左
drawHanyangRifle(ctx);
ctx.restore();
ctx.restore();
// ===== 绘制 UI 信息 =====
ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
ctx.font = '12px sans-serif';
ctx.fillText(`缩放: ${camera.zoom.toFixed(2)}x | 旋转: ${(camera.rotation * 180 / Math.PI).toFixed(0)}° | 平移: (${camera.x.toFixed(0)}, ${camera.y.toFixed(0)})`, 20, 30);
requestAnimationFrame(animate);
}
// =====================================================================
// 交互事件监听(拖拽平移、右键旋转、滚轮缩放)
// =====================================================================
canvas.addEventListener('contextmenu', e => e.preventDefault()); // 禁用右键菜单
canvas.addEventListener('mousedown', (e) => {
camera.isDragging = true;
camera.dragButton = e.button; // 0: 左键, 2: 右键
camera.lastX = e.clientX;
camera.lastY = e.clientY;
});
canvas.addEventListener('mousemove', (e) => {
if (!camera.isDragging) return;
const dx = e.clientX - camera.lastX;
const dy = e.clientY - camera.lastY;
if (camera.dragButton === 0) {
// 左键:平移
camera.x += dx;
camera.y += dy;
} else if (camera.dragButton === 2) {
// 右键:旋转
camera.rotation += dx * 0.005;
}
camera.lastX = e.clientX;
camera.lastY = e.clientY;
});
canvas.addEventListener('mouseup', () => {
camera.isDragging = false;
});
canvas.addEventListener('mouseleave', () => {
camera.isDragging = false;
});
// 滚轮缩放
canvas.addEventListener('wheel', (e) => {
e.preventDefault();
const zoomFactor = e.deltaY > 0 ? 0.9 : 1.1;
camera.zoom *= zoomFactor;
// 限制缩放范围
camera.zoom = Math.max(0.2, Math.min(5.0, camera.zoom));
}, { passive: false });
// 双击重置视角
canvas.addEventListener('dblclick', () => {
camera.x = canvas.width / 2;
camera.y = canvas.height / 2;
camera.zoom = 1.0;
camera.rotation = 0;
});
// 启动主循环
animate();
</script>
</body>
</html>Game Source: 汉阳造步枪 - 真实比例 + 摄像机系统
Creator: TurboKoala83
Libraries: none
Complexity: complex (377 lines, 11.8 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: game-turbokoala83-mup4h0cx" to link back to the original. Then publish at arcadelab.ai/publish.