<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">图片翻转工具 · 水平垂直镜像</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background: #f5f7fb;
font-family: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
padding: 16px;
}
.card {
background: white;
border-radius: 32px;
box-shadow: 0 20px 40px -12px rgba(0, 0, 0, 0.15), 0 4px 18px rgba(0,0,0,0.05);
max-width: 760px;
width: 100%;
padding: 28px 24px 32px;
transition: all 0.2s ease;
}
h1 {
font-weight: 600;
font-size: 1.9rem;
letter-spacing: -0.01em;
color: #0b1a33;
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
margin-bottom: 8px;
}
h1 small {
font-size: 0.9rem;
font-weight: 400;
color: #64748b;
margin-left: 6px;
}
.subhead {
color: #475569;
margin-bottom: 24px;
border-left: 4px solid #3b82f6;
padding-left: 14px;
background: #f8fafc;
border-radius: 0 8px 8px 0;
line-height: 1.4;
font-size: 0.95rem;
}
.upload-area {
background: #f1f5f9;
border: 2px dashed #cbd5e1;
border-radius: 24px;
padding: 32px 16px;
text-align: center;
cursor: pointer;
transition: all 0.15s ease;
position: relative;
}
.upload-area:hover {
background: #eef2f6;
border-color: #94a3b8;
}
.upload-area.dragover {
background: #e2e8f0;
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59,130,246,0.15);
}
.upload-icon {
font-size: 48px;
line-height: 1;
margin-bottom: 8px;
opacity: 0.7;
}
.upload-text {
font-weight: 500;
color: #1e293b;
}
.upload-hint {
font-size: 0.85rem;
color: #64748b;
margin-top: 8px;
}
#fileInput {
display: none;
}
.canvas-wrapper {
position: relative;
margin: 22px 0 18px;
border-radius: 20px;
overflow: hidden;
background: #f1f5f9;
box-shadow: inset 0 0 0 1px rgba(0,0,0,0.04);
display: flex;
justify-content: center;
align-items: center;
min-height: 200px;
}
#imageCanvas {
display: block;
max-width: 100%;
height: auto;
background: #ffffff;
box-shadow: 0 4px 12px rgba(0,0,0,0.04);
transition: transform 0.1s ease;
image-rendering: auto;
}
.placeholder-note {
color: #64748b;
padding: 48px 16px;
font-size: 0.95rem;
text-align: center;
width: 100%;
}
.control-bar {
display: flex;
flex-wrap: wrap;
gap: 14px 18px;
align-items: center;
justify-content: center;
margin: 20px 0 18px;
}
.btn-group {
display: flex;
gap: 12px;
flex-wrap: wrap;
justify-content: center;
}
.btn {
background: white;
border: 1px solid #d1d9e6;
padding: 10px 20px;
border-radius: 40px;
font-weight: 500;
font-size: 0.95rem;
color: #1e293b;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 8px;
box-shadow: 0 1px 3px rgba(0,0,0,0.02);
transition: all 0.12s ease;
background: #f9fafc;
}
.btn:hover {
background: #f0f4fe;
border-color: #3b82f6;
color: #1e4b8c;
box-shadow: 0 4px 8px rgba(59,130,246,0.08);
transform: translateY(-1px);
}
.btn:active {
transform: scale(0.96);
background: #e6edf8;
}
.btn-primary {
background: #2563eb;
border-color: #2563eb;
color: white;
box-shadow: 0 4px 8px rgba(37,99,235,0.15);
}
.btn-primary:hover {
background: #1d4ed8;
border-color: #1d4ed8;
color: white;
box-shadow: 0 6px 12px rgba(37,99,235,0.25);
}
.btn-success {
background: #0f973d;
border-color: #0f973d;
color: white;
box-shadow: 0 4px 8px rgba(15,151,61,0.15);
}
.btn-success:hover {
background: #0b7e33;
border-color: #0b7e33;
color: white;
}
.btn-icon {
font-size: 1.2rem;
line-height: 1;
}
.badge {
background: #e9edf2;
border-radius: 30px;
padding: 2px 12px;
font-size: 0.8rem;
color: #2c3e50;
display: inline-block;
}
.status-row {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
font-size: 0.9rem;
color: #334155;
margin: 8px 0 4px;
padding: 0 4px;
}
.footer-actions {
display: flex;
flex-wrap: wrap;
gap: 12px;
justify-content: center;
margin-top: 8px;
}
.reset-link {
color: #64748b;
background: none;
border: none;
font-size: 0.9rem;
cursor: pointer;
text-decoration: underline dotted;
padding: 4px 6px;
}
.reset-link:hover {
color: #1e293b;
}
@media (max-width: 480px) {
.card { padding: 20px 16px; }
.btn { padding: 8px 16px; font-size: 0.85rem; }
.btn-group { gap: 8px; }
}
</style>
</head>
<body>
<div class="card">
<h1>
🖼️ 翻转工具
<small>水平 · 垂直镜像</small>
</h1>
<div class="subhead">
上传图片,一键水平翻转 (左右镜像) 或垂直翻转 (上下镜像)
</div>
<!-- 上传区域 -->
<div class="upload-area" id="dropZone">
<div class="upload-icon">📁</div>
<div class="upload-text">点击选择图片 或 拖拽图片到此处</div>
<div class="upload-hint">支持 JPG / PNG / WebP 等常见格式</div>
<input type="file" id="fileInput" accept="image/*">
</div>
<!-- 画布预览区 -->
<div class="canvas-wrapper">
<canvas id="imageCanvas" width="600" height="400"></canvas>
<div class="placeholder-note" id="placeholderMsg">暂无图片 · 请上传</div>
</div>
<!-- 翻转控制 -->
<div class="control-bar">
<div class="btn-group">
<button class="btn" id="flipHBtn" title="水平镜像 (左右翻转)">
<span class="btn-icon">↔</span> 水平翻转
</button>
<button class="btn" id="flipVBtn" title="垂直镜像 (上下翻转)">
<span class="btn-icon">↕</span> 垂直翻转
</button>
</div>
</div>
<!-- 状态与下载 -->
<div class="status-row">
<span id="imageStatus"><span class="badge">⏳ 未加载</span></span>
<span id="flipInfo" style="font-weight: 450;">镜像: 无</span>
</div>
<div class="footer-actions">
<button class="btn btn-success" id="downloadBtn">
<span class="btn-icon">⬇</span> 下载翻转图片
</button>
<button class="reset-link" id="resetBtn">↺ 重置为原始方向</button>
</div>
</div>
<script>
(function() {
const canvas = document.getElementById('imageCanvas');
const ctx = canvas.getContext('2d');
const fileInput = document.getElementById('fileInput');
const dropZone = document.getElementById('dropZone');
const placeholderMsg = document.getElementById('placeholderMsg');
const imageStatus = document.getElementById('imageStatus');
const flipInfo = document.getElementById('flipInfo');
const flipHBtn = document.getElementById('flipHBtn');
const flipVBtn = document.getElementById('flipVBtn');
const downloadBtn = document.getElementById('downloadBtn');
const resetBtn = document.getElementById('resetBtn');
// 状态存储
let sourceImage = null; // 原始图片对象 (HTMLImageElement)
let canvasWidth = 600;
let canvasHeight = 400;
let flipH = false; // 当前水平镜像状态
let flipV = false; // 当前垂直镜像状态
// 初始化显示占位
function drawPlaceholder() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
placeholderMsg.style.display = 'block';
canvas.style.background = '#f1f5f9';
imageStatus.innerHTML = `<span class="badge">⏳ 未加载</span>`;
flipInfo.textContent = '镜像: 无';
}
drawPlaceholder();
// 更新翻转状态显示
function updateFlipInfo() {
const parts = [];
if (flipH) parts.push('水平');
if (flipV) parts.push('垂直');
if (parts.length === 0) flipInfo.textContent = '镜像: 无';
else flipInfo.textContent = `镜像: ${parts.join(' + ')}`;
}
// 核心绘制: 根据当前翻转状态绘制图像
function renderImage() {
if (!sourceImage) {
drawPlaceholder();
return;
}
placeholderMsg.style.display = 'none';
canvas.style.background = '#fff';
// 计算 canvas 尺寸适应图片比例 (保持比例, 最大宽600)
let imgW = sourceImage.naturalWidth || sourceImage.width;
let imgH = sourceImage.naturalHeight || sourceImage.height;
if (imgW === 0 || imgH === 0) {
// 回退
imgW = 300; imgH = 200;
}
const maxW = 600;
let displayW, displayH;
if (imgW > maxW) {
displayW = maxW;
displayH = (imgH / imgW) * maxW;
} else {
displayW = imgW;
displayH = imgH;
}
// 确保整数 (避免模糊)
displayW = Math.floor(displayW);
displayH = Math.floor(displayH);
if (displayW < 20) displayW = 20;
if (displayH < 20) displayH = 20;
canvas.width = displayW;
canvas.height = displayH;
canvasWidth = displayW;
canvasHeight = displayH;
// 清除画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// ---------- 镜像变换 ----------
ctx.save();
// 水平翻转: 先平移再缩放 (-1, 1)
if (flipH && flipV) {
ctx.translate(canvas.width, canvas.height);
ctx.scale(-1, -1);
} else if (flipH) {
ctx.translate(canvas.width, 0);
ctx.scale(-1, 1);
} else if (flipV) {
ctx.translate(0, canvas.height);
ctx.scale(1, -1);
}
// 绘制图像 (使用原始图片大小)
ctx.drawImage(sourceImage, 0, 0, canvas.width, canvas.height);
ctx.restore();
// 更新状态UI
const label = sourceImage.alt || '图片';
imageStatus.innerHTML = `<span class="badge">✅ ${label} · ${canvas.width}×${canvas.height}</span>`;
updateFlipInfo();
}
// 从文件加载图片
function loadImageFile(file) {
if (!file || !file.type.startsWith('image/')) {
alert('请选择有效的图片文件');
return;
}
const reader = new FileReader();
reader.onload = (e) => {
const img = new Image();
img.onload = () => {
sourceImage = img;
// 重置翻转状态
flipH = false;
flipV = false;
renderImage();
};
img.onerror = () => {
alert('图片加载失败,请尝试其他文件');
};
img.src = e.target.result;
};
reader.readAsDataURL(file);
}
// ----- 事件绑定 -----
// 点击上传
dropZone.addEventListener('click', () => {
fileInput.click();
});
fileInput.addEventListener('change', (e) => {
if (e.target.files && e.target.files[0]) {
loadImageFile(e.target.files[0]);
}
// 重置input以允许重复上传相同文件
fileInput.value = '';
});
// 拖拽上传
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('dragover');
});
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('dragover');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('dragover');
const files = e.dataTransfer.files;
if (files && files.length > 0) {
loadImageFile(files[0]);
}
});
// 水平翻转
flipHBtn.addEventListener('click', () => {
if (!sourceImage) {
alert('请先上传图片');
return;
}
flipH = !flipH;
renderImage();
});
// 垂直翻转
flipVBtn.addEventListener('click', () => {
if (!sourceImage) {
alert('请先上传图片');
return;
}
flipV = !flipV;
renderImage();
});
// 重置 (清除所有翻转)
resetBtn.addEventListener('click', () => {
if (!sourceImage) {
return;
}
if (flipH === false && flipV === false) {
// 如果已经是原始状态,轻提示
return;
}
flipH = false;
flipV = false;
renderImage();
});
// 下载
downloadBtn.addEventListener('click', () => {
if (!sourceImage) {
alert('没有图片可下载,请先上传');
return;
}
// 导出当前canvas内容 (已应用翻转)
const link = document.createElement('a');
link.download = `flipped_${Date.now()}.png`;
link.href = canvas.toDataURL('image/png');
link.click();
});
// 额外:键盘快捷键? (非必须)
// 保证在页面内也能重置样式
window.addEventListener('load', () => {
// 若canvas显示占位, 确保宽高
});
// 小优化:如果用户点击重置时无图片,忽略
// 扩展: 双击canvas重置? (个人喜好)
canvas.addEventListener('dblclick', () => {
if (sourceImage) {
flipH = false;
flipV = false;
renderImage();
}
});
})();
</script>
</body>
</html>
核心功能与操作流程
您可以通过上传图片,并点击按钮来应用镜像效果,其核心交互流程如下:

- 图片加载:您可以通过点击上传区域或直接拖拽图片来加载图片,支持 JPG、PNG 等常见格式。
- 应用翻转:点击 “水平翻转” 按钮可使图片左右镜像,点击 “垂直翻转” 按钮可使图片上下镜像,您可以组合使用这两个按钮。
- 管理与导出:界面会清晰显示当前应用的镜像状态,您可以通过 “重置为原始方向” 按钮撤销所有翻转,或使用 “下载翻转图片” 按钮保存处理后的图片。
优化建议: 您可以根据需要调整 maxW 变量(第166行附近)的值,以修改画布预览的最大宽度,从而适配不同的展示布局。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播