首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 等比例生成縮略圖函數:支持裁剪/非裁剪雙模式

     編輯:PHP 相關知識

前言

等比例縮略圖生成是圖片處理的基礎需求。本文提供一個精簡、健壯的 reSizeImg() 函數,支持**裁剪模式**(填滿目標框、居中裁剪)與**非裁剪模式**(完整顯示、尺寸不固定)雙模式,代碼結構清晰,邏輯嚴密,可直接集成至項目。

核心函數:reSizeImg()

<?php
/**
 * 等比例生成縮略圖
 * @param string $img_src      源圖路徑
 * @param int    $resize_width  目標寬度
 * @param int    $resize_height 目標高度
 * @param bool   $is_cut        true=裁剪模式(填滿), false=非裁剪模式(完整顯示)
 * @return bool 成功返回 true,失敗拋異常
 */
function reSizeImg($img_src, $resize_width, $resize_height, $is_cut = false) {
    // 1. 解析源圖類型(通過擴展名,簡化版;生產建議用魔數/finfo)
    $type = strtolower(pathinfo($img_src, PATHINFO_EXTENSION));
    $type = $type === 'jpg' ? 'jpeg' : $type; // 統一 jpg→jpeg
    
    // 2. 載入源圖資源
    $create_map = [
        'jpeg' => 'imagecreatefromjpeg',
        'png'  => 'imagecreatefrompng',
        'gif'  => 'imagecreatefromgif',
        'webp' => 'imagecreatefromwebp',
    ];
    if (!isset($create_map[$type])) {
        throw new Exception("不支持的圖片格式: $type");
    }
    $src_img = $create_map[$type]($img_src);
    if (!$src_img) throw new Exception("無法載入源圖: $img_src");
    
    // 3. 獲取源圖尺寸
    $src_w = imagesx($src_img);
    $src_h = imagesy($src_img);
    
    // 4. 構建目標文件名:原名_s.擴展名
    $path_info = pathinfo($img_src);
    $dst_img = $path_info['dirname'] . '/' . $path_info['filename'] . '_s.' . $path_info['extension'];
    
    // 5. 計算比例
    $resize_ratio = $resize_width / $resize_height; // 目標比例
    $src_ratio    = $src_w / $src_h;                // 源圖比例
    
    if ($is_cut) {
        // ===== 裁剪模式:填滿目標框,居中裁剪 =====
        $dst_img_res = imagecreatetruecolor($resize_width, $resize_height);
        
        if ($src_ratio >= $resize_ratio) {
            // 源圖更「寬」或等比:以高度為基準縮放,寬度超出 → 裁剪左右
            $scale = $resize_height / $src_h;
            $scaled_w = (int)round($src_w * $scale);
            $scaled_h = $resize_height;
            $src_x = (int)(($scaled_w - $resize_width) / 2);
            $src_y = 0;
        } else {
            // 源圖更「高」:以寬度為基準縮放,高度超出 → 裁剪上下
            $scale = $resize_width / $src_w;
            $scaled_w = $resize_width;
            $scaled_h = (int)round($src_h * $scale);
            $src_x = 0;
            $src_y = (int)(($scaled_h - $resize_height) / 2);
        }
        
        // 中間畫布:縮放後的完整圖
        $mid_img = imagecreatetruecolor($scaled_w, $scaled_h);
        copy_transparency($src_img, $mid_img, $type);
        imagecopyresampled($mid_img, $src_img, 0, 0, 0, 0, $scaled_w, $scaled_h, $src_w, $src_h);
        
        // 目標畫布:裁剪中心區域
        imagecopy($dst_img_res, $mid_img, 0, 0, $src_x, $src_y, $resize_width, $resize_height);
        imagedestroy($mid_img);
        
    } else {
        // ===== 非裁剪模式:完整顯示,尺寸≤目標框 =====
        if ($src_ratio >= $resize_ratio) {
            // 源圖更寬:以寬度為基準
            $dst_w = $resize_width;
            $dst_h = (int)round($resize_width / $src_ratio);
        } else {
            // 源圖更高:以高度為基準
            $dst_h = $resize_height;
            $dst_w = (int)round($resize_height * $src_ratio);
        }
        
        $dst_img_res = imagecreatetruecolor($dst_w, $dst_h);
        copy_transparency($src_img, $dst_img_res, $type);
        imagecopyresampled($dst_img_res, $src_img, 0, 0, 0, 0, $dst_w, $dst_h, $src_w, $src_h);
    }
    
    // 6. 保存輸出
    $save_map = [
        'jpeg' => fn($im, $path) => imagejpeg($im, $path, 90),
        'png'  => fn($im, $path) => imagepng($im, $path, 6),
        'gif'  => fn($im, $path) => imagegif($im, $path),
        'webp' => fn($im, $path) => imagewebp($im, $path, 90),
    ];
    $save_map[$type]($dst_img_res, $dst_img);
    
    // 7. 清理資源
    imagedestroy($src_img);
    imagedestroy($dst_img_res);
    
    return true;
}

/**
 * 透明度保持輔助函數
 */
