汉阳造步枪 - 精修版(无刺刀) + 摄像机系统
by TurboKoala83357 lines11.1 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,
y: canvas.height / 2,
zoom: 1.0,
rotation: 0,
isDragging: false,
dragButton: 0,
lastX: 0,
lastY: 0
};
// =====================================================================
// 汉阳造步枪 - 精修版(无刺刀)
// 原点 (0,0) 在【扳机位置】
// x 轴正方向为枪口方向(向右),y 轴正方向向下
// =====================================================================
function drawHanyangRifle(ctx) {
// ---------------------------------------------------------
// 1. 枪托后部(木质)- 根据照片精修的流畅曲线
// ---------------------------------------------------------
// 使用渐变增强木质立体感
const stockGrad = ctx.createLinearGradient(0, -15, 0, 45);
stockGrad.addColorStop(0, '#4A2E15');
stockGrad.addColorStop(0.5, '#6B4A2A');
stockGrad.addColorStop(1, '#3A2210');
ctx.fillStyle = stockGrad;
ctx.beginPath();
ctx.moveTo(-25, -8); // 顶部:连接机匣
ctx.lineTo(-130, -5); // 枪托上沿
ctx.quadraticCurveTo(-160, 5, -155, 28); // 优美的向下弧度
ctx.lineTo(-130, 38); // 底部后段
ctx.quadraticCurveTo(-60, 48, -35, 28); // 底部:平滑的握把弧线
ctx.lineTo(-25, 12); // 底部:连接扳机护圈
ctx.closePath();
ctx.fill();
// 木质高光
ctx.fillStyle = 'rgba(255, 255, 255, 0.1)';
ctx.beginPath();
ctx.moveTo(-25, -8);
ctx.lineTo(-130, -5);
ctx.lineTo(-130, 0);
ctx.lineTo(-25, -3);
ctx.closePath();
ctx.fill();
// 握把底部的防滑纹理(对应照片中的握把凹槽)
ctx.strokeStyle = 'rgba(0, 0, 0, 0.2)';
ctx.lineWidth = 1;
for (let i = 0; i < 3; i++) {
ctx.beginPath();
ctx.arc(-75, 32, 12 + i * 4, 0.2, Math.PI - 0.2);
ctx.stroke();
}
// 枪托尾端的金属托底板
ctx.fillStyle = '#2A2A2A';
ctx.beginPath();
ctx.moveTo(-155, 28);
ctx.quadraticCurveTo(-165, 15, -160, -5);
ctx.lineTo(-152, -5);
ctx.quadraticCurveTo(-157, 15, -148, 30);
ctx.closePath();
ctx.fill();
// ---------------------------------------------------------
// 2. 下护木(木质)- 从扳机延伸到枪管前端
// ---------------------------------------------------------
const lowerGrad = ctx.createLinearGradient(0, -10, 0, 20);
lowerGrad.addColorStop(0, '#6B4A2A');
lowerGrad.addColorStop(0.5, '#8A5A3A');
lowerGrad.addColorStop(1, '#4A2E15');
ctx.fillStyle = lowerGrad;
ctx.beginPath();
ctx.moveTo(-25, -8);
ctx.lineTo(170, -8); // 下护木顶部
ctx.lineTo(170, 15); // 下护木底部
ctx.lineTo(-25, 15);
ctx.closePath();
ctx.fill();
// ---------------------------------------------------------
// 3. 上护木(木质)- 覆盖枪管上方,中间留出缝隙
// ---------------------------------------------------------
const upperGrad = ctx.createLinearGradient(0, -20, 0, -8);
upperGrad.addColorStop(0, '#4A2E15');
upperGrad.addColorStop(1, '#6B4A2A');
ctx.fillStyle = upperGrad;
ctx.beginPath();
ctx.moveTo(-25, -20); // 上护木后段
ctx.lineTo(150, -20); // 上护木前端(比下护木短一点)
ctx.lineTo(150, -8);
ctx.lineTo(-25, -8);
ctx.closePath();
ctx.fill();
// 上护木的立体阴影线
ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
ctx.fillRect(-25, -10, 175, 2);
// ---------------------------------------------------------
// 4. 枪管(金属)- 位于上下护木之间,向前延伸
// ---------------------------------------------------------
const barrelStart = 150;
const barrelLength = 200;
// 金属渐变(圆柱感)
const metalGrad = ctx.createLinearGradient(0, -6, 0, 6);
metalGrad.addColorStop(0, '#6A6A6A');
metalGrad.addColorStop(0.5, '#9A9A9A');
metalGrad.addColorStop(1, '#3A3A3A');
ctx.fillStyle = metalGrad;
ctx.fillRect(barrelStart, -6, barrelLength, 12);
// 枪管顶部高光
ctx.fillStyle = 'rgba(255, 255, 255, 0.4)';
ctx.fillRect(barrelStart, -5, barrelLength, 2);
// ---------------------------------------------------------
// 5. 金属枪箍(固定上下护木和枪管)
// ---------------------------------------------------------
const clampPositions = [80, 155];
clampPositions.forEach(x => {
ctx.fillStyle = '#333333';
ctx.fillRect(x, -22, 10, 38); // 金属箍主体
ctx.fillStyle = '#666666';
ctx.fillRect(x, -22, 2, 38); // 左侧高光
ctx.fillStyle = '#111111';
ctx.fillRect(x + 8, -22, 2, 38); // 右侧阴影
});
// ---------------------------------------------------------
// 6. 机匣、枪栓与扳机(金属)
// ---------------------------------------------------------
// 机匣底座
ctx.fillStyle = '#2A2A2A';
ctx.fillRect(-25, -15, 40, 30);
// 机匣顶部高光
ctx.fillStyle = '#555555';
ctx.fillRect(-25, -15, 40, 2);
// 枪栓拉柄(向下弯曲的柄 + 球头)
ctx.fillStyle = '#4A4A4A';
ctx.beginPath();
ctx.moveTo(-5, -12);
ctx.quadraticCurveTo(-10, -28, -5, -32);
ctx.lineTo(5, -28);
ctx.quadraticCurveTo(0, -25, 3, -12);
ctx.closePath();
ctx.fill();
// 球状拉柄头
ctx.fillStyle = '#888888';
ctx.beginPath();
ctx.arc(0, -32, 7, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#AAAAAA';
ctx.beginPath();
ctx.arc(-2, -34, 3, 0, Math.PI * 2);
ctx.fill();
// 扳机护圈(金属弧线)
ctx.strokeStyle = '#555555';
ctx.lineWidth = 3;
ctx.beginPath();
ctx.arc(0, 18, 12, 0, Math.PI);
ctx.stroke();
// 扳机
ctx.fillStyle = '#333333';
ctx.fillRect(-2, 12, 4, 14);
// ---------------------------------------------------------
// 7. 表尺照门(立框式表尺)
// ---------------------------------------------------------
ctx.fillStyle = '#333333';
ctx.fillRect(35, -22, 15, 12); // 底座
ctx.fillStyle = '#666666';
ctx.fillRect(40, -40, 4, 18); // 立框
// 表尺游标
ctx.fillStyle = '#999999';
ctx.fillRect(37, -32, 10, 6);
// 缺口
ctx.fillStyle = '#1A1A1A';
ctx.fillRect(41, -40, 2, 4);
// ---------------------------------------------------------
// 8. 枪口与准星(无刺刀)
// ---------------------------------------------------------
const muzzleX = barrelStart + barrelLength;
// 枪口帽
ctx.fillStyle = '#333333';
ctx.fillRect(muzzleX - 5, -8, 5, 16);
// 准星底座
ctx.fillStyle = '#555555';
ctx.fillRect(muzzleX - 20, -14, 8, 6);
// 刀片状准星
ctx.fillStyle = '#999999';
ctx.beginPath();
ctx.moveTo(muzzleX - 16, -14);
ctx.lineTo(muzzleX - 12, -14);
ctx.lineTo(muzzleX - 14, -22);
ctx.closePath();
ctx.fill();
// ---------------------------------------------------------
// 9. 枪背带(皮革,连接枪托和枪箍)
// ---------------------------------------------------------
// 前挂环
ctx.fillStyle = '#222222';
ctx.beginPath();
ctx.arc(85, 16, 4, 0, Math.PI * 2);
ctx.fill();
// 后挂环
ctx.beginPath();
ctx.arc(-145, 38, 4, 0, Math.PI * 2);
ctx.fill();
// 背带主体
ctx.strokeStyle = '#5A4A3A';
ctx.lineWidth = 5;
ctx.beginPath();
ctx.moveTo(-145, 38);
ctx.quadraticCurveTo(-50, 75, 20, 55); // 自然下垂的弧度
ctx.lineTo(85, 16);
ctx.stroke();
// 背带边缘线(增加质感)
ctx.strokeStyle = 'rgba(0,0,0,0.3)';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(-145, 36);
ctx.quadraticCurveTo(-50, 72, 20, 52);
ctx.stroke();
}
// =====================================================================
// 主循环 + 摄像机变换
// =====================================================================
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);
ctx.rotate(camera.rotation);
ctx.scale(camera.zoom, camera.zoom);
ctx.translate(-camera.x, -camera.y);
// 在画布中央绘制枪械
ctx.save();
ctx.translate(canvas.width / 2 - 50, canvas.height / 2); // 原点居中偏左
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;
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();
camera.zoom *= e.deltaY > 0 ? 0.9 : 1.1;
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 (357 lines, 11.1 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-mup4jsee" to link back to the original. Then publish at arcadelab.ai/publish.