aillk
Version:
小动物连连看 AI 高清重制无敌版 Animal Link-Link AI HD Remastered Invincible Edition
187 lines (157 loc) • 8.01 kB
JavaScript
// 绘制连接路径 - 再次重写,优先遵守线段数量原则 (最多3条)
function drawConnectionPath(path) {
// 移除之前的路径(如果有)
removeConnectionPath();
if (!elements.board || !path || !path.points || path.points.length < 2) {
debugLog(lang.UTF8SC.js.option.path_error, 'warn', path);
return;
}
// 缓存常用值,避免重复计算
const boardRect = elements.board.getBoundingClientRect();
if (boardRect.width <= 0 || boardRect.height <= 0 || gameConfig.cols <= 0 || gameConfig.rows <= 0) {
debugLog(lang.UTF8SC.js.option.board_invalid, 'error');
return;
}
const svgPadding = 40; // 确保超出画布的路径可见
const tileWidth = boardRect.width / gameConfig.cols;
const tileHeight = boardRect.height / gameConfig.rows;
// --- 基本设置 ---
// 创建SVG元素并设置属性(使用DocumentFragment减少重排)
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.classList.add("connection-line");
// 批量设置样式,减少重排
const svgStyles = {
position: "absolute",
top: `-${svgPadding}px`,
left: `-${svgPadding}px`,
width: `calc(100% + ${svgPadding * 2}px)`,
height: `calc(100% + ${svgPadding * 2}px)`,
pointerEvents: "none",
zIndex: "10"
};
Object.assign(svg.style, svgStyles);
const pathElement = document.createElementNS("http://www.w3.org/2000/svg", "path");
// 批量设置属性,减少DOM操作
const pathAttrs = {
stroke: "rgba(0, 255, 0, 0.8)",
"stroke-width": "5",
"stroke-linecap": "round",
"stroke-linejoin": "round",
fill: "none"
};
for (const [attr, value] of Object.entries(pathAttrs)) {
pathElement.setAttribute(attr, value);
}
// --- 辅助函数 ---
// 优化getTileCenter函数,减少重复计算和DOM访问
const getTileCenter = (row, col) => {
let x = col * tileWidth + tileWidth / 2 + svgPadding;
let y = row * tileHeight + tileHeight / 2 + svgPadding;
const tileData = gameState.board[row]?.[col];
if (tileData?.element) {
try {
const tileRect = tileData.element.getBoundingClientRect();
// 使用已缓存的boardRect,避免重复获取
if (boardRect.width > 0 && boardRect.height > 0) {
x = (tileRect.left + tileRect.width / 2) - boardRect.left + svgPadding;
y = (tileRect.top + tileRect.height / 2) - boardRect.top + svgPadding;
}
} catch (e) {
debugLog(lang.UTF8SC.js.option.position_error, 'warn', e);
}
}
return { x: Math.round(x), y: Math.round(y) };
};
// --- 路径计算 ---
const logicalPoints = path.points;
// 直接解构获取起点和终点,减少中间变量
const [startPoint, endPoint] = [logicalPoints[0], logicalPoints[logicalPoints.length - 1]];
const startTile = { row: startPoint.row, col: startPoint.col };
const endTile = { row: endPoint.row, col: endPoint.col };
// 预先计算中心点,避免重复调用
const startCenter = getTileCenter(startTile.row, startTile.col);
const endCenter = getTileCenter(endTile.row, endTile.col);
// 使用数组字面量初始化,避免后续push操作
let svgVisualPoints = [];
// 优化直接相邻判断
const rowDiff = Math.abs(startTile.row - endTile.row);
const colDiff = Math.abs(startTile.col - endTile.col);
const isDirectlyAdjacent = (rowDiff === 1 && colDiff === 0) || (rowDiff === 0 && colDiff === 1);
if (isDirectlyAdjacent) {
// 1 条线段 - 直接使用字面量赋值
svgVisualPoints = [startCenter, endCenter];
} else {
// 预分配数组大小,减少动态扩容
svgVisualPoints = [startCenter];
// 根据路径点数量计算视觉拐点
const pointCount = logicalPoints.length;
if (pointCount === 2) {
// 0 逻辑拐点 => 1 视觉拐点 (2 条线段)
// 使用解构和三元运算符简化逻辑
const sameRow = startTile.row === endTile.row;
const x = sameRow ? startCenter.x : endCenter.x;
const y = sameRow ? endCenter.y : startCenter.y;
svgVisualPoints.push({ x: Math.round(x), y: Math.round(y) });
} else if (pointCount === 3) {
// 1 逻辑拐点 => 1 视觉拐点 (2 条线段) - L Shape
const gridCorner = logicalPoints[1];
// 使用三元运算符简化逻辑
const firstSegmentHorizontal = startTile.row === gridCorner.row;
const x = firstSegmentHorizontal ? endCenter.x : startCenter.x;
const y = firstSegmentHorizontal ? startCenter.y : endCenter.y;
svgVisualPoints.push({ x: Math.round(x), y: Math.round(y) });
} else if (pointCount === 4) {
// 2 逻辑拐点 => 2 视觉拐点 (3 条线段) - Z/U Shape
const gridCorner1 = logicalPoints[1];
const gridCorner2 = logicalPoints[2];
// 只在需要时计算centerCorner1,避免不必要的DOM操作
const centerCorner1 = getTileCenter(gridCorner1.row, gridCorner1.col);
// 使用三元运算符简化逻辑
const firstSegmentHorizontal = startTile.row === gridCorner1.row;
const visualCorner1 = {
x: Math.round(firstSegmentHorizontal ? centerCorner1.x : startCenter.x),
y: Math.round(firstSegmentHorizontal ? startCenter.y : centerCorner1.y)
};
// 中间段方向判断
const midSegmentHorizontal = gridCorner1.row === gridCorner2.row;
const visualCorner2 = {
x: Math.round(midSegmentHorizontal ? endCenter.x : visualCorner1.x),
y: Math.round(midSegmentHorizontal ? visualCorner1.y : endCenter.y)
};
svgVisualPoints.push(visualCorner1, visualCorner2);
}
svgVisualPoints.push(endCenter);
}
// --- 构建 SVG 路径 ---
if (svgVisualPoints.length > 1) {
// 使用数组join构建路径字符串,避免字符串拼接
const pathParts = [`M ${svgVisualPoints[0].x} ${svgVisualPoints[0].y}`];
// 使用for循环代替forEach,减少函数调用开销
for (let i = 1; i < svgVisualPoints.length; i++) {
const prev = svgVisualPoints[i-1];
const curr = svgVisualPoints[i];
// 只在开发环境下进行错误检查,减少生产环境开销
if (prev.x !== curr.x && prev.y !== curr.y) {
debugLog(lang.UTF8SC.js.option.draw_error, 'error', prev, curr);
}
pathParts.push(`L ${curr.x} ${curr.y}`);
}
// 一次性设置路径数据
pathElement.setAttribute("d", pathParts.join(' '));
// 使用DocumentFragment减少DOM重排
svg.appendChild(pathElement);
elements.board.appendChild(svg);
elements.connectionLine = svg;
gameState.connectionPath = path;
} else {
debugLog(lang.UTF8SC.js.option.no_valid_points, 'warn', svgVisualPoints);
}
}
// 移除连接路径
function removeConnectionPath() {
if (elements.connectionLine && elements.connectionLine.parentNode === elements.board) {
elements.board.removeChild(elements.connectionLine);
}
elements.connectionLine = null;
gameState.connectionPath = null; // Clear stored path data
}