
这是一个HTML代码上传图片后您可以圈出任意矩形区域松手就能把它变成像素方块。html!DOCTYPE htmlhtml langzh-CNheadmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1title圈选区域 → 像素方块/titlestyle*{box-sizing:border-box}body{margin:0;min-height:100vh;padding:20px;font-family:system-ui,-apple-system,PingFang SC,Microsoft YaHei,sans-serif;background:#14161a;color:#e8eaed;display:flex;flex-direction:column;align-items:center;gap:16px;}.panel{display:flex;flex-wrap:wrap;gap:10px 16px;align-items:center;justify-content:center;background:#1e2128;padding:12px 18px;border-radius:14px;box-shadow:0 6px 24px rgba(0,0,0,.35);}label{display:flex;align-items:center;gap:8px;font-size:14px;white-space:nowrap}input[typerange]{width:150px;accent-color:#4f9cff}#sizeVal{width:28px;text-align:right;color:#4f9cff;font-variant-numeric:tabular-nums}button,.file-btn{border:none;border-radius:9px;padding:9px 16px;font-size:14px;background:#4f9cff;color:#fff;cursor:pointer;transition:.18s;}button:hover:not(:disabled),.file-btn:hover{background:#3a86e8}button:disabled{opacity:.35;cursor:not-allowed}button.ghost{background:#2c313b}button.ghost:hover:not(:disabled){background:#3a414e}#file{display:none}#stage{width:min(1100px,100%);max-height:70vh;overflow:auto;border:2px dashed #333842;border-radius:16px;padding:14px;display:flex;}#stage.dragover{border-color:#4f9cff;background:rgba(79,156,255,.08)}.hint{color:#7c8493;font-size:14px;margin:auto;text-align:center;line-height:1.9}.canvas-wrap{position:relative;margin:auto;max-width:100%;line-height:0}.canvas-wrap canvas{display:block;width:100%;height:auto}#overlay{position:absolute;inset:0;width:100%;height:100%;cursor:crosshair;touch-action:none}/style/headbodydiv classpanellabel classfile-btn选择图片input typefile idfile acceptimage/*/labellabel方块大小input typerange idsize min2 max60 value12span idsizeVal12/span/labelbutton idundo classghost disabled撤销/buttonbutton idreset classghost disabled重置/buttonbutton iddownload disabled下载 PNG/button/divdiv idstagep classhint点击「选择图片」或把图片拖到这里 / CtrlV 粘贴br然后在图片上按住拖出一个矩形松手即变成像素方块可多次圈选/p/divscript/* ---------------- DOM ---------------- */const fileInput document.getElementById(file);const sizeInput document.getElementById(size);const sizeVal document.getElementById(sizeVal);const stage document.getElementById(stage);const undoBtn document.getElementById(undo);const resetBtn document.getElementById(reset);const downloadBtn document.getElementById(download);/* ---------------- 状态 ---------------- */let sourceImg null; // 原始图片let board, ctx; // 底层画布真正被修改的图let overlay, octx; // 上层画布只用来画选区虚线框let ops []; // 已应用的选区 [{x,y,w,h}...]let drawing false;let startPt null;let curRect null;/* ---------------- 加载图片 ---------------- */function loadFile(file){if (!file || !file.type.startsWith(image/)) return;const url URL.createObjectURL(file);const img new Image();img.onload () {URL.revokeObjectURL(url);sourceImg img;ops [];setup();rebuild();updateButtons();};img.onerror () alert(图片读取失败换一张试试);img.src url;}/* ---------------- 建画布 ---------------- */function setup(){stage.innerHTML ;const wrap document.createElement(div);wrap.className canvas-wrap;wrap.style.width sourceImg.naturalWidth px; // 原图宽度但不超过容器wrap.style.maxWidth 100%;board document.createElement(canvas);board.width sourceImg.naturalWidth;board.height sourceImg.naturalHeight;ctx board.getContext(2d);overlay document.createElement(canvas);overlay.width board.width;overlay.height board.height;overlay.id overlay;octx overlay.getContext(2d);wrap.appendChild(board);wrap.appendChild(overlay);stage.appendChild(wrap);bindPointer();}/* ---------------- 屏幕坐标 → 画布坐标 ---------------- */function getPos(e){const r board.getBoundingClientRect();return {x: (e.clientX - r.left) * (board.width / r.width),y: (e.clientY - r.top ) * (board.height / r.height)};}const clampX v Math.max(0, Math.min(board.width, v));const clampY v Math.max(0, Math.min(board.height, v));/* ---------------- 圈选交互 ---------------- */function bindPointer(){overlay.addEventListener(pointerdown, e {if (!sourceImg) return;e.preventDefault();overlay.setPointerCapture(e.pointerId);const p getPos(e);p.x clampX(p.x); p.y clampY(p.y);drawing true;startPt p;curRect { x0: p.x, y0: p.y, x1: p.x, y1: p.y };drawSelection(curRect);});overlay.addEventListener(pointermove, e {if (!drawing) return;const p getPos(e);curRect.x1 clampX(p.x);curRect.y1 clampY(p.y);drawSelection(curRect);});const finish e {if (!drawing || !curRect) return;drawing false;try { overlay.releasePointerCapture(e.pointerId); } catch(_) {}const r normalize(curRect);curRect null;octx.clearRect(0, 0, overlay.width, overlay.height);if (r.w 3 r.h 3){const op { x: r.x, y: r.y, w: r.w, h: r.h };ops.push(op);pixelateRegion(op); // 只处理这一块立刻生效updateButtons();}};overlay.addEventListener(pointerup, finish);overlay.addEventListener(pointercancel, finish);}function normalize(r){return {x: Math.min(r.x0, r.x1),y: Math.min(r.y0, r.y1),w: Math.abs(r.x1 - r.x0),h: Math.abs(r.y1 - r.y0)};}/* ---------------- 画选区虚线框 ---------------- */function drawSelection(rect){const r board.getBoundingClientRect();const scale r.width / board.width; // 屏幕像素 / 画布像素octx.clearRect(0, 0, overlay.width, overlay.height);if (!rect) return;const n normalize(rect);octx.fillStyle rgba(79,156,255,0.16);octx.fillRect(n.x, n.y, n.w, n.h);octx.lineWidth Math.max(1, 2 / scale);octx.strokeStyle #4f9cff;octx.setLineDash([8 / scale, 6 / scale]);octx.strokeRect(n.x, n.y, n.w, n.h);octx.setLineDash([]);}/* ---------------- 核心把指定矩形变成像素方块 ---------------- */function pixelateRegion(op){const block Number(sizeInput.value);const x Math.max(0, Math.round(op.x));const y Math.max(0, Math.round(op.y));const w Math.min(board.width - x, Math.round(op.w));const h Math.min(board.height - y, Math.round(op.h));if (w 1 || h 1) return;const cols Math.max(1, Math.round(w / block)); // 横向切成几块const rows Math.max(1, Math.round(h / block)); // 纵向切成几块if (cols w rows h) return; // 方块比区域还大没意义// ① 把这块区域抠出来const tmp document.createElement(canvas);tmp.width w; tmp.height h;const tctx tmp.getContext(2d);tctx.imageSmoothingEnabled false;tctx.drawImage(board, x, y, w, h, 0, 0, w, h);// ② 逐级折半缩小到 cols×rows每个像素 一个方块的平均色let cur tmp, cw w, ch h;while (cw cols * 2 ch rows * 2 cw 1 ch 1){const nw Math.max(1, Math.floor(cw / 2));const nh Math.max(1, Math.floor(ch / 2));const n document.createElement(canvas);n.width nw; n.height nh;const nc n.getContext(2d);nc.imageSmoothingEnabled true;nc.imageSmoothingQuality high;nc.drawImage(cur, 0, 0, cw, ch, 0, 0, nw, nh);cur n; cw nw; ch nh;}const small document.createElement(canvas);small.width cols; small.height rows;const sctx small.getContext(2d);sctx.imageSmoothingEnabled true;sctx.imageSmoothingQuality high;sctx.drawImage(cur, 0, 0, cw, ch, 0, 0, cols, rows);// ③ 关掉平滑硬边放大贴回原处 → 方块感ctx.imageSmoothingEnabled false;ctx.drawImage(small, 0, 0, cols, rows, x, y, w, h);ctx.imageSmoothingEnabled true;}/* ---------------- 整体重绘改方块大小时用 ---------------- */function rebuild(){if (!sourceImg || !ctx) return;ctx.imageSmoothingEnabled true;ctx.imageSmoothingQuality high;ctx.clearRect(0, 0, board.width, board.height);ctx.drawImage(sourceImg, 0, 0, board.width, board.height);for (const op of ops) pixelateRegion(op);}let rafId 0;function scheduleRebuild(){if (rafId) return;rafId requestAnimationFrame(() { rafId 0; rebuild(); });}/* ---------------- 按钮状态 ---------------- */function updateButtons(){const has ops.length 0;undoBtn.disabled !has;resetBtn.disabled !has;downloadBtn.disabled !sourceImg;}/* ---------------- 事件 ---------------- */fileInput.addEventListener(change, e loadFile(e.target.files[0]));sizeInput.addEventListener(input, () {sizeVal.textContent sizeInput.value;scheduleRebuild(); // 拖滑块 所有已圈区域实时重算});undoBtn.addEventListener(click, () {ops.pop();rebuild();updateButtons();});resetBtn.addEventListener(click, () {ops [];rebuild();updateButtons();});downloadBtn.addEventListener(click, () {if (!board) return;const a document.createElement(a);a.download pixel-region.png;a.href board.toDataURL(image/png);a.click();});/* 拖拽上传 */[dragenter,dragover].forEach(ev stage.addEventListener(ev, e { e.preventDefault(); stage.classList.add(dragover); }));[dragleave,drop].forEach(ev stage.addEventListener(ev, e { e.preventDefault(); stage.classList.remove(dragover); }));stage.addEventListener(drop, e loadFile(e.dataTransfer.files[0]));/* 粘贴上传 */document.addEventListener(paste, e {const item [...(e.clipboardData?.items || [])].find(i i.type.startsWith(image/));if (item) loadFile(item.getAsFile());});/script/body/html圈选像素化的操作与反馈上手很简单圈选区域后会立刻看到像素方块效果。· 上传与圈选点击「选择图片」、拖拽到虚线框或按 CtrlV 粘贴然后在图片上按住拖出矩形松手后选区立刻变成像素方块。可多次圈选每次只处理当前区域。· 方块大小调节拖动「方块大小」滑块所有已圈选的区域会实时重算像素块大小方便统一调整整体风格。· 撤销与重置圈选错了可以「撤销」最后一步「重置」则恢复原图并清除全部圈选记录。· 导出结果调整满意后点「下载 PNG」保存当前画布为图片。---优化建议 如果希望调整默认像素块大小可以修改 idsize 输入框的 value12同时记得把旁边的 sizeVal 初始文字也改成相同数值。文章仅供参考学习用。