電商商品圖、使用者頭像、海報合成常需縮圖、裁切、圓角、旋轉等組合操作。本文解析一個 resizeimage 類別,將上述功能封裝於單一建構子流程中,支援等比縮放/裁切填滿雙模式、圓角遮罩、任意角度旋轉,適合直接整合至上傳管線或即時轉檔服務。
class resizeimage {
var $type; // 副檔名
var $width, $height; // 原圖尺寸
var $resize_width, $resize_height; // 目標尺寸
var $cut; // '1'=裁切填滿, '0'=等比含蓋
var $srcimg; // 來源路徑
var $dstimg; // 輸出路徑
var $corner; // 圓角半徑 (0=不圓角)
var $im; // GD 資源
var $angle; // 旋轉角度
function resizeimage($img, $corner, $wid, $hei, $c, $corner_radius, $angle) {
$this->srcimg = $img;
$this->corner = $corner; // '1'=啟用圓角
$this->resize_width = $wid;
$this->resize_height = $hei;
$this->cut = $c; // 裁切模式
$this->corner_radius = $corner_radius;
$this->angle = $angle; // 旋轉度數
// 偵測類型
$this->type = strtolower(substr(strrchr($img, '.'), 1));
$this->initi_img(); // 載入資源
$this->dst_img(); // 產生輸出路徑
$this->width = imagesx($this->im);
$this->height = imagesy($this->im);
$this->newimg(); // 核心處理
imagedestroy($this->im);
}
}
newimg():裁切 vs 等比function newimg() {
$resize_ratio = $this->resize_width / $this->resize_height;
$ratio = $this->width / $this->height;
if ($this->cut == '1') {
// === 裁切填滿 (Cover) ===
$newimg = imagecreatetruecolor($this->resize_width, $this->resize_height);
if ($ratio >= $resize_ratio) {
// 原圖較寬 → 以高為準縮放,左右裁切
$srcH = $this->height;
$srcW = $srcH * $resize_ratio;
$srcX = ($this->width - $srcW) / 2;
$srcY = 0;
} else {
// 原圖較高 → 以寬為準縮放,上下裁切
$srcW = $this->width;
$srcH = $srcW / $resize_ratio;
$srcX = 0;
$srcY = ($this->height - $srcH) / 2;
}
imagecopyresampled($newimg, $this->im, 0,0, $srcX,$srcY, $this->resize_width,$this->resize_height, $srcW,$srcH);
// 圓角處理
$tmp = $this->rounded_corner($newimg, $this->resize_width);
imagepng($tmp, $this->dstimg);
} else {
// === 等比含蓋 (Contain) - 原碼未完整,此處補全 ===
$newimg = imagecreatetruecolor($this->resize_width, $this->resize_height);
// 白底 (JPEG) 或透明 (PNG)
$white = imagecolorallocate($newimg, 255,255,255);
imagefill($newimg, 0,0, $white);
if ($ratio >= $resize_ratio) {
// 以寬為準
$dstW = $this->resize_width;
$dstH = round($this->height * $dstW / $this->width);
$dstX = 0; $dstY = ($this->resize_height - $dstH) / 2;
} else {
// 以高為準
$dstH = $this->resize_height;
$dstW = round($this->width * $dstH / $this->height);
$dstX = ($this->resize_width - $dstW) / 2; $dstY = 0;
}
imagecopyresampled($newimg, $this->im, $dstX,$dstY, 0,0, $dstW,$dstH, $this->width,$this->height);
// 旋轉
if ($this->angle) {
$newimg = imagerotate($newimg, $this->angle, $white);
}
// 圓角
if ($this->corner == '1') $newimg = $this->rounded_corner($newimg, $this->resize_width);
imagepng($newimg, $this->dstimg);
}
}
rounded_corner() (補全實作)function rounded_corner($src, $radius) {
$w = imagesx($src); $h = imagesy($src);
$mask = imagecreatetruecolor($w, $h);
$transparent = imagecolorallocatealpha($mask, 0,0,0, 127);
imagefill($mask, 0,0, $transparent);
$white = imagecolorallocate($mask, 255,255,255);
// 四個圓角扇形
imagefilledellipse($mask, $radius, $radius, $radius*2, $radius*2, $white); // 左上
imagefilledellipse($mask, $w-$radius, $radius, $radius*2, $radius*2, $white); // 右上
imagefilledellipse($mask, $radius, $h-$radius, $radius*2, $radius*2, $white); // 左下
imagefilledellipse($mask, $w-$radius, $h-$radius, $radius*2, $radius*2, $white); // 右下
// 中間矩形填補
imagefilledrectangle($mask, $radius, 0, $w-$radius, $h, $white);
imagefilledrectangle($mask, 0, $radius, $w, $h-$radius, $white);
// 套用遮罩
$dst = imagecreatetruecolor($w, $h);
imagealphablending($dst, false);
imagesavealpha($dst, true);
$trans = imagecolorallocatealpha($dst, 0,0,0, 127);
imagefill($dst, 0,0, $trans);
imagecopy($dst, $src, 0,0, 0,0, $w,$h);
imagecopymerge($dst, $mask, 0,0, 0,0, $w,$h, 100); // 以 mask 為 alpha
return $dst;
}
function initi_img() {
switch ($this->type) {
case 'gif': $this->im = @imagecreatefromgif($this->srcimg); break;
case 'jpg':
case 'jpeg': $this->im = @imagecreatefromjpeg($this->srcimg); break;
case 'png': $this->im = @imagecreatefrompng($this->srcimg); break;
default: die('不支援格式');
}
}
function dst_img() {
$path = dirname($this->srcimg);
$name = basename($this->srcimg, '.'.$this->type);
$this->dstimg = $path . '/' . $name . '_processed.png'; // 統一輸出 PNG 保留透明度
}
// 1. 裁切填滿 300x300 + 圓角 20px
new resizeimage(
'uploads/photo.jpg', // 來源
'1', // 圓角啟用
300, 300, // 目標尺寸
'1', // 裁切模式
20, // 圓角半徑
0 // 不旋轉
); // 輸出 uploads/photo_processed.png
// 2. 等比含蓋 400x300 + 旋轉 15° + 無圓角
new resizeimage(
'uploads/banner.png',
'0', 400, 300, '0', 0, 15
);
| 功能 | 原類別支援 | 建議擴充 |
|---|---|---|
| 縮放模式 | 裁切/等比 | 新增 stretch (強制拉伸)、fit (等比含蓋含白邊) |
| 圓角 | 四角同半徑 | 支援單獨四角半徑 ['tl'=>20,'tr'=>0,'br'=>20,'bl'=>0] |
| 旋轉 | 任意角度 | 新增 flipH/flipV 鏡像 |
| 輸出格式 | 固定 PNG | 參數化 $format, $quality |
| 濾鏡 | 無 | 整合 imagefilter() (灰階、模糊、銳化) |
| 疊加 | 無 | 新增 overlay($logoPath, $position, $opacity) |
| 記憶體管理 | 手動 destroy | 實作 __destruct 自動清理 |
use Intervention\Image\ImageManagerStatic as Image;
$img = Image::make('uploads/photo.jpg')
->resize(300, 300, function($c) { $c->aspectRatio(); }) // 等比
->resizeCanvas(300, 300, 'center', false, '#fff') // 畫布填滿
->rounded(20) // 圓角
->rotate(15, '#fff') // 旋轉
->encode('png', 90)
->save('uploads/photo_processed.png');
resizeimage 類別將縮放模式、圓角、旋轉整合在建構子單一流程中,雖程式碼風格較舊 (var、public 屬性、建構子做所有事),但邏輯清晰、功能實用。實務上建議採用 Intervention Image 的鏈式 API,或將每個操作封裝為獨立 Processor 類別,組合成 Pipeline,獲得更好的可測試性、可擴充性與單一職責原則遵循。