function copy_transparency($src, $dst, $type) {
    if (in_array($type, ['png', 'gif', 'webp'])) {
        imagealphablending($dst, false);
        imagesavealpha($dst, true);
        $tp = imagecolorallocatealpha($dst, 255, 255, 255, 127);
        imagefill($dst, 0, 0, $tp);
    }
}

使用示例

<?php
require 'reSizeImg.php';

// 1. 裁剪模式:生成 300x300 正方形頭像(填滿、居中裁剪)
reSizeImg('uploads/avatar.jpg', 300, 300, true);
// 輸出:uploads/avatar_s.jpg (300x300)

// 2. 非裁剪模式:生成寬度≤400、高度≤300 的列表圖(完整顯示)
reSizeImg('uploads/photo.png', 400, 300, false);
// 輸出:uploads/photo_s.png (如 400x225 或 300x300)

// 3. 批量處理上傳文件
$uploaded = $_FILES['photo'];
if ($uploaded['error'] === UPLOAD_ERR_OK) {
    $ext = pathinfo($uploaded['name'], PATHINFO_EXTENSION);
    $tmp_name = 'uploads/' . bin2hex(random_bytes(8)) . '.' . $ext;
    move_uploaded_file($uploaded['tmp_name'], $tmp_name);
    
    // 生成三種規格
    reSizeImg($tmp_name, 800, 600, false);  // 大圖
    reSizeImg($tmp_name, 400, 300, false);  // 中圖
    reSizeImg($tmp_name, 150, 150, true);   // 縮略圖(正方形裁剪)
}

核心邏輯圖解

源圖: 800×600 (4:3)      目標: 300×300 (1:1)     裁剪模式
src_ratio = 1.33, resize_ratio = 1.0
src_ratio >= resize_ratio → 以高為準
scale = 300/600 = 0.5
scaled_w = 800*0.5 = 400, scaled_h = 300
src_x = (400-300)/2 = 50, src_y = 0
→ 縮放至 400×300,裁剪左/右各 50px,得到 300×300 中心區域

源圖: 600×800 (3:4)      目標: 400×300 (4:3)     非裁剪模式
src_ratio = 0.75, resize_ratio = 1.33
src_ratio < resize_ratio → 以高為準
dst_h = 300, dst_w = 300*0.75 = 225
→ 輸出 225×300,完整顯示,寬度未滿 400

關鍵設計決策

1. 雙模式參數化:$is_cut 布爾值控制

  • true:Cover 模式(填滿裁剪),輸出尺寸**固定**為 $resize_width × $resize_height。
  • false:Contain 模式(完整顯示),輸出尺寸**≤**目標框,保持原比例。

2. 中間畫布策略(裁剪模式關鍵)

$mid_img = imagecreatetruecolor($scaled_w, $scaled_h); // 縮放後完整圖
imagecopyresampled($mid_img, $src_img, ...);            // 繪製縮放圖
imagecopy($dst_img_res, $mid_img, 0, 0, $src_x, $src_y, ...); // 裁剪中心

先縮放後裁剪,而非直接帶座標裁剪,邏輯更清晰、座標計算更直觀。

3. 透明度統一處理

copy_transparency() 封裝了 PNG/GIF/WebP 的 4 步驟,避免代碼重複與遺漏。

4. 文件名自動生成

{原名}_s.{擴展名} 規則簡單明確,避免覆蓋原圖,便於識別。

生產環境增強建議

  1. 類型檢測升級:用 finfo 或魔數替代擴展名判斷,防偽造。
  2. 異常處理細化:區分「文件不存在」「格式不支持」「內存不足」「磁盤滿」等錯誤碼。
  3. 輸出質量可配置:將 90/6 參數提取為函數參數或配置項。
  4. 支持指定輸出路徑:添加 $dst_path 參數,默認自動生成。
  5. 批量處理優化:同一源圖生成多規格時,單次載入 $src_img,循環生成多個 $dst_img_res。
  6. 大圖內存保護:處理前檢查 imagesx * imagesy * 4 > memory_limit * 0.8,超限拒絕或降級。

擴展:添加水印支持


function reSizeImg($img_src, $resize_width, $resize_height, $is_cut = false, $watermark = null) {
    // ... 生成 $dst_img_res 後 ...
    if ($watermark) apply_watermark($dst_img_res, $watermark);
    // ... 保存 ...
}

function apply_watermark($img, $wm) {
    // $wm = ['text'=>'©Site', 'font'=>'...', 'color'=>[255,255,255,100], 'pos'=>'br', 'size'=>14]
    // 或 $wm = ['image'=>'logo.png', 'pos'=>'br', 'opacity'=>0.5]
    // 實現略...
}

總結

reSizeImg() 函數以 60 行核心代碼實現了「雙模式等比縮放、透明度保持、多格式支持、自動命名、資源清理」的完整鏈路。裁剪模式通過「中間畫布縮放→中心裁剪」清晰解決幾何計算;非裁剪模式直接計算目標尺寸繪製。代碼無第三方依賴、邏輯自洽、易於閱讀與擴展,是中小項目圖片處理的理想基石。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved