图片翻转工具水平垂直镜像

联启 电脑工具 15
<!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>

核心功能与操作流程

您可以通过上传图片,并点击按钮来应用镜像效果,其核心交互流程如下:

图片翻转工具水平垂直镜像-第1张图片-电脑手机工具软件下载 - 免费实用工具合集 | 联启科技

  1. 图片加载:您可以通过点击上传区域或直接拖拽图片来加载图片,支持 JPG、PNG 等常见格式。
  2. 应用翻转:点击 “水平翻转” 按钮可使图片左右镜像,点击 “垂直翻转” 按钮可使图片上下镜像,您可以组合使用这两个按钮。
  3. 管理与导出:界面会清晰显示当前应用的镜像状态,您可以通过 “重置为原始方向” 按钮撤销所有翻转,或使用 “下载翻转图片” 按钮保存处理后的图片。

优化建议: 您可以根据需要调整 maxW 变量(第166行附近)的值,以修改画布预览的最大宽度,从而适配不同的展示布局。

标签: 图片翻转工具 水平垂直镜像

抱歉,评论功能暂时关闭!