Zlibrary图书推荐 zlib热门图书,AI大佬推荐

在线转图形

富文本 · 更新于 2026-11-15

注意逻辑区分清楚: ✅ 原图带透明(BMP/PNG 有 Alpha),导出 JPG:必须填充底色 ✅ 导出 PNG:PNG 本身支持透明,默认保留透明;如果你想要【把透明区域替换成指定颜色(白色 / 黑色 / 自定义)、不再透明】,这个需求可以做

下面这个新版增加:

  • 开启「替换透明」复选框
  • 拾色器选填充颜色(默认白色 #ffffff)
  • 只在原图有透明通道、并且勾选替换时才上色;不勾选则 PNG 保留透明
  • 画质滑块对 PNG 无效(浏览器 canvas 的 PNG 是无损)
  • 保留原有全部功能:拖拽、多格式互转、打包 ZIP
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>本地图片批量转换(支持透明替换)</title>
<style>
*{box-sizing:border-box;font-family:system-ui, -apple-system, BlinkMacSystemFont, sans-serif;margin:0;padding:0}
body{padding:24px;max-width:920px;margin:0 auto;background:#f6f7f9;color:#222}
h2{margin-bottom:20px;font-weight:600}
.card{background:#fff;border-radius:14px;padding:24px;box-shadow:0 2px 12px rgba(0,0,0,0.06)}
.drop-area{border:2px dashed #cbd5e1;border-radius:10px;padding:40px;text-align:center;transition:0.2s;margin-bottom:20px}
.drop-area.active{border-color:#2563eb;background:#eff6ff}
input[type=file]{display:none}
.ctrl-row{display:flex;flex-wrap:wrap;gap:16px;align-items:center;margin-bottom:16px}
.ctrl-item{display:flex;align-items:center;gap:8px}
label{font-size:14px;color:#444}
select,button,input[type=range],input[type=color]{padding:7px 10px;border-radius:6px;border:1px solid #d1d5db;font-size:14px}
button{background:#2563eb;color:white;border:none;cursor:pointer;transition:0.2s}
button:hover{background:#1d4ed8}
button.secondary{background:#f1f5f9;color:#222}
button.secondary:hover{background:#e2e8f0}
#log{margin-top:16px;padding:12px;background:#f8fafc;border-radius:8px;font-size:13px;min-height:120px;white-space:pre-wrap;overflow:auto;max-height:240px}
</style>
</head>
<body>
<div class="card">
  <h2>🖼️ 本地图片批量转换|透明区域自定义底色</h2>
  <div class="drop-area" id="dropBox">
    <p>拖拽图片到这里 / 点击选择文件</p>
    <input type="file" id="fileInput" multiple accept=".bmp,.png,.jpg,.jpeg,.webp">
  </div>

  <div class="ctrl-row">
    <div class="ctrl-item">
      <label>输出格式:</label>
      <select id="outFormat">
        <option value="image/jpeg">JPG</option>
        <option value="image/png">PNG</option>
        <option value="image/webp">WebP</option>
      </select>
    </div>
    <div class="ctrl-item">
      <label>画质:</label>
      <input type="range" id="quality" min="0.1" max="1" step="0.05" value="0.9">
      <span id="qText">0.9</span>
    </div>
    <div class="ctrl-item">
      <input type="checkbox" id="replaceAlpha">
      <label for="replaceAlpha">替换透明区域</label>
      <input type="color" id="alphaColor" value="#ffffff">
    </div>
    <div class="ctrl-item">
      <button id="convertBtn">开始转换</button>
      <button id="zipBtn" class="secondary">打包ZIP下载</button>
      <button id="clearBtn" class="secondary">清空</button>
    </div>
  </div>

  <div id="log"></div>
</div>

<script src="https://cdn.jsdelivr.net/npm/jszip@3.10.1/dist/jszip.min.js"></script>
<script>
const dropBox = document.getElementById('dropBox');
const fileInput = document.getElementById('fileInput');
const convertBtn = document.getElementById('convertBtn');
const zipBtn = document.getElementById('zipBtn');
const clearBtn = document.getElementById('clearBtn');
const logDom = document.getElementById('log');
const qualityInput = document.getElementById('quality');
const qText = document.getElementById('qText');
const outFormatSel = document.getElementById('outFormat');
const replaceAlphaCb = document.getElementById('replaceAlpha');
const alphaColorPicker = document.getElementById('alphaColor');

let fileList = [];
let resultList = [];

qualityInput.oninput = () => qText.innerText = qualityInput.value;

// 拖拽
dropBox.onclick = () => fileInput.click();
dropBox.ondragover = e => { e.preventDefault(); dropBox.classList.add('active'); };
dropBox.ondragleave = () => dropBox.classList.remove('active');
dropBox.ondrop = e => {
  e.preventDefault();
  dropBox.classList.remove('active');
  const fs = Array.from(e.dataTransfer.files).filter(f => {
    const ext = f.name.toLowerCase();
    return ext.endsWith('.bmp') || ext.endsWith('.png') || ext.endsWith('.jpg') || ext.endsWith('.jpeg') || ext.endsWith('.webp');
  });
  fileList = fs;
  logDom.innerText = `✅ 已选中 ${fs.length} 个文件`;
};
fileInput.onchange = () => {
  const fs = Array.from(fileInput.files).filter(f => {
    const ext = f.name.toLowerCase();
    return ext.endsWith('.bmp') || ext.endsWith('.png') || ext.endsWith('.jpg') || ext.endsWith('.jpeg') || ext.endsWith('.webp');
  });
  fileList = fs;
  logDom.innerText = `✅ 已选中 ${fs.length} 个文件`;
};

// hex 转 rgb
function hexToRgb(hex) {
  const r = parseInt(hex.slice(1,3),16);
  const g = parseInt(hex.slice(3,5),16);
  const b = parseInt(hex.slice(5,7),16);
  return {r,g,b};
}

function getExtByMime(mime) {
  if (mime === 'image/jpeg') return 'jpg';
  if (mime === 'image/png') return 'png';
  if (mime === 'image/webp') return 'webp';
  return 'jpg';
}

async function convertImage(file, targetMime, quality, fillAlpha, fillColorHex) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = e => {
      const img = new Image();
      img.onload = () => {
        const canvas = document.createElement('canvas');
        canvas.width = img.width;
        canvas.height = img.height;
        const ctx = canvas.getContext('2d');

        // 如果勾选替换透明,先铺满底色
        if(fillAlpha){
          const c = hexToRgb(fillColorHex);
          ctx.fillStyle = `rgb(${c.r},${c.g},${c.b})`;
          ctx.fillRect(0,0,canvas.width,canvas.height);
        }
        ctx.drawImage(img, 0, 0);

        canvas.toBlob(blob => {
          if (!blob) return reject('转码失败');
          const baseName = file.name.replace(/\.(bmp|png|jpg|jpeg|webp)$/i, '');
          const newName = baseName + '.' + getExtByMime(targetMime);
          resolve({ name: newName, blob });
        }, targetMime, quality);
      };
      img.onerror = reject;
      img.src = e.target.result;
    };
    reader.readAsDataURL(file);
  });
}

convertBtn.onclick = async () => {
  if (!fileList.length) { alert('请先选择图片!'); return; }
  const targetMime = outFormatSel.value;
  const q = Number(qualityInput.value);
  const fillAlpha = replaceAlphaCb.checked;
  const fillColor = alphaColorPicker.value;
  resultList = [];
  logDom.innerText = '⏳ 转换中...\n';
  for (const f of fileList) {
    try {
      const res = await convertImage(f, targetMime, q, fillAlpha, fillColor);
      resultList.push(res);
      logDom.innerText += `✅ ${f.name} → ${res.name}\n`;
    } catch (err) {
      logDom.innerText += `❌ ${f.name} 失败:${err.message}\n`;
    }
  }
  logDom.innerText += `🎉 转换完成,共 ${resultList.length} 个成功`;
};

zipBtn.onclick = async () => {
  if (!resultList.length) { alert('请先执行转换!'); return; }
  const zip = new JSZip();
  for (const item of resultList) {
    zip.file(item.name, item.blob);
  }
  const zipBlob = await zip.generateAsync({ type: 'blob' });
  const a = document.createElement('a');
  a.href = URL.createObjectURL(zipBlob);
  a.download = '图片转换结果.zip';
  a.click();
  URL.revokeObjectURL(a.href);
};

clearBtn.onclick = () => {
  fileList = [];
  resultList = [];
  fileInput.value = '';
  logDom.innerText = '';
};
</script>
</body>
</html>

使用说明

  1. 不勾选【替换透明区域】
    • PNG 输出:保留原图透明通道 ✅
    • JPG/WebP:本身不支持透明,canvas 底层会自动填充黑色(这是浏览器限制,JPG 没有 alpha)
  2. 勾选【替换透明区域】,右边拾色器选颜色
    • 不管输出是 JPG / PNG / WebP,透明区域都会被你选的颜色铺满,图片不再透明
    • 比如选白色 #ffffff,就是白底
  3. 拾色器可以选任意颜色,Mac Safari、Chrome 都正常

举个场景

  • 你有一张带透明的 PNG,想导出 PNG、但是透明地方改成白色:勾选 + 选白色,转换
  • 你想保留透明 PNG:不勾选,直接导出 PNG

如果你想要,我还可以再做一个:离线独立版(内置 jszip,断网也能用)