網站常需將使用者上傳的圖片產生多種尺寸(縮圖、列表圖、詳情圖)。直接縮放會變形,單純裁切可能切掉主體。本文提供一個智慧裁切縮放函式:依目標長寬比,自動決定「先縮放再置中裁切」或「先裁切再縮放」,確保主體完整、無變形、無留白。
$srcRatio = $srcW / $srcH, $dstRatio = $dstW / $dstH<?php
/**
* 智慧裁切縮放:保持比例、置中裁切、無變形
*
* @param string $srcPath 來源圖片路徑
* @param string $dstPath 目標儲存路徑
* @param int $dstWidth 目標寬度
* @param int $dstHeight 目標高度
* @param int $quality JPEG/WebP 品質 0-100
* @return bool 成功與否
*/
function smart_resize_crop(string $srcPath, string $dstPath, int $dstWidth, int $dstHeight, int $quality = 90): bool
{
if (!file_exists($srcPath)) {
trigger_error("來源檔案不存在: $srcPath", E_USER_WARNING);
return false;
}
if ($dstWidth <= 0 || $dstHeight <= 0) {
trigger_error('目標寬高必須大於 0', E_USER_WARNING);
return false;
}
// 1. 偵測圖片類型並建立資源
$type = exif_imagetype($srcPath);
$supported = [IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_GIF, IMAGETYPE_WEBP];
if (!in_array($type, $supported, true)) {
trigger_error('不支援的圖片格式,僅支援 JPEG/PNG/GIF/WebP', E_USER_WARNING);
return false;
}
$createFunc = [
IMAGETYPE_JPEG => 'imagecreatefromjpeg',
IMAGETYPE_PNG => 'imagecreatefrompng',
IMAGETYPE_GIF => 'imagecreatefromgif',
IMAGETYPE_WEBP => 'imagecreatefromwebp',
];
$srcImg = $createFunc[$type]($srcPath);
if (!$srcImg) {
trigger_error('建立圖片資源失敗', E_USER_WARNING);
return false;
}
// 2. 取得原始尺寸
$srcW = imagesx($srcImg);
$srcH = imagesy($srcImg);
// 3. 計算縮放比例與中間畫布尺寸
$srcRatio = $srcW / $srcH;
$dstRatio = $dstWidth / $dstHeight;
if ($srcRatio > $dstRatio) {
// 原圖較寬 → 以高度為基準縮放
$interW = (int)round($dstHeight * $srcRatio);
$interH = $dstHeight;
} else {
// 原圖較高或相等 → 以寬度為基準縮放
$interW = $dstWidth;
$interH = (int)round($dstWidth / $srcRatio);
}
// 4. 建立中間畫布並縮放
$interImg = imagecreatetruecolor($interW, $interH);
// 保留透明度(PNG/GIF/WebP)
if ($type === IMAGETYPE_PNG || $type === IMAGETYPE_GIF || $type === IMAGETYPE_WEBP) {
imagealphablending($interImg, false);
imagesavealpha($interImg, true);
$transparent = imagecolorallocatealpha($interImg, 0, 0, 0, 127);
imagefill($interImg, 0, 0, $transparent);
}
imagecopyresampled($interImg, $srcImg, 0, 0, 0, 0, $interW, $interH, $srcW, $srcH);
// 5. 置中裁切至目標尺寸
$dstImg = imagecreatetruecolor($dstWidth, $dstHeight);
if ($type === IMAGETYPE_PNG || $type === IMAGETYPE_GIF || $type === IMAGETYPE_WEBP) {
imagealphablending($dstImg, false);
imagesavealpha($dstImg, true);
$transparent = imagecolorallocatealpha($dstImg, 0, 0, 0, 127);
imagefill($dstImg, 0, 0, $transparent);
}
$srcX = ($interW - $dstWidth) / 2;
$srcY = ($interH - $dstHeight) / 2;
imagecopy($dstImg, $interImg, 0, 0, $srcX, $srcY, $dstWidth, $dstHeight);
// 6. 輸出儲存
$outputFunc = [
IMAGETYPE_JPEG => fn($img, $path) => imagejpeg($img, $path, $quality),
IMAGETYPE_PNG => fn($img, $path) => imagepng($img, $path, (int)round($quality / 10)), // PNG 壓縮等級 0-9
IMAGETYPE_GIF => fn($img, $path) => imagegif($img, $path),
IMAGETYPE_WEBP => fn($img, $path) => imagewebp($img, $path, $quality),
];
$success = $outputFunc[$type]($dstImg, $dstPath);
// 7. 釋放記憶體
imagedestroy($srcImg);
imagedestroy($interImg);
imagedestroy($dstImg);
return $success;
}
<?php
require_once 'smart_resize_crop.php';
// 產生 400x300 縮圖
smart_resize_crop('uploads/photo.jpg', 'thumbs/photo_400x300.jpg', 400, 300, 85);
// 批次處理上傳目錄
$targetSizes = [
'thumb' => [150, 150],
'medium' => [400, 300],
'large' => [800, 600],
];
foreach (glob('uploads/*.{jpg,jpeg,png,gif,webp}', GLOB_BRACE) as $file) {
$baseName = pathinfo($file, PATHINFO_FILENAME);
foreach ($targetSizes as $prefix => [$w, $h]) {
$out = "thumbs/{$baseName}_{$prefix}.jpg";
smart_resize_crop($file, $out, $w, $h, 85);
}
}
// 修改輸出函式,統一輸出 WebP
$outputFunc = [
IMAGETYPE_JPEG => fn($img, $path) => imagewebp($img, $path, $quality),
IMAGETYPE_PNG => fn($img, $path) => imagewebp($img, $path, $quality),
IMAGETYPE_GIF => fn($img, $path) => imagewebp($img, $path, $quality),
IMAGETYPE_WEBP => fn($img, $path) => imagewebp($img, $path, $quality),
];
// 並將 $dstPath 副檔名改為 .webp
memory_limit(如 256M)。convert 更靈活。智慧裁切縮放的核心在於比例比較 → 基準縮放 → 置中裁切三步驟。封裝為可重複使用的函式,並妥善處理透明度、記憶體釋放、多格式輸出,即可滿足多數 Web 專案的圖片處理需求。若流量大、圖片多,建議升級至 ImageMagick 或雲端圖片服務。