不知名提交

This commit is contained in:
2025-12-13 20:53:50 +08:00
parent c147502b4d
commit 1221d6faf1
120 changed files with 11005 additions and 1092 deletions

View File

@@ -0,0 +1,327 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<title>图片转化 base64 编码</title>
<meta name="description" content="将图片快速转换为 Base64 / Data URL支持一键复制与清空适配手机竖屏。" />
<style>
:root{
--bg1:#dff7d6; /* 淡绿色 */
--bg2:#eef8c9; /* 淡黄绿色 */
--card:#ffffffcc;
--text:#0f2f1a;
--muted:#3d5f46;
--accent:#6ebf75;
--accent-2:#a6d98d;
--danger:#b55252;
--radius:18px;
--shadow:0 10px 24px rgba(0,0,0,.08), 0 2px 8px rgba(0,0,0,.04);
}
html,body{height:100%;}
body{
margin:0;
font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Apple Color Emoji", "Segoe UI Emoji";
color:var(--text);
background: linear-gradient(135deg,var(--bg1),var(--bg2));
-webkit-font-smoothing:antialiased;
-moz-osx-font-smoothing:grayscale;
}
.container{
max-width: 860px;
padding: 16px clamp(14px,4vw,28px) 28px;
margin: 0 auto;
display:flex;
flex-direction:column;
gap:14px;
}
header{
text-align:center;
padding-top: 8px;
padding-bottom: 6px;
}
h1{
margin:0 0 6px;
font-size: clamp(20px, 5.5vw, 32px);
letter-spacing: .5px;
font-weight: 800;
background: linear-gradient(90deg, #3a7f43, #79c26f);
-webkit-background-clip: text;
background-clip:text;
color: transparent;
}
.subtitle{
font-size: clamp(12px, 3.6vw, 14px);
color: color-mix(in oklab, var(--muted) 80%, white);
}
.card{
background: var(--card);
border: 1px solid rgba(99, 135, 102, .15);
backdrop-filter: blur(6px);
border-radius: var(--radius);
box-shadow: var(--shadow);
padding: 14px;
}
.uploader{
display:grid;
gap:12px;
}
.dropzone{
position:relative;
border:2px dashed rgba(59, 120, 74, .35);
border-radius: calc(var(--radius) - 6px);
padding: 18px;
background: linear-gradient(180deg, rgba(255,255,255,.85), rgba(255,255,255,.65));
transition: .2s ease;
cursor: pointer;
display:flex;
align-items:center;
gap:14px;
}
.dropzone:hover{ border-color: rgba(59,120,74,.6); }
.dropzone.dragover{ box-shadow: inset 0 0 0 3px rgba(110,191,117,.35); background: rgba(255,255,255,.9); }
.drop-icon{ width:40px; height:40px; flex: 0 0 40px; }
.dz-text{ display:flex; flex-direction:column; gap:4px; }
.dz-title{ font-weight:700; font-size: 15px; }
.dz-sub{ font-size:12px; color: color-mix(in oklab, var(--muted) 75%, white); }input[type="file"]{
position:absolute; inset:0; opacity:0; cursor:pointer; width:100%; height:100%;
}
.preview{
display:grid; grid-template-columns: 1fr; gap:10px; align-items:start;
}
.preview img{
width:100%; height:auto; max-height: 55vh; object-fit: contain;
border-radius: 12px;
background: #f8fff2;
border:1px solid rgba(59, 120, 74, .18);
}
.controls{
display:flex; gap:10px; flex-wrap:wrap; align-items:center; justify-content:space-between;
}
.left-controls, .right-controls{ display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
.btn{
appearance:none; border:0; border-radius: 999px;
padding: 10px 14px; font-weight: 700; letter-spacing:.2px;
background: linear-gradient(90deg, var(--accent), var(--accent-2));
color:#05370f; box-shadow: var(--shadow); cursor:pointer; transition:.15s ease; display:flex; gap:8px; align-items:center;
}
.btn:active{ transform: translateY(1px); }
.btn.secondary{ background: #ffffff; color:#2a5532; border:1px solid rgba(59,120,74,.2); }
.btn.danger{ background: #ffecec; color:#7a2222; border:1px solid rgba(181,82,82,.35); }
.format{
display:flex; gap:8px; align-items:center; background:#ffffff; border:1px solid rgba(59,120,74,.15);
padding:6px 10px; border-radius:999px; box-shadow: var(--shadow);
font-size: 13px;
}
.format label{ display:flex; gap:6px; align-items:center; padding:6px 8px; border-radius: 999px; cursor:pointer; }
.format input{ accent-color:#6ebf75; }
.info{
font-size:12px; color: color-mix(in oklab, var(--muted) 70%, white);
display:flex; gap:10px; flex-wrap:wrap; align-items:center; line-height:1.4;
}
.output card{
display:block;
}
textarea{
width:100%; min-height: 36vh; resize: vertical; padding:12px 12px; line-height:1.35; border-radius: 12px;
border:1px solid rgba(59,120,74,.2); background: #fbfff6; outline: none; box-shadow: inset 0 2px 4px rgba(0,0,0,.03);
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
font-size: 13px;
}
footer{ text-align:center; font-size:12px; color: color-mix(in oklab, var(--muted) 64%, white); padding: 10px 0 0; }
/* 小屏优化(手机竖屏) */
@media (max-width: 480px){
.controls{ gap:8px; }
.btn{ padding: 10px 12px; }
.format{ width:100%; justify-content:center; }
.left-controls{ width:100%; justify-content:center; }
.right-controls{ width:100%; justify-content:center; }
}
/* toast */
.toast{ position: fixed; z-index: 50; left: 50%; bottom: 24px; transform: translateX(-50%) translateY(16px);
background: #103e1b; color: #e9ffe9; padding: 10px 14px; border-radius: 999px; opacity:0; pointer-events:none;
transition: .25s ease; box-shadow: var(--shadow); font-size: 13px; }
.toast.show{ opacity:1; transform: translateX(-50%) translateY(0); }
</style>
</head>
<body>
<div class="container">
<header>
<h1>图片转化 base64 编码</h1>
<div class="subtitle">上传或拖拽图片,立即生成 Base64 / Data URL支持一键复制与清空。已针对手机竖屏优化。</div>
</header><section class="card uploader">
<div class="dropzone" id="dropZone" tabindex="0" aria-label="点击或拖拽图片到此处上传">
<svg class="drop-icon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<path d="M12 16v-8M8 8l4-4 4 4" stroke="#568f5b" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
<rect x="3" y="12" width="18" height="8" rx="3" stroke="#85c076" stroke-width="1.4" />
</svg>
<div class="dz-text">
<div class="dz-title">点击选择图片或直接拖拽到这里</div>
<div class="dz-sub">支持 PNG / JPG / GIF / WebP / SVG 等常见格式</div>
</div>
<input id="fileInput" type="file" accept="image/*" />
</div>
<div class="preview" id="previewWrap" hidden>
<img id="preview" alt="图片预览" />
<div class="info" id="info"></div>
<div class="controls">
<div class="left-controls format" role="radiogroup" aria-label="输出格式">
<label><input type="radio" name="format" value="dataurl" checked> Data URL含前缀</label>
<label><input type="radio" name="format" value="base64"> 仅 Base64不含前缀</label>
</div>
<div class="right-controls">
<button class="btn" id="copyBtn" type="button" title="复制到剪贴板">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M9 9h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2Z" stroke="#234b2d" stroke-width="1.6"/><path d="M7 15H6a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v1" stroke="#234b2d" stroke-width="1.6"/></svg>
复制
</button>
<button class="btn danger" id="clearBtn" type="button" title="清空当前内容">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M4 7h16M9 7V5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2M6 7l1.2 12.1A2 2 0 0 0 9.2 21h5.6a2 2 0 0 0 2-1.9L18 7" stroke="#8a3737" stroke-width="1.6" stroke-linecap="round"/></svg>
清空
</button>
</div>
</div>
<div class="output card">
<textarea id="output" placeholder="这里将显示转换后的内容……" readonly></textarea>
</div>
</div>
</section>
<footer>本工具在浏览器本地完成转换,不会上传图片或保存数据。</footer>
</div> <div class="toast" id="toast" role="status" aria-live="polite"></div> <script>
const fileInput = document.getElementById('fileInput');
const dropZone = document.getElementById('dropZone');
const previewWrap = document.getElementById('previewWrap');
const previewImg = document.getElementById('preview');
const infoEl = document.getElementById('info');
const output = document.getElementById('output');
const copyBtn = document.getElementById('copyBtn');
const clearBtn = document.getElementById('clearBtn');
const formatRadios = document.querySelectorAll('input[name="format"]');
const toast = document.getElementById('toast');
let originalDataUrl = '';
function showToast(text){
toast.textContent = text;
toast.classList.add('show');
setTimeout(()=> toast.classList.remove('show'), 1800);
}
function humanSize(bytes){
if(bytes < 1024) return bytes + ' B';
const units = ['KB','MB','GB'];
let i = -1; do { bytes = bytes / 1024; i++; } while(bytes >= 1024 && i < units.length-1);
return bytes.toFixed(bytes < 10 ? 2 : 1) + ' ' + units[i];
}
function setOutputByFormat(){
if(!originalDataUrl) return;
const base64 = originalDataUrl.split(',')[1] || '';
const format = document.querySelector('input[name="format"]:checked')?.value || 'dataurl';
output.value = format === 'base64' ? base64 : originalDataUrl;
}
function updateInfo(file){
const base64Len = (originalDataUrl.split(',')[1] || '').length;
const approxBytes = Math.floor(base64Len * 3/4); // 估算
const tip = base64Len > 3_000_000 ? '(较大,复制可能稍慢)' : '';
infoEl.innerHTML = `
<span><strong>文件:</strong>${file.name}</span>
<span><strong>类型:</strong>${file.type || '未知'}</span>
<span><strong>原大小:</strong>${humanSize(file.size)}</span>
<span><strong>Base64 长度:</strong>${base64Len.toLocaleString()} 字符 ≈ ${humanSize(approxBytes)}</span>
<span>${tip}</span>
`;
}
function handleFile(file){
if(!file) return;
if(!file.type.startsWith('image/')){
showToast('请选择图片文件');
return;
}
const reader = new FileReader();
reader.onload = (e)=>{
originalDataUrl = String(e.target.result || '');
previewImg.src = originalDataUrl;
previewWrap.hidden = false;
setOutputByFormat();
updateInfo(file);
};
reader.onerror = ()=> showToast('读取文件失败');
reader.readAsDataURL(file);
}
// 文件选择
fileInput.addEventListener('change', (e)=> handleFile(e.target.files?.[0]));
// 拖拽上传
['dragenter','dragover'].forEach(ev=> dropZone.addEventListener(ev, (e)=>{ e.preventDefault(); e.dataTransfer.dropEffect='copy'; dropZone.classList.add('dragover'); }));
;['dragleave','drop'].forEach(ev=> dropZone.addEventListener(ev, (e)=>{ e.preventDefault(); dropZone.classList.remove('dragover'); }));
dropZone.addEventListener('drop', (e)=>{
const file = e.dataTransfer.files?.[0];
handleFile(file);
});
// 键盘无障碍:回车打开文件选择
dropZone.addEventListener('keydown', (e)=>{
if(e.key === 'Enter' || e.key === ' '){
e.preventDefault();
fileInput.click();
}
});
// 切换输出格式
formatRadios.forEach(r => r.addEventListener('change', setOutputByFormat));
// 复制
copyBtn.addEventListener('click', async ()=>{
if(!output.value){ showToast('没有可复制的内容'); return; }
try{
await navigator.clipboard.writeText(output.value);
showToast('已复制到剪贴板');
}catch(err){
// 兼容:选中文本让用户手动复制
output.select();
const ok = document.execCommand?.('copy');
showToast(ok ? '已复制到剪贴板' : '复制失败,请手动复制');
}
});
// 清空
clearBtn.addEventListener('click', ()=>{
originalDataUrl = '';
output.value = '';
previewImg.removeAttribute('src');
previewWrap.hidden = true;
fileInput.value = '';
showToast('已清空');
});
// 粘贴图片(可选加分功能)
window.addEventListener('paste', (e)=>{
const items = e.clipboardData?.items || [];
for(const it of items){
if(it.type.startsWith('image/')){
const file = it.getAsFile();
handleFile(file);
showToast('已从剪贴板粘贴图片');
break;
}
}
});
</script></body>
</html>