以下是简化后的版本,保留了正方形画布和科技感网络线,代码结构清晰,视觉风格干净利落。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>简洁网格画布 | Canvas 网络线</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
min-height: 100vh;
background: linear-gradient(145deg, #1a2a3a 0%, #0a1118 100%);
display: flex;
justify-content: center;
align-items: center;
font-family: system-ui, 'Segoe UI', monospace;
}
/* 画布容器微光边框,简单提升质感 */
.canvas-wrapper {
border-radius: 24px;
background: #0a1018;
box-shadow: 0 20px 35px -12px rgba(0,0,0,0.5), 0 0 0 1px rgba(0, 255, 255, 0.2) inset;
padding: 6px;
}
canvas {
display: block;
width: 480px;
height: 480px;
border-radius: 20px;
cursor: crosshair;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
/* 简单文字标注,可选,不影响核心网格视觉 */
.caption {
text-align: center;
margin-top: 18px;
font-size: 13px;
letter-spacing: 1px;
color: #9bbcdb;
background: rgba(10, 20, 30, 0.6);
backdrop-filter: blur(4px);
padding: 6px 16px;
border-radius: 40px;
display: inline-block;
width: auto;
font-family: monospace;
}
@media (max-width: 560px) {
canvas {
width: 320px;
height: 320px;
}
.canvas-wrapper {
padding: 4px;
}
.caption {
font-size: 10px;
}
}
</style>
</head>
<body>
<div style="display: flex; flex-direction: column; align-items: center;">
<div class="canvas-wrapper">
<canvas id="gridCanvas" width="480" height="480"></canvas>
</div>
<div class="caption">
✦ 网格线 | 步长 40px ✦
</div>
</div>
<script>
(function() {
const canvas = document.getElementById('gridCanvas');
const ctx = canvas.getContext('2d');
// 正方形边长 (像素)
const SIZE = 480;
// 网格步长 (40px 完全整除 480 → 12x12 网格线,整齐)
const STEP = 40;
// 1. 绘制深邃渐变背景 (简单但好看)
function drawBackground() {
const grad = ctx.createLinearGradient(0, 0, SIZE, SIZE);
grad.addColorStop(0, '#0c1824');
grad.addColorStop(0.6, '#07111c');
grad.addColorStop(1, '#03070e');
ctx.fillStyle = grad;
ctx.fillRect(0, 0, SIZE, SIZE);
// 可选:添加极简微光点 (几个静默星点提升质感,不复杂)
ctx.fillStyle = 'rgba(180, 220, 255, 0.4)';
for (let i = 0; i < 80; i++) {
if (i % 2 === 0) continue; // 只画约40个点,保持简洁
const x = (i * 131) % SIZE;
const y = (i * 253) % SIZE;
ctx.beginPath();
ctx.arc(x, y, 1.2, 0, Math.PI * 2);
ctx.fill();
}
}
// 2. 绘制主要网格线 (锐利、科技感)
function drawGrid() {
ctx.save();
// 线条光晕效果 (轻微发光)
ctx.shadowBlur = 2.5;
ctx.shadowColor = '#0af';
ctx.lineWidth = 1.2;
ctx.strokeStyle = '#3ccbff';
ctx.globalAlpha = 0.85;
// 绘制所有竖线
for (let x = 0; x <= SIZE; x += STEP) {
ctx.beginPath();
// 偏移0.5让线条更锐利 (像素对齐)
ctx.moveTo(x + 0.5, 0);
ctx.lineTo(x + 0.5, SIZE);
ctx.stroke();
}
// 绘制所有横线
for (let y = 0; y <= SIZE; y += STEP) {
ctx.beginPath();
ctx.moveTo(0, y + 0.5);
ctx.lineTo(SIZE, y + 0.5);
ctx.stroke();
}
// 加强中心轴线 (让视觉更平衡)
ctx.lineWidth = 2;
ctx.strokeStyle = '#0ff';
ctx.shadowBlur = 5;
const center = SIZE / 2;
ctx.beginPath();
ctx.moveTo(center + 0.5, 0);
ctx.lineTo(center + 0.5, SIZE);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, center + 0.5);
ctx.lineTo(SIZE, center + 0.5);
ctx.stroke();
ctx.restore();
}
// 3. 绘制网格交叉点小圆球 (强化网络节点的感觉,简洁精致)
function drawNodes() {
ctx.save();
ctx.shadowBlur = 3;
ctx.shadowColor = '#0cf';
for (let x = 0; x <= SIZE; x += STEP) {
for (let y = 0; y <= SIZE; y += STEP) {
ctx.beginPath();
ctx.arc(x, y, 2.2, 0, Math.PI * 2);
ctx.fillStyle = '#40d0ff';
ctx.fill();
// 内层高光
ctx.beginPath();
ctx.arc(x - 0.8, y - 0.8, 0.8, 0, Math.PI * 2);
ctx.fillStyle = '#ffffff';
ctx.fill();
}
}
// 中心节点加大一点强调
ctx.beginPath();
ctx.arc(center, center, 4, 0, Math.PI * 2);
ctx.fillStyle = '#0cf';
ctx.fill();
ctx.beginPath();
ctx.arc(center - 1, center - 1, 1.5, 0, Math.PI * 2);
ctx.fillStyle = 'white';
ctx.fill();
ctx.restore();
}
// 4. 添加简单的内发光边框 (让画布边界更精致)
function drawBorderGlow() {
ctx.save();
ctx.shadowBlur = 0;
ctx.beginPath();
ctx.strokeStyle = 'rgba(0, 200, 255, 0.5)';
ctx.lineWidth = 1.5;
ctx.strokeRect(2, 2, SIZE - 4, SIZE - 4);
ctx.beginPath();
ctx.strokeStyle = 'rgba(100, 180, 255, 0.25)';
ctx.lineWidth = 0.8;
ctx.strokeRect(4, 4, SIZE - 8, SIZE - 8);
ctx.restore();
}
// 主渲染 (所有绘制步骤)
function render() {
drawBackground();
drawGrid();
drawNodes();
drawBorderGlow();
}
// 确保canvas尺寸与CSS一致 (像素比完美)
canvas.width = SIZE;
canvas.height = SIZE;
render();
// 可选: 如果窗口大小改变导致显示问题, 但canvas固定尺寸无需重绘,但保留以防万一
window.addEventListener('resize', () => render());
})();
</script>
</body>
</html>
以下是您所需的一个使用Canvas技术绘制的、带有科技感网格线的正方形画布的HTML代码。它创建了一个带有渐变背景、发光网格线和交互提示的视觉元素。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>科技网格画布 | Canvas 正方形视觉艺术</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
min-height: 100vh;
background: radial-gradient(circle at 20% 30%, #0a0f1e, #03060c);
display: flex;
justify-content: center;
align-items: center;
font-family: 'Segoe UI', 'Poppins', 'Orbitron', monospace;
padding: 20px;
}
/* 画布容器,美化外框效果 */
.canvas-container {
position: relative;
border-radius: 32px;
background: rgba(0, 10, 20, 0.4);
backdrop-filter: blur(2px);
box-shadow: 0 20px 35px -15px rgba(0, 0, 0, 0.6),
0 0 0 1px rgba(0, 255, 255, 0.15) inset,
0 0 20px rgba(0, 255, 255, 0.2);
transition: all 0.3s ease;
}
.canvas-container:hover {
box-shadow: 0 25px 40px -12px rgba(0, 0, 0, 0.8),
0 0 0 1.5px rgba(0, 255, 255, 0.4) inset,
0 0 28px rgba(0, 210, 255, 0.4);
}
canvas {
display: block;
width: 500px;
height: 500px;
border-radius: 28px;
cursor: crosshair;
transition: filter 0.2s;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
/* 信息面板 - 显示鼠标坐标网格信息,提升科技感 */
.info-panel {
margin-top: 24px;
text-align: center;
background: rgba(12, 20, 28, 0.75);
backdrop-filter: blur(8px);
border-radius: 60px;
padding: 10px 24px;
border: 1px solid rgba(0, 255, 255, 0.3);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
font-size: 14px;
font-weight: 500;
letter-spacing: 1px;
color: #bbf0ff;
transition: all 0.2s;
}
.info-panel span {
color: #0ff;
font-weight: bold;
text-shadow: 0 0 4px #0ff;
font-family: monospace;
font-size: 16px;
}
.info-title {
display: inline-block;
margin-right: 16px;
border-right: 1px solid rgba(0, 255, 255, 0.5);
padding-right: 16px;
font-family: 'Orbitron', monospace;
font-size: 13px;
letter-spacing: 1.5px;
}
.coordinates {
font-family: 'Fira Code', monospace;
background: #05161f;
padding: 4px 12px;
border-radius: 32px;
display: inline-block;
box-shadow: inset 0 0 4px rgba(0,0,0,0.5), 0 0 6px rgba(0,255,255,0.2);
}
/* 底部装饰网格标识 */
.badge {
position: absolute;
bottom: -30px;
right: 12px;
font-size: 11px;
opacity: 0.5;
color: #6cf;
font-family: monospace;
pointer-events: none;
}
@media (max-width: 600px) {
canvas {
width: 320px;
height: 320px;
}
.info-panel {
font-size: 11px;
padding: 6px 16px;
}
.info-title {
font-size: 10px;
}
.canvas-container {
border-radius: 24px;
}
canvas {
border-radius: 22px;
}
}
/* 简单动画提示光晕 */
@keyframes softPulse {
0% { text-shadow: 0 0 2px #0ff; }
100% { text-shadow: 0 0 6px #0ff; }
}
.info-panel span {
animation: softPulse 1.2s infinite alternate;
}
</style>
</head>
<body>
<div style="display: flex; flex-direction: column; align-items: center;">
<div class="canvas-container">
<canvas id="techCanvas" width="500" height="500" style="width:500px; height:500px;"></canvas>
<div class="badge">✧ GRID NETWORK v1.0 ✧</div>
</div>
<div class="info-panel">
<span class="info-title">◈ 网格定位 ◈</span>
<span>鼠标位置 → 网格单元: </span>
<span id="gridCoordDisplay">(--- , ---)</span>
<span style="margin-left:10px;">| 步长 50px | 11 x 11 节点</span>
</div>
</div>
<script>
(function() {
// 获取canvas元素
const canvas = document.getElementById('techCanvas');
const ctx = canvas.getContext('2d');
// 定义画布尺寸 (正方形)
const SIZE = 500;
// 网格步长 (50px 完美整除,边界完整)
const STEP = 50;
// 网格线数量: 边界0到500, 线条数 = (SIZE / STEP) + 1 = 11
const GRID_LINES = SIZE / STEP; // 10个间隔, 11条线
// 让画布实际像素与CSS匹配 (已经设置width/height=500),无需额外处理,但确保持清晰
// 抗锯齿优化对于线条偏移0.5即可
// ---------- 绚丽背景绘制: 径向渐变 + 星空微粒 ----------
function drawBackground() {
// 深空科技感径向渐变 (中心亮蓝紫渐变到深邃黑蓝)
const grad = ctx.createRadialGradient(SIZE*0.4, SIZE*0.3, 30, SIZE*0.5, SIZE*0.5, SIZE*0.8);
grad.addColorStop(0, '#0a1a2f');
grad.addColorStop(0.4, '#07121f');
grad.addColorStop(0.7, '#030b14');
grad.addColorStop(1, '#010408');
ctx.fillStyle = grad;
ctx.fillRect(0, 0, SIZE, SIZE);
// 添加动态星尘效果 (随机亮点,增加视觉深度)
// 为了丰富星空,生成固定随机点 (但是为了美感,不同刷新位置略有差异但每次都不同也无伤大雅,不过我们做稳定的伪随机)
// 使用确定性的随机种子保证每次刷新效果一致,但为了自然感可不追求严格一致性,但都仅在初始化执行一次完美。
const starCount = 360;
for (let i = 0; i < starCount; i++) {
// 避免过多重叠在中心过度区域,采用随机分布
const x = Math.random() * SIZE;
const y = Math.random() * SIZE;
const radius = Math.random() * 1.8 + 0.5;
// 星星透明度随机的亮白/淡蓝
const alpha = Math.random() * 0.5 + 0.2;
const colorChoice = Math.random() > 0.7 ? 'rgba(120, 200, 255, ' + alpha + ')' : 'rgba(255, 240, 200, ' + alpha + ')';
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fillStyle = colorChoice;
ctx.fill();
// 偶尔添加微光晕
if (radius > 1.2 && Math.random() > 0.85) {
ctx.shadowBlur = 2;
ctx.shadowColor = '#7df9ff';
ctx.fill();
ctx.shadowBlur = 0; // 重置避免影响过多
}
}
// 额外增加淡淡星云旋臂效果: 极淡的椭圆光晕 (科技氛围)
ctx.save();
ctx.globalCompositeOperation = 'lighter';
ctx.beginPath();
ctx.ellipse(SIZE*0.7, SIZE*0.2, 100, 60, -0.5, 0, Math.PI*2);
ctx.fillStyle = 'rgba(20, 80, 130, 0.08)';
ctx.fill();
ctx.beginPath();
ctx.ellipse(SIZE*0.2, SIZE*0.8, 130, 90, 0.8, 0, Math.PI*2);
ctx.fillStyle = 'rgba(30, 100, 150, 0.07)';
ctx.fill();
ctx.restore();
}
// ---------- 绘制科技感网格线 (发光效果 + 精确偏移0.5确保清晰) ----------
function drawGridLines() {
ctx.save();
// 开启阴影光晕特效 (让网格线科技感十足)
ctx.shadowBlur = 3.5;
ctx.shadowColor = '#0af';
ctx.lineWidth = 1.2;
ctx.strokeStyle = 'rgba(0, 210, 255, 0.75)';
// 绘制竖线 (x = i) , i 从 0 到 SIZE 步长 STEP
for (let x = 0; x <= SIZE; x += STEP) {
ctx.beginPath();
// 偏移0.5使线条在像素边界清晰锐利
const xPos = x + 0.5;
ctx.moveTo(xPos, 0);
ctx.lineTo(xPos, SIZE);
ctx.stroke();
}
// 绘制横线 (y = i)
for (let y = 0; y <= SIZE; y += STEP) {
ctx.beginPath();
const yPos = y + 0.5;
ctx.moveTo(0, yPos);
ctx.lineTo(SIZE, yPos);
ctx.stroke();
}
// 额外绘制更亮的主轴线 (中轴线交叉高亮) 提升美学: 绘制 x = 250, y = 250 的中心线加粗增亮效果
ctx.lineWidth = 2;
ctx.strokeStyle = 'rgba(80, 240, 255, 0.9)';
ctx.shadowBlur = 6;
ctx.shadowColor = '#0cf';
// 中间竖线 (250)
ctx.beginPath();
const center = SIZE / 2;
ctx.moveTo(center + 0.5, 0);
ctx.lineTo(center + 0.5, SIZE);
ctx.stroke();
// 中间横线
ctx.beginPath();
ctx.moveTo(0, center + 0.5);
ctx.lineTo(SIZE, center + 0.5);
ctx.stroke();
// 边界额外微光轮廓 (可选)
ctx.lineWidth = 1.2;
ctx.strokeStyle = 'rgba(0, 200, 230, 0.5)';
ctx.shadowBlur = 2;
ctx.strokeRect(0.5, 0.5, SIZE-1, SIZE-1);
ctx.restore();
}
// ---------- 绘制网格交点光点 (科技感节点) 让连接更生动 ----------
function drawIntersectionPoints() {
ctx.save();
ctx.shadowBlur = 4;
ctx.shadowColor = '#0cf';
// 遍历所有交点 (0,0) 到 (500,500) 步长 STEP
for (let x = 0; x <= SIZE; x += STEP) {
for (let y = 0; y <= SIZE; y += STEP) {
// 根据位置不同略微改变色调: 边缘冷色,中心暖一点
let pointColor;
const distToCenter = Math.hypot(x - SIZE/2, y - SIZE/2);
const ratio = distToCenter / (SIZE/2);
if (ratio < 0.6) {
pointColor = `rgba(0, 220, 255, ${0.85 - ratio*0.2})`;
} else {
pointColor = `rgba(100, 180, 255, 0.65)`;
}
ctx.beginPath();
ctx.arc(x, y, 2.2, 0, Math.PI * 2);
ctx.fillStyle = pointColor;
ctx.fill();
// 内层小高光
ctx.beginPath();
ctx.arc(x, y, 1.0, 0, Math.PI * 2);
ctx.fillStyle = `rgba(255, 255, 255, 0.9)`;
ctx.fill();
}
}
// 强化中心交叉点: 增加特殊光晕效果
const cx = SIZE/2;
const cy = SIZE/2;
ctx.beginPath();
ctx.arc(cx, cy, 4.5, 0, Math.PI*2);
ctx.fillStyle = 'rgba(0, 230, 255, 0.7)';
ctx.shadowBlur = 8;
ctx.fill();
ctx.beginPath();
ctx.arc(cx, cy, 2.2, 0, Math.PI*2);
ctx.fillStyle = '#ffffff';
ctx.fill();
// 四个辅助方向小光晕
for (let i = -1; i <= 1; i++) {
for (let j = -1; j <= 1; j++) {
if (i === 0 && j === 0) continue;
const xOff = i * STEP;
const yOff = j * STEP;
if (Math.abs(xOff) <= SIZE && Math.abs(yOff) <= SIZE) {
ctx.beginPath();
ctx.arc(cx + xOff, cy + yOff, 2.8, 0, Math.PI*2);
ctx.fillStyle = 'rgba(0, 200, 240, 0.65)';
ctx.fill();
}
}
}
ctx.restore();
}
// 可选添加微妙的动态流光粒子 (进阶美化: 简单的漂浮粒子, 增强"网络线"的感觉)
// 但为了避免性能消耗,在静态画布中添加一组"数据流"装饰点 --- 只绘制静态光粒,不做动画 (保持简洁但充满细节)
function drawDataParticles() {
ctx.save();
ctx.shadowBlur = 2;
ctx.shadowColor = '#6cf';
// 增加一些随机微粒漂浮感,在网格之间游弋 (固定位置,但散布在空隙)
const particles = [
[75, 125], [180, 340], [420, 80], [360, 450], [110, 390], [290, 190],
[460, 270], [40, 430], [32, 88], [480, 475], [230, 470], [310, 30],
[150, 270], [410, 330], [55, 210], [499, 310], [280, 410], [390, 160]
];
for (let p of particles) {
ctx.beginPath();
ctx.arc(p[0], p[1], 1.8, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(0, 210, 255, 0.6)';
ctx.fill();
ctx.beginPath();
ctx.arc(p[0]-0.7, p[1]-0.5, 0.6, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
ctx.fill();
}
// 随机再增加一些光晕线段? 为了更符合网络线科技感, 增加短线连接相邻节点的光晕 (可选)
ctx.restore();
}
// 绘制精致的边框修饰 + 角落科技图案 (提升正方形整体美感)
function drawCornerTech() {
ctx.save();
ctx.shadowBlur = 0;
ctx.lineWidth = 1.5;
ctx.strokeStyle = 'rgba(0, 210, 230, 0.7)';
// 绘制四个角落点缀 (小L形和点阵)
const cornerSize = 18;
const offset = 8;
// 左上角
ctx.beginPath();
ctx.moveTo(offset, cornerSize);
ctx.lineTo(offset, offset);
ctx.lineTo(cornerSize, offset);
ctx.stroke();
ctx.beginPath();
ctx.arc(offset + 3, offset + 3, 1.2, 0, Math.PI*2);
ctx.fillStyle = '#0ff';
ctx.fill();
// 右上角
ctx.beginPath();
ctx.moveTo(SIZE - offset, cornerSize);
ctx.lineTo(SIZE - offset, offset);
ctx.lineTo(SIZE - cornerSize, offset);
ctx.stroke();
ctx.beginPath();
ctx.arc(SIZE - offset - 3, offset + 3, 1.2, 0, Math.PI*2);
ctx.fill();
// 左下角
ctx.beginPath();
ctx.moveTo(offset, SIZE - cornerSize);
ctx.lineTo(offset, SIZE - offset);
ctx.lineTo(cornerSize, SIZE - offset);
ctx.stroke();
ctx.beginPath();
ctx.arc(offset + 3, SIZE - offset - 3, 1.2, 0, Math.PI*2);
ctx.fill();
// 右下角
ctx.beginPath();
ctx.moveTo(SIZE - offset, SIZE - cornerSize);
ctx.lineTo(SIZE - offset, SIZE - offset);
ctx.lineTo(SIZE - cornerSize, SIZE - offset);
ctx.stroke();
ctx.beginPath();
ctx.arc(SIZE - offset - 3, SIZE - offset - 3, 1.2, 0, Math.PI*2);
ctx.fill();
// 额外装饰:顶部和底部的微细光带
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = 'rgba(0, 210, 230, 0.4)';
for (let i = 0; i < 4; i++) {
const yOff = 12 + i * 8;
ctx.beginPath();
ctx.moveTo(20, yOff);
ctx.lineTo(50, yOff);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(SIZE - 20, yOff);
ctx.lineTo(SIZE - 50, yOff);
ctx.stroke();
// 底部
ctx.beginPath();
ctx.moveTo(20, SIZE - yOff);
ctx.lineTo(50, SIZE - yOff);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(SIZE - 20, SIZE - yOff);
ctx.lineTo(SIZE - 50, SIZE - yOff);
ctx.stroke();
}
ctx.restore();
}
// 主渲染函数: 合并所有图层 (背景、星尘、网格线、交点和装饰)
function renderCanvas() {
if (!ctx) return;
// 1.深邃背景及星尘
drawBackground();
// 2. 网格线 (含科技光晕效果)
drawGridLines();
// 3. 网格交点光点 (增强网络视觉)
drawIntersectionPoints();
// 4. 数据微粒点缀 (分布网格空隙之间增加科技数据感)
drawDataParticles();
// 5. 四角机械风格装饰边框细节
drawCornerTech();
// 额外增加淡淡的内发光边框,让正方形画布更精致
ctx.save();
ctx.shadowBlur = 0;
ctx.beginPath();
ctx.strokeStyle = 'rgba(100, 210, 255, 0.5)';
ctx.lineWidth = 1.2;
ctx.strokeRect(2, 2, SIZE - 4, SIZE - 4);
ctx.beginPath();
ctx.strokeStyle = 'rgba(0, 150, 200, 0.3)';
ctx.lineWidth = 0.8;
ctx.strokeRect(4, 4, SIZE - 8, SIZE - 8);
ctx.restore();
}
// 鼠标交互:显示鼠标所在网格信息 (提升体验, 展示网络线定位)
function setupCoordinateTracker() {
const coordSpan = document.getElementById('gridCoordDisplay');
if (!coordSpan) return;
function updateMousePosition(e) {
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width; // 因为canvas实际像素500,rect宽高可能因css变化但宽高500px严格相等,scale恒为1
const scaleY = canvas.height / rect.height;
// 确保相对canvas坐标 (px)
let mouseX = (e.clientX - rect.left) * scaleX;
let mouseY = (e.clientY - rect.top) * scaleY;
// 边界裁剪
mouseX = Math.min(Math.max(0, mouseX), SIZE);
mouseY = Math.min(Math.max(0, mouseY), SIZE);
// 计算当前所在网格行列 (步长 STEP = 50)
const col = Math.floor(mouseX / STEP);
const row = Math.floor(mouseY / STEP);
// 边界修正: 如果恰好落在网格线上且为边界最后一个,最大行列是10(因为0~10共11条线,单元格索引0~9)
let gridCol = col;
let gridRow = row;
if (gridCol >= GRID_LINES) gridCol = GRID_LINES - 1;
if (gridRow >= GRID_LINES) gridRow = GRID_LINES - 1;
if (gridCol < 0) gridCol = 0;
if (gridRow < 0) gridRow = 0;
const cellX = gridCol;
const cellY = gridRow;
// 显示 "行,列" 和 单元格范围信息
coordSpan.innerHTML = `(${cellX}, ${cellY}) | [${cellX*STEP}~${(cellX+1)*STEP}, ${cellY*STEP}~${(cellY+1)*STEP}]`;
// 额外视觉反馈: 改变鼠标样式已在CSS中体现(crosshair),可以不给canvas加额外重绘性能开销
}
canvas.addEventListener('mousemove', updateMousePosition);
canvas.addEventListener('mouseleave', () => {
coordSpan.innerHTML = '(--- , ---)';
});
}
// 初始化渲染, 并处理设备像素比(无需额外, 因为固定尺寸)
function init() {
renderCanvas();
setupCoordinateTracker();
}
// 可选:让窗口尺寸改变时保持质量 (由于canvas固定尺寸,无需重新渲染,但若用户缩放页面无影响)
// 为了完美主义,若用户调整字体缩放可能导致canvas变形?不会,canvas像素固定,重新渲染一次也没损失。
window.addEventListener('resize', () => {
// 重绘确保质量 (因canvas内容不变, resize不会清空, 但某些极端情况设备旋转? 无需额外, 但重绘也无妨)
// 但是为了稳妥, 监听resize重新渲染, 可能用户旋转手机屏幕虽然canvas宽高固定不变, 但父容器不影响canvas绘图.
// 重绘保证无闪烁
renderCanvas();
});
// 启动所有效果
init();
})();
</script>
</body>
</html>