star-cloud/app/Traits/ImageHandler.php
sky121113 2fd6419129 [FEAT] 商品圖片尺寸標準化與自動裁切功能實作
1. 增強 ImageHandler trait,支援居中裁切(Center Crop)與指定尺寸縮放功能。
2. 更新 ProductController 的 store 與 update 方法,強制將商品圖片處理為 320x320 像素之 WebP 格式。
3. 優化 create 與 edit 商品頁面,新增 320x320 建議尺寸與自動裁切之提示文字。
4. 新增繁體中文、英文與日文的多語系翻譯對應。
2026-05-13 09:46:24 +08:00

115 lines
3.9 KiB
PHP

<?php
namespace App\Traits;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
trait ImageHandler
{
/**
* 將圖片轉換為 WebP 並儲存 (支援自動裁切與縮放)
*
* @param UploadedFile $file 原始檔案
* @param string $directory 儲存目錄 (不含 disk 名稱)
* @param int $quality 壓縮品質 (0-100)
* @param int|null $targetWidth 目標寬度
* @param int|null $targetHeight 目標高度
* @return string 儲存的路徑
*/
protected function storeAsWebp(UploadedFile $file, string $directory, int $quality = 80, ?int $targetWidth = null, ?int $targetHeight = null): string
{
$filename = Str::random(40) . '.webp';
$path = "{$directory}/{$filename}";
// 讀取原始圖片資訊
$imageInfo = getimagesize($file->getRealPath());
if (!$imageInfo) {
return $file->store($directory, 'public');
}
$origWidth = $imageInfo[0];
$origHeight = $imageInfo[1];
$mime = $imageInfo['mime'];
$source = null;
switch ($mime) {
case 'image/jpeg':
$source = imagecreatefromjpeg($file->getRealPath());
break;
case 'image/png':
$source = imagecreatefrompng($file->getRealPath());
break;
case 'image/gif':
$source = imagecreatefromgif($file->getRealPath());
break;
case 'image/webp':
$source = imagecreatefromwebp($file->getRealPath());
break;
default:
// 不支援的格式直接存
return $file->store($directory, 'public');
}
if (!$source) {
return $file->store($directory, 'public');
}
// 處理裁切與縮放
if ($targetWidth && $targetHeight) {
$targetImage = imagecreatetruecolor($targetWidth, $targetHeight);
// 處理透明背景 (如果是 PNG/WebP)
if ($mime == 'image/png' || $mime == 'image/webp') {
imagealphablending($targetImage, false);
imagesavealpha($targetImage, true);
$transparent = imagecolorallocatealpha($targetImage, 255, 255, 255, 127);
imagefilledrectangle($targetImage, 0, 0, $targetWidth, $targetHeight, $transparent);
}
// 居中裁切計算法
$sourceAspect = $origWidth / $origHeight;
$targetAspect = $targetWidth / $targetHeight;
if ($sourceAspect > $targetAspect) {
// 原圖太寬,裁切兩邊
$tempHeight = $origHeight;
$tempWidth = (int)($origHeight * $targetAspect);
$srcX = (int)(($origWidth - $tempWidth) / 2);
$srcY = 0;
} else {
// 原圖太高,裁切上下
$tempWidth = $origWidth;
$tempHeight = (int)($origWidth / $targetAspect);
$srcX = 0;
$srcY = (int)(($origHeight - $tempHeight) / 2);
}
imagecopyresampled(
$targetImage, $source,
0, 0, $srcX, $srcY,
$targetWidth, $targetHeight, $tempWidth, $tempHeight
);
imagedestroy($source);
$source = $targetImage;
} else {
// 確保支援真彩色 (解決 palette image 問題)
if (!imageistruecolor($source)) {
imagepalettetotruecolor($source);
}
}
// 確保目錄存在
Storage::disk('public')->makeDirectory($directory);
$fullPath = Storage::disk('public')->path($path);
// 轉換並儲存
imagewebp($source, $fullPath, $quality);
imagedestroy($source);
return $path;
}
}