Files
dsProject/dsLightRag/XingJun/move.html
2025-09-10 07:14:53 +08:00

528 lines
27 KiB
HTML
Raw 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="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>箭头动画</title>
<style>
body { margin: 0; padding: 0; height: 100vh; display: flex; overflow: hidden; }
.background-container { position: relative; flex: 1; height: 100vh; background-image: url('background.png'); background-position: center; background-repeat: no-repeat; background-size: contain; z-index: 1; }
.control-panel { width: 200px; background-color: #fff; border-left: 1px solid #ccc; padding: 20px; height: 100vh; box-sizing: border-box; z-index: 2; overflow-y: auto; }
.control-section { margin-bottom: 30px; }
.control-section h3 { margin-top: 0; padding-bottom: 10px; border-bottom: 1px solid #eee; }
.image-selector { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin-top: 15px; }
.image-btn { width: 70px; height: 70px; border: 2px solid #ddd; border-radius: 4px; cursor: pointer; background-size: contain; background-repeat: no-repeat; background-position: center; transition: all 0.2s; }
.image-btn:hover { border-color: #4CAF50; transform: scale(1.05); }
.image-btn:active { transform: scale(0.98); }
.arrow-container { position: absolute; pointer-events: auto; }
.arrow-image { width: 155px; height: 173px; background-size: contain; background-repeat: no-repeat; cursor: move; }
.save-btn { width: 100%; padding: 10px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; margin-top: 20px; }
.save-btn:hover { background-color: #45a049; }
.arrow-text { position: absolute; top: '100%'; left: '50%'; transform: translateX(-50%); margin-top: '-5px'; font-weight: bold; color: red; font-size: 16px; white-space: nowrap; text-align: center; width: 100%; background-color: transparent; padding: 3px 8px; border-radius: 4px; }
#contextMenu { position: absolute; display: none; background: white; border: 1px solid #ccc; padding: 5px; z-index: 9999; }
#deleteArrow { padding: 3px 10px; cursor: pointer; }
.menu-separator { height: 1px; background-color: #ccc; margin: 5px 0; }
.menu-item { padding: 3px 10px; cursor: pointer; }
.menu-item:hover { background-color: #f0f0f0; }
.check-mark { display: inline-block; width: 16px; text-align: center; }
.center-dot { position: absolute; width: 8px; height: 8px; background: red; border-radius: 50%; transform: translate(-50%, -50%); pointer-events: none; z-index: 5; }
</style>
</head>
<body>
<div class="background-container"></div>
<div class="control-panel">
<div class="control-section">
<h3>增加箭头</h3>
<div class="image-selector">
<div class="image-btn" data-image="./1.png" style="background-image: url('./1.png');"></div>
<div class="image-btn" data-image="./2.png" style="background-image: url('./2.png');"></div>
<div class="image-btn" data-image="./3.png" style="background-image: url('./3.png');"></div>
<div class="image-btn" data-image="./4.png" style="background-image: url('./4.png');"></div>
<div class="image-btn" data-image="./5.png" style="background-image: url('./5.png');"></div>
<div class="image-btn" data-image="./6.png" style="background-image: url('./6.png');"></div>
</div>
<!-- 移除保存位置按钮 -->
<button class="save-btn" id="markCenterBtn" style="margin-top: 10px;">标记中心点</button>
<button class="save-btn" id="startAnimationBtn" style="margin-top: 10px;">开始</button>
<button class="save-btn" id="resetBtn" style="margin-top: 10px;">重置</button>
</div>
</div>
<div id="contextMenu">
<div id="deleteArrow">删除箭头</div>
<div class="menu-separator"></div>
<div class="menu-item" data-point="top-left"><span class="check-mark"></span>左上角</div>
<div class="menu-item" data-point="top-right"><span class="check-mark"></span>右上角</div>
<div class="menu-item" data-point="bottom-left"><span class="check-mark"></span>左下角</div>
<div class="menu-item" data-point="bottom-right"><span class="check-mark"></span>右下角</div>
<!-- 添加偏移量输入框 -->
<div class="menu-separator"></div>
<div class="menu-item input-item">
<label for="stop-distance">偏移量:</label>
<input type="number" id="stop-distance" min="0" max="100" value="20" style="width:60px;margin-left:5px;">
</div>
</div>
<script>
// 添加常量定义
const ARROW_WIDTH = 155;
const ARROW_HEIGHT = 173;
const STOP_DISTANCE = 50;
// 修复核心整理所有JS代码到单个script标签内
document.addEventListener('DOMContentLoaded', function() {
const backgroundContainer = document.querySelector('.background-container');
const imageButtons = document.querySelectorAll('.image-btn');
const saveBtn = document.getElementById('savePositionsBtn');
const markCenterBtn = document.getElementById('markCenterBtn');
const startAnimationBtn = document.getElementById('startAnimationBtn');
const resetBtn = document.getElementById('resetBtn');
const contextMenu = document.getElementById('contextMenu');
const deleteArrow = document.getElementById('deleteArrow');
let selectedArrow = null;
let centerDot = null;
// 添加重置按钮事件监听器
resetBtn.addEventListener('click', function() {
location.reload();
});
// 添加开始按钮事件监听器
startAnimationBtn.addEventListener('click', startAnimation);
// 动画控制函数 - 确保在DOMContentLoaded回调内部定义
function startAnimation() {
// 检查是否已设置中心点
if (!centerDot) {
alert('请先确定中心点');
return;
}
// 获取中心点坐标修复使用getBoundingClientRect确保坐标正确
const centerRect = centerDot.getBoundingClientRect();
const containerRect = backgroundContainer.getBoundingClientRect();
const centerX = centerRect.left - containerRect.left;
const centerY = centerRect.top - containerRect.top;
// 获取所有箭头
const arrows = document.querySelectorAll('.arrow-container');
if (arrows.length === 0) {
alert('没有可移动的箭头');
return;
}
// 为每个箭头添加动画
arrows.forEach(arrow => {
animateArrow(arrow, centerX, centerY);
});
}
// 单个箭头动画函数
function animateArrow(arrow, targetX, targetY) {
// 获取箭头图片尺寸固定为155px×173px
const arrowWidth = 155;
const arrowHeight = 173;
// 计算中心点偏移量(宽度和高度的一半)
const centerOffsetX = arrowWidth / 2;
const centerOffsetY = arrowHeight / 2;
// 定义停止距离(箭头中心点与目标中心点的距离)
const stopDistance = 50;
// 获取箭头当前位置(相对于背景容器)
const rect = arrow.getBoundingClientRect();
const containerRect = backgroundContainer.getBoundingClientRect();
// 当前位置需要加上偏移量,获取实际中心点位置
let currentX = rect.left - containerRect.left + centerOffsetX;
let currentY = rect.top - containerRect.top + centerOffsetY;
// 添加随机偏移避免重叠
const offsetRange = 20;
const randomOffsetX = (Math.random() - 0.5) * offsetRange;
const randomOffsetY = (Math.random() - 0.5) * offsetRange;
const finalTargetX = targetX + randomOffsetX;
const finalTargetY = targetY + randomOffsetY;
// 计算方向向量和距离
const dxTotal = finalTargetX - currentX;
const dyTotal = finalTargetY - currentY;
const distanceTotal = Math.sqrt(dxTotal * dxTotal + dyTotal * dyTotal);
// 计算调整后的目标位置保持stopDistance距离
let adjustedTargetX, adjustedTargetY;
if (distanceTotal > stopDistance) {
// 如果距离大于停止距离,计算新目标位置
const ratio = (distanceTotal - stopDistance) / distanceTotal;
adjustedTargetX = currentX + dxTotal * ratio;
adjustedTargetY = currentY + dyTotal * ratio;
} else {
// 如果已在停止距离内,保持当前位置
adjustedTargetX = currentX;
adjustedTargetY = currentY;
}
function updatePosition() {
const dx = adjustedTargetX - currentX;
const dy = adjustedTargetY - currentY;
const distance = Math.sqrt(dx * dx + dy * dy);
// 当距离小于2px时停止动画确保到达调整后的目标位置
if (distance < 2) {
arrow.style.left = (adjustedTargetX - centerOffsetX) + 'px';
arrow.style.top = (adjustedTargetY - centerOffsetY) + 'px';
return;
}
const step = Math.max(distance * 0.05, 1);
const moveX = (dx / distance) * step;
const moveY = (dy / distance) * step;
currentX += moveX;
currentY += moveY;
arrow.style.left = (currentX - centerOffsetX) + 'px';
arrow.style.top = (currentY - centerOffsetY) + 'px';
requestAnimationFrame(updatePosition);
}
updatePosition();
}
// 加载保存的元素
loadAllElements();
// 绑定箭头按钮事件
imageButtons.forEach(button => {
button.addEventListener('click', () => {
const imagePath = button.getAttribute('data-image');
addImageToContainer(imagePath);
});
});
// 保存按钮事件
// 删除保存按钮事件监听器
// saveBtn.addEventListener('click', saveAllElements);
// 右键菜单事件
deleteArrow.addEventListener('click', function() {
if (selectedArrow) {
selectedArrow.remove();
saveAllElements();
contextMenu.style.display = 'none';
selectedArrow = null;
}
});
// 为匹配角菜单项添加事件监听器
document.querySelectorAll('.menu-item').forEach(item => {
item.addEventListener('click', function() {
if (selectedArrow) {
const referencePoint = this.getAttribute('data-point');
selectedArrow.dataset.referencePoint = referencePoint;
saveAllElements(); // 保存更改
contextMenu.style.display = 'none';
selectedArrow = null;
}
});
});
// 加载保存的元素
// 点击空白处关闭菜单
document.addEventListener('click', function() {
contextMenu.style.display = 'none';
selectedArrow = null;
});
// 标记中心点功能
markCenterBtn.addEventListener('click', function() {
// 移除旧点
if (centerDot) {
centerDot.remove();
centerDot = null;
}
// 创建新点
function handleMapClick(e) {
const rect = backgroundContainer.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
centerDot = document.createElement('div');
centerDot.className = 'center-dot';
centerDot.style.left = x + 'px';
centerDot.style.top = y + 'px';
backgroundContainer.appendChild(centerDot);
backgroundContainer.removeEventListener('click', handleMapClick);
// 修复中心点保存 - 添加详细日志
console.log('[中心点] 已标记中心点:', { left: centerDot.style.left, top: centerDot.style.top });
saveAllElements(); // 确保保存中心点
}
backgroundContainer.addEventListener('click', handleMapClick);
});
// 添加箭头函数
function addImageToContainer(imagePath, position = null, textContent = '') {
const container = document.createElement('div');
container.className = 'arrow-container';
container.dataset.imagePath = imagePath;
const newImage = document.createElement('div');
newImage.className = 'arrow-image';
newImage.style.backgroundImage = `url('${imagePath}')`;
container.appendChild(newImage);
// 添加文字元素
if (textContent) {
const textElement = document.createElement('div');
textElement.className = 'arrow-text';
textElement.textContent = textContent;
container.dataset.text = textContent;
container.appendChild(textElement);
}
// 设置位置 - 使用中心点作为参照
const arrowWidth = 155;
const arrowHeight = 173;
const centerOffsetX = arrowWidth / 2;
const centerOffsetY = arrowHeight / 2;
if (position) {
// 修复:直接使用传递的左上角位置,不再减去偏移量
container.style.left = parseFloat(position.left) + 'px';
container.style.top = parseFloat(position.top) + 'px';
} else {
// 随机位置也使用中心点作为参照
const x = Math.random() * (backgroundContainer.offsetWidth - arrowWidth) + centerOffsetX;
const y = Math.random() * (backgroundContainer.offsetHeight - arrowHeight) + centerOffsetY;
container.style.left = (x - centerOffsetX) + 'px';
container.style.top = (y - centerOffsetY) + 'px';
}
// 双击编辑文字
container.addEventListener('dblclick', (e) => {
e.stopPropagation();
const currentText = container.dataset.text || '';
const textContent = prompt('请输入文字:', currentText);
if (textContent !== null) {
if (textContent.trim() === '') {
container.querySelector('.arrow-text')?.remove();
delete container.dataset.text;
} else {
let textElement = container.querySelector('.arrow-text');
if (!textElement) {
textElement = document.createElement('div');
textElement.className = 'arrow-text';
container.appendChild(textElement);
}
textElement.textContent = textContent;
container.dataset.text = textContent;
saveAllElements(); // 文字编辑后自动保存
}
}
});
// 右键菜单支持
container.addEventListener('contextmenu', function(e) {
e.preventDefault();
selectedArrow = this;
contextMenu.style.left = `${e.clientX}px`;
contextMenu.style.top = `${e.clientY}px`;
contextMenu.style.display = 'block';
// 显示当前选中的匹配角对号
const currentPoint = selectedArrow.dataset.referencePoint || 'top-left';
document.querySelectorAll('.menu-item').forEach(item => {
const checkMark = item.querySelector('.check-mark');
if (!checkMark) return; // Add null check
if (item.dataset.point === currentPoint) {
checkMark.textContent = '✓';
} else {
checkMark.textContent = '';
}
});
// 添加偏移量保存逻辑
const stopDistanceInput = document.getElementById('stop-distance');
// 设置输入框当前值
stopDistanceInput.value = selectedArrow.dataset.stopDistance || 20;
// 点击空白处保存偏移量
contextMenu.addEventListener('click', function(e) {
e.stopPropagation(); // 阻止事件冒泡到document
});
// 修改输入框事件处理,添加失去焦点时保存
// Single declaration at the top
//const stopDistanceInput = document.getElementById('stop-distance');
// Later in your code (no redeclaration)
stopDistanceInput.addEventListener('blur', function() {
if (selectedArrow) {
selectedArrow.dataset.stopDistance = this.value;
console.log(`[设置] 停止半径: ${this.value}px`);
saveAllElements(); // 立即保存
}
});
// 修改原有点击保存逻辑,仅在点击菜单外部时触发
function saveStopDistance(e) {
if (!contextMenu.contains(e.target)) {
if (selectedArrow) {
selectedArrow.dataset.stopDistance = stopDistanceInput.value;
console.log(`[设置] 停止半径: ${stopDistanceInput.value}px`);
}
document.removeEventListener('click', saveStopDistance);
contextMenu.style.display = 'none';
}
}
});
// 拖拽功能
makeElementDraggable(container);
backgroundContainer.appendChild(container);
saveAllElements(); // 添加后自动保存
return container;
}
// 拖拽实现
function makeElementDraggable(element) {
let isDragging = false, offsetX, offsetY;
element.addEventListener('mousedown', (e) => {
if (e.button === 2) return; // 忽略右键
isDragging = true;
const rect = element.getBoundingClientRect();
offsetX = e.clientX - rect.left;
offsetY = e.clientY - rect.top;
element.style.zIndex = '10';
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
const x = e.clientX - offsetX;
const y = e.clientY - offsetY;
element.style.left = `${x}px`;
element.style.top = `${y}px`;
// 实时保存并控制台输出
saveAllElements();
console.log(`箭头移动并保存: 位置(${x}, ${y})`);
});
document.addEventListener('mouseup', () => {
if (isDragging) {
isDragging = false;
element.style.zIndex = '2';
saveAllElements(); // 拖拽结束后自动保存
}
});
}
// 保存/加载功能
function saveAllElements() {
const elements = [];
const arrowWidth = 155;
const arrowHeight = 173;
const centerOffsetX = arrowWidth / 2;
const centerOffsetY = arrowHeight / 2;
document.querySelectorAll('.arrow-container').forEach(arrow => {
// 保存中心点坐标而非左上角坐标
const left = parseFloat(arrow.style.left) || 0;
const top = parseFloat(arrow.style.top) || 0;
elements.push({
type: 'arrow',
imagePath: arrow.dataset.imagePath,
left: left + centerOffsetX,
top: top + centerOffsetY,
text: arrow.dataset.text || '',
referencePoint: arrow.dataset.referencePoint || 'top-left', // 默认左上角
stopDistance: arrow.dataset.stopDistance || 20 // 添加此行保存偏移量
});
});
// 保存中心点
if (centerDot) {
elements.push({
type: 'center',
left: centerDot.style.left,
top: centerDot.style.top
});
}
localStorage.setItem('savedElements', JSON.stringify(elements));
// 添加详细保存信息
const centerElement = elements.find(el => el.type === 'center');
if (centerElement) {
console.log(`[${new Date().toLocaleTimeString()}] 中心点已保存:`, centerElement);
} else {
console.log(`[${new Date().toLocaleTimeString()}] 未检测到中心点,未保存`);
}
console.log(`[${new Date().toLocaleTimeString()}] 自动保存完成: 共${elements.length}个元素`);
console.log('保存内容:', elements);
}
function loadAllElements() {
const savedElements = JSON.parse(localStorage.getItem('savedElements') || '[]');
const arrowWidth = 155;
const arrowHeight = 173;
const centerOffsetX = arrowWidth / 2;
const centerOffsetY = arrowHeight / 2;
savedElements.forEach(item => {
if (item.type === 'arrow') {
// 加载时使用中心点坐标减去偏移量得到左上角位置
addImageToContainer(item.imagePath, {
left: item.left - centerOffsetX,
top: item.top - centerOffsetY
}, item.text);
// 添加停止距离加载
const arrows = backgroundContainer.querySelectorAll('.arrow-container');
const lastArrow = arrows[arrows.length - 1];
lastArrow.dataset.stopDistance = item.stopDistance || 20;
} else if (item.type === 'center') {
// 创建中心点
centerDot = document.createElement('div');
centerDot.className = 'center-dot';
centerDot.style.left = item.left;
centerDot.style.top = item.top;
backgroundContainer.appendChild(centerDot);
console.log('[加载] 已恢复中心点:', item);
centerDotExists = true; // 添加变量声明
saveAllElements(); // 添加此行
}
});
}
}); // DOMContentLoaded回调结束
// 确保这里没有残留的startAnimation函数定义
function animateArrow(arrow) {
// 获取自定义停止半径
const stopDistance = parseFloat(arrow.dataset.stopDistance) || 20;
const centerX = parseFloat(centerDot.style.left) || 0;
const centerY = parseFloat(centerDot.style.top) || 0;
// 获取箭头当前位置和参照点
let currentX = parseFloat(arrow.style.left);
let currentY = parseFloat(arrow.style.top);
const referencePoint = arrow.dataset.referencePoint || 'top-left';
// 根据参照点计算实际位置
if (referencePoint === 'top-right') currentX += 155;
if (referencePoint === 'bottom-left') currentY += 173;
if (referencePoint === 'bottom-right') { currentX += 155; currentY += 173; }
// 计算距离
const dx = centerX - currentX;
const dy = centerY - currentY;
const distance = Math.sqrt(dx * dx + dy * dy);
// 判断是否到达停止距离
if (distance > stopDistance) {
// 移动逻辑...
} else {
return true; // 已停止
}
}
</script>
</body>
</html>