qs_xinchun2026_h5/canvas.html

580 lines
19 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Canvas图片拖放功能</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
color: #333;
}
.header {
text-align: center;
margin-bottom: 30px;
max-width: 800px;
}
h1 {
font-size: 2.5rem;
color: #2c3e50;
margin-bottom: 10px;
text-shadow: 1px 1px 3px rgba(0,0,0,0.1);
}
.subtitle {
font-size: 1.2rem;
color: #7f8c8d;
margin-bottom: 20px;
}
.container {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 30px;
max-width: 1200px;
width: 100%;
}
.canvas-container {
background-color: white;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
padding: 20px;
flex: 1;
min-width: 300px;
max-width: 700px;
}
canvas {
display: block;
border-radius: 8px;
background-color: white;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
margin: 0 auto;
}
.controls {
background-color: white;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
padding: 25px;
flex: 0 0 300px;
display: flex;
flex-direction: column;
gap: 20px;
}
.control-group {
padding: 15px;
border-radius: 8px;
background-color: #f8f9fa;
}
h2 {
font-size: 1.5rem;
color: #2c3e50;
margin-bottom: 15px;
padding-bottom: 8px;
border-bottom: 2px solid #eaeaea;
}
h3 {
font-size: 1.2rem;
color: #3498db;
margin-bottom: 10px;
}
.instructions {
line-height: 1.6;
color: #555;
}
.instructions li {
margin-bottom: 8px;
padding-left: 5px;
}
.target-area {
background-color: #e8f4fc;
border: 2px dashed #3498db;
border-radius: 8px;
padding: 15px;
text-align: center;
margin-top: 10px;
}
.target-area.active {
background-color: #d1f2eb;
border-color: #2ecc71;
}
.status {
padding: 12px;
border-radius: 8px;
background-color: #f9f9f9;
text-align: center;
font-weight: 500;
transition: all 0.3s;
}
.status.dragging {
background-color: #fff9e6;
color: #e67e22;
}
.status.success {
background-color: #d4f8e8;
color: #27ae60;
}
.reset-btn {
background-color: #3498db;
color: white;
border: none;
padding: 14px 20px;
border-radius: 8px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
width: 100%;
}
.reset-btn:hover {
background-color: #2980b9;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(41, 128, 185, 0.2);
}
.character-info {
background-color: #fff8e1;
border-left: 4px solid #ffb300;
padding: 12px 15px;
border-radius: 0 8px 8px 0;
}
.stats {
display: flex;
justify-content: space-between;
background-color: #f8f9fa;
padding: 10px 15px;
border-radius: 8px;
font-size: 0.9rem;
}
.stat-item {
text-align: center;
}
.stat-value {
font-size: 1.3rem;
font-weight: 700;
color: #3498db;
}
.stat-label {
color: #7f8c8d;
font-size: 0.9rem;
}
footer {
margin-top: 40px;
text-align: center;
color: #7f8c8d;
font-size: 0.9rem;
padding: 20px;
}
@media (max-width: 768px) {
.container {
flex-direction: column;
align-items: center;
}
.controls {
width: 100%;
max-width: 700px;
}
}
</style>
</head>
<body>
<div class="header">
<h1>Canvas图片拖放功能</h1>
<p class="subtitle">将橙色角色拖动到目标区域体验HTML5 Canvas的交互功能</p>
</div>
<div class="container">
<div class="canvas-container">
<h2>拖放画布</h2>
<canvas id="myCanvas" width="650" height="500"></canvas>
<div class="character-info">
<p><strong>角色说明:</strong>这是一个橙色卡通角色,正在向后转身。角色背部有白色椭圆形特征,右臂向后摆动,左臂略微向前,呈现动态效果。</p>
</div>
</div>
<div class="controls">
<div class="control-group">
<h3>操作说明</h3>
<ul class="instructions">
<li>1. 在左侧画布上点击并按住橙色角色</li>
<li>2. 拖动角色到任意位置</li>
<li>3. 将角色移动到下方目标区域</li>
<li>4. 释放鼠标放下角色</li>
</ul>
<div class="target-area" id="targetArea">
<strong>目标区域</strong><br>
将角色拖放到这里
</div>
</div>
<div class="stats">
<div class="stat-item">
<div class="stat-value" id="dragCount">0</div>
<div class="stat-label">拖放次数</div>
</div>
<div class="stat-item">
<div class="stat-value" id="targetHits">0</div>
<div class="stat-label">命中目标</div>
</div>
<div class="stat-item">
<div class="stat-value" id="distance">0</div>
<div class="stat-label">移动距离(px)</div>
</div>
</div>
<div class="status" id="status">
点击并拖动角色开始体验
</div>
<button class="reset-btn" id="resetBtn">重置角色位置</button>
</div>
</div>
<footer>
<p>HTML5 Canvas 拖放功能示例 | 基于用户提供的图片描述实现</p>
</footer>
<script>
document.addEventListener('DOMContentLoaded', function() {
// 获取Canvas和上下文
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// 获取DOM元素
const status = document.getElementById('status');
const resetBtn = document.getElementById('resetBtn');
const targetArea = document.getElementById('targetArea');
const dragCountElement = document.getElementById('dragCount');
const targetHitsElement = document.getElementById('targetHits');
const distanceElement = document.getElementById('distance');
// 图片对象
const character = {
x: 100,
y: 100,
width: 150,
height: 200,
isDragging: false,
offsetX: 0,
offsetY: 0,
color: '#FF8C00', // 橙色
text: 'A'
};
// 目标区域
const target = {
x: 400,
y: 300,
width: 200,
height: 150
};
// 统计数据
let stats = {
dragCount: 0,
targetHits: 0,
totalDistance: 0
};
// 更新统计显示
function updateStats() {
dragCountElement.textContent = stats.dragCount;
targetHitsElement.textContent = stats.targetHits;
distanceElement.textContent = Math.round(stats.totalDistance);
}
// 绘制角色
function drawCharacter() {
// 绘制角色主体
ctx.fillStyle = character.color;
// 绘制身体
ctx.beginPath();
ctx.ellipse(
character.x + character.width/2,
character.y + character.height/2,
character.width/3,
character.height/2.5,
0, 0, Math.PI * 2
);
ctx.fill();
// 绘制头部
ctx.beginPath();
ctx.arc(character.x + character.width/2, character.y + 40, 30, 0, Math.PI * 2);
ctx.fill();
// 绘制背部白色标记
ctx.fillStyle = 'white';
ctx.beginPath();
ctx.ellipse(
character.x + character.width/2,
character.y + character.height/2,
15,
25,
0, 0, Math.PI * 2
);
ctx.fill();
// 绘制四肢
ctx.fillStyle = character.color;
// 左臂
ctx.fillRect(character.x + 10, character.y + 80, 20, 60);
// 右臂
ctx.fillRect(character.x + character.width - 30, character.y + 80, 20, 60);
// 左腿
ctx.fillRect(character.x + 40, character.y + 150, 20, 50);
// 右腿
ctx.fillRect(character.x + 90, character.y + 150, 20, 50);
// 绘制鞋子
ctx.fillStyle = '#333';
ctx.fillRect(character.x + 35, character.y + 195, 30, 10);
ctx.fillRect(character.x + 85, character.y + 195, 30, 10);
// 绘制面部特征
ctx.fillStyle = 'white';
ctx.beginPath();
ctx.arc(character.x + character.width/2 - 10, character.y + 35, 5, 0, Math.PI * 2);
ctx.arc(character.x + character.width/2 + 10, character.y + 35, 5, 0, Math.PI * 2);
ctx.fill();
// 绘制嘴部
ctx.beginPath();
ctx.arc(character.x + character.width/2, character.y + 50, 8, 0, Math.PI);
ctx.stroke();
// 绘制角色上的文字
ctx.fillStyle = 'white';
ctx.font = 'bold 24px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(character.text, character.x + character.width/2, character.y + 100);
// 如果正在拖拽,绘制高亮边框
if (character.isDragging) {
ctx.strokeStyle = '#3498db';
ctx.lineWidth = 3;
ctx.setLineDash([5, 5]);
ctx.strokeRect(character.x - 5, character.y - 5, character.width + 10, character.height + 10);
ctx.setLineDash([]);
}
}
// 绘制目标区域
function drawTarget() {
ctx.fillStyle = 'rgba(52, 152, 219, 0.1)';
ctx.strokeStyle = '#3498db';
ctx.lineWidth = 2;
ctx.setLineDash([5, 5]);
ctx.fillRect(target.x, target.y, target.width, target.height);
ctx.strokeRect(target.x, target.y, target.width, target.height);
ctx.setLineDash([]);
// 绘制目标区域内的文字
ctx.fillStyle = '#3498db';
ctx.font = 'bold 20px Arial';
ctx.textAlign = 'center';
ctx.fillText('放置区域', target.x + target.width/2, target.y + target.height/2);
}
// 绘制Canvas
function drawCanvas() {
// 清除Canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 绘制目标区域
drawTarget();
// 绘制角色
drawCharacter();
// 检查角色是否在目标区域内
checkTargetHit();
}
// 检查角色是否在目标区域内
function checkTargetHit() {
const charCenterX = character.x + character.width/2;
const charCenterY = character.y + character.height/2;
const isInTarget =
charCenterX > target.x &&
charCenterX < target.x + target.width &&
charCenterY > target.y &&
charCenterY < target.y + target.height;
if (isInTarget) {
targetArea.classList.add('active');
status.textContent = "成功!角色已在目标区域内";
status.className = "status success";
} else {
targetArea.classList.remove('active');
if (character.isDragging) {
status.textContent = "拖动角色到目标区域";
status.className = "status dragging";
} else {
status.textContent = "点击并拖动角色开始体验";
status.className = "status";
}
}
return isInTarget;
}
// 检查点击是否在角色上
function isPointInCharacter(x, y) {
return x > character.x &&
x < character.x + character.width &&
y > character.y &&
y < character.y + character.height;
}
// 计算两点之间的距离
function calculateDistance(x1, y1, x2, y2) {
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
}
// 鼠标按下事件
function handleMouseDown(e) {
const rect = canvas.getBoundingClientRect();
const mouseX = e.clientX - rect.left;
const mouseY = e.clientY - rect.top;
if (isPointInCharacter(mouseX, mouseY)) {
character.isDragging = true;
character.offsetX = mouseX - character.x;
character.offsetY = mouseY - character.y;
// 记录开始拖拽的位置
character.startDragX = character.x;
character.startDragY = character.y;
drawCanvas();
}
}
// 鼠标移动事件
function handleMouseMove(e) {
if (!character.isDragging) return;
const rect = canvas.getBoundingClientRect();
const mouseX = e.clientX - rect.left;
const mouseY = e.clientY - rect.top;
// 计算移动距离
const distanceMoved = calculateDistance(
character.x + character.width/2,
character.y + character.height/2,
mouseX - character.offsetX + character.width/2,
mouseY - character.offsetY + character.height/2
);
stats.totalDistance += distanceMoved;
// 更新角色位置
character.x = mouseX - character.offsetX;
character.y = mouseY - character.offsetY;
// 限制角色不超出Canvas边界
character.x = Math.max(0, Math.min(canvas.width - character.width, character.x));
character.y = Math.max(0, Math.min(canvas.height - character.height, character.y));
drawCanvas();
updateStats();
}
// 鼠标释放事件
function handleMouseUp() {
if (character.isDragging) {
character.isDragging = false;
stats.dragCount++;
// 检查是否在目标区域内
if (checkTargetHit()) {
stats.targetHits++;
}
drawCanvas();
updateStats();
}
}
// 重置角色位置
function resetCharacter() {
character.x = 100;
character.y = 100;
character.isDragging = false;
// 重置目标区域状态
targetArea.classList.remove('active');
status.textContent = "点击并拖动角色开始体验";
status.className = "status";
drawCanvas();
}
// 初始化
function init() {
drawCanvas();
// 添加事件监听器
canvas.addEventListener('mousedown', handleMouseDown);
canvas.addEventListener('mousemove', handleMouseMove);
canvas.addEventListener('mouseup', handleMouseUp);
// 添加重置按钮事件监听器
resetBtn.addEventListener('click', resetCharacter);
// 更新统计
updateStats();
}
// 启动应用
init();
});
</script>
</body>
</html>