Oh MyUtils

画像圧縮ツール - PNG, JPG, WebP 圧縮 オンライン

画質を維持しながら画像を圧縮。PNG、JPG、WebPファイルサイズを前後比較付きで削減 — アップロードなし、100%ブラウザ処理。

よくある質問

画像圧縮とは何ですか?なぜ必要ですか?

画像圧縮は、不要なデータを削除したり、画像をより効率的にエンコードする数学的アルゴリズムを適用することで、画像のファイルサイズを縮小します。Web開発において、圧縮された画像はページの読み込み時間の短縮、帯域幅消費の削減、Core Web Vitalsスコアの向上、SEOランキングの改善につながります。一般的な5MBのDSLR写真は、視覚的な品質損失を最小限に抑えながら500KB以下に圧縮できます。

この画像圧縮ツールの使い方は?

1. アップロードエリアに画像をドラッグ&ドロップ(またはクリックしてファイルを選択)。2. 品質スライダーを調整(デフォルトは80%で、良いバランスです)。3. オプションで最大幅/高さを設定してリサイズ。4. 個別の画像で「圧縮」またはバッチ処理で「すべて圧縮」をクリック。5. 比較タブでビフォー/アフターを確認。6. 個別の画像をダウンロードまたは「すべてダウンロード(ZIP)」でZIPアーカイブを取得。

画像データは安全ですか?サーバーにアップロードされますか?

画像は100%安全で、ブラウザから離れることはありません。すべての圧縮はHTML5 Canvas APIとOffscreenCanvasを使用してクライアントサイドで実行されます。画像データはサーバーに送信されることはなく、完全にプライベートです。ページ読み込み後はオフラインでも使用できます。これがTinyPNGなどのサーバーベースのツールとの違いです。

非可逆圧縮と可逆圧縮の違いは何ですか?

非可逆圧縮(JPEG、WebP)は、より小さいファイルサイズを実現するために一部の画像データを永久的に削除します。70-90%の品質設定では、視覚的な差異は通常感知できません。可逆圧縮(PNG)はすべての画像データを正確に保存し、ファイルは大きくなりますが完璧な品質が得られます。JPEGとWebPはデフォルトで非可逆圧縮を使用し、Canvas API使用時のPNGは常に可逆です。

JPEG、PNG、WebPのどの画像フォーマットを使うべきですか?

JPEGは、多くの色やグラデーションを持つ写真や複雑な画像に最適です(透明度非対応)。PNGは、グラフィック、アイコン、スクリーンショット、テキストやシャープなエッジのある画像に最適です(透明度対応、ファイルサイズ大)。WebPは同等の品質でJPEGより通常25-35%小さい最新フォーマットで、非可逆と可逆の両方の圧縮と透明度をサポートし、95%以上のグローバルブラウザサポートでWeb使用に推奨されます。

圧縮後にPNGファイルが大きくなったのはなぜですか?

Canvas APIは可逆PNG出力を生成しますが、これは最適化されたPNGエンコーダー(OptiPNGやpngquantなど)とは異なる場合があります。元のPNGがすでに十分に最適化されていた場合、Canvasを通じた再エンコードはわずかに大きなファイルを生成する可能性があります。PNGサイズを縮小するには、画像をより小さい寸法にリサイズしてみてください。

サポートされている最大ファイルサイズと解像度は?

このツールはファイルあたり最大50MB、最大16384 x 16384ピクセルの解像度(ほとんどのブラウザでのCanvas API制限)をサポートしています。非常に高解像度の画像の場合は、まずリサイズオプションを使用して寸法を縮小することを検討してください。これにより圧縮も高速化されます。モバイルデバイスでは、メモリの制約により最大解像度が低くなる場合があります。

複数の画像を一度に圧縮できますか?

はい、バッチ処理は最大20枚の画像を同時にサポートします。すべての画像に対してグローバルな品質レベルを設定できます。圧縮後、「すべてダウンロード(ZIP)」を使用して、すべての圧縮済み画像を1つのZIPファイルでダウンロードできます。

コード例

// Client-side image compression using Canvas API
async function compressImage(file, quality = 0.8) {
  return new Promise((resolve, reject) => {
    const img = new Image();
    const url = URL.createObjectURL(file);

    img.onload = () => {
      URL.revokeObjectURL(url);

      const canvas = document.createElement('canvas');
      canvas.width = img.width;
      canvas.height = img.height;

      const ctx = canvas.getContext('2d');
      ctx.drawImage(img, 0, 0);

      // Determine output type (use WebP if supported, fallback to JPEG)
      const outputType = file.type === 'image/png' ? 'image/png' : 'image/webp';

      canvas.toBlob(
        (blob) => {
          if (!blob) {
            reject(new Error('Compression failed'));
            return;
          }
          resolve({
            blob,
            originalSize: file.size,
            compressedSize: blob.size,
            reductionPercent: Math.round((1 - blob.size / file.size) * 100),
          });
        },
        outputType,
        outputType === 'image/png' ? undefined : quality
      );
    };

    img.onerror = () => {
      URL.revokeObjectURL(url);
      reject(new Error('Failed to load image'));
    };

    img.src = url;
  });
}

// Compress with resize
async function compressAndResize(file, maxWidth, maxHeight, quality = 0.8) {
  return new Promise((resolve, reject) => {
    const img = new Image();
    const url = URL.createObjectURL(file);

    img.onload = () => {
      URL.revokeObjectURL(url);

      let { width, height } = img;

      // Calculate new dimensions maintaining aspect ratio
      if (maxWidth && width > maxWidth) {
        height = Math.round((height * maxWidth) / width);
        width = maxWidth;
      }
      if (maxHeight && height > maxHeight) {
        width = Math.round((width * maxHeight) / height);
        height = maxHeight;
      }

      const canvas = document.createElement('canvas');
      canvas.width = width;
      canvas.height = height;

      const ctx = canvas.getContext('2d');
      ctx.drawImage(img, 0, 0, width, height);

      canvas.toBlob(
        (blob) => {
          if (!blob) {
            reject(new Error('Compression failed'));
            return;
          }
          resolve({
            blob,
            originalSize: file.size,
            compressedSize: blob.size,
            width,
            height,
            reductionPercent: Math.round((1 - blob.size / file.size) * 100),
          });
        },
        'image/jpeg',
        quality
      );
    };

    img.onerror = () => {
      URL.revokeObjectURL(url);
      reject(new Error('Failed to load image'));
    };

    img.src = url;
  });
}

// Batch compression
async function compressBatch(files, quality = 0.8) {
  const results = [];
  for (const file of files) {
    try {
      const result = await compressImage(file, quality);
      results.push({ filename: file.name, ...result });
    } catch (error) {
      results.push({ filename: file.name, error: error.message });
    }
  }
  return results;
}

// Usage
const input = document.querySelector('input[type="file"]');
input.addEventListener('change', async (e) => {
  const files = Array.from(e.target.files);
  const results = await compressBatch(files, 0.8);

  results.forEach((result) => {
    if (result.error) {
      console.error(`${result.filename}: ${result.error}`);
    } else {
      console.log(
        `${result.filename}: ${result.originalSize} -> ${result.compressedSize} (${result.reductionPercent}% reduction)`
      );
    }
  });
});

関連ツール