功能特色
- 支援多資料系列、自動計算百分比
- 3D 立體效果 (側面、頂面陰影)
- 中文標籤支援 (TrueType 字體)
- 圖例色塊、數值、百分比顯示
- 輸出 PNG 檔案、可直接
<img src="pie.php"> 嵌入
完整程式碼 (pie3d.php)
>16)&0xFF, ($hex>>8)&0xFF, $hex&0xFF];
}
function darker($rgb) { return [$rgb[0]/2, $rgb[1]/2, $rgb[2]/2]; }
function ellipsePoint($a, $b, $deg) {
$rad = deg2rad($deg);
return [round($a*cos($rad)), round($b*sin($rad))];
}
// 建立畫布
$n = count($data);
$sum = array_sum($data);
$a = 250; $b = 120; // 橢圓長/短軸
$cx = $a + 10; $cy = $b + 10 + PIE_3D_HEIGHT;
$w = $a*2 + 20;
$h = $b*2 + 20 + PIE_3D_HEIGHT + ($n * 18);
$img = imagecreatetruecolor($w, $h);
// 背景白
$white = imagecolorallocate($img, 255, 255, 255);
imagefill($img, 0, 0, $white);
// 分配顏色
$colorIdx = [];
foreach ($colors as $c) {
$rgb = hexToRgb($c);
$colorIdx[] = imagecolorallocate($img, ...$rgb);
}
$black = imagecolorallocate($img, 0, 0, 0);
// 繪製扇區 (從 0 度開始)
$startDeg = 0;
$legendY = $b*2 + 20 + PIE_3D_HEIGHT + 10;
for ($i = 0; $i < $n; $i++) {
$endDeg = $startDeg + ($data[$i] / $sum) * 360;
$clr = $colorIdx[$i];
$darkClr = imagecolorallocate($img, ...darker(hexToRgb($colors[$i])));
// 3D 側面 (0-180 度可見)
if ($startDeg < 180) {
$visibleEnd = min($endDeg, 180);
$p1 = ellipsePoint($a, $b, $startDeg);
$p2 = ellipsePoint($a, $b, $visibleEnd);
// 左側邊
imageline($img, $cx+$p1[0], $cy+$p1[1], $cx+$p1[0], $cy+$p1[1]+PIE_3D_HEIGHT, $darkClr);
// 右側邊
imageline($img, $cx+$p2[0], $cy+$p2[1], $cx+$p2[0], $cy+$p2[1]+PIE_3D_HEIGHT, $darkClr);
// 底部弧
for ($d = $startDeg; $d < $visibleEnd; $d += ANGLE_STEP) {
$pt = ellipsePoint($a, $b, $d);
$ptNext = ellipsePoint($a, $b, min($d+ANGLE_STEP, $visibleEnd));
imageline($img, $cx+$pt[0], $cy+$pt[1]+PIE_3D_HEIGHT, $cx+$ptNext[0], $cy+$ptNext[1]+PIE_3D_HEIGHT, $darkClr);
}
// 填充側面 (簡易:中點填充)
$mid = ellipsePoint($a, $b, ($startDeg+$visibleEnd)/2);
imagefill($img, $cx+$mid[0], $cy+$mid[1]+PIE_3D_HEIGHT/2, $darkClr);
}
// 頂面扇區
imageline($img, $cx, $cy, $cx+$p1[0], $cy+$p1[1], $clr);
for ($d = $startDeg; $d < $endDeg; $d += ANGLE_STEP) {
$pt = ellipsePoint($a, $b, $d);
$ptNext = ellipsePoint($a, $b, min($d+ANGLE_STEP, $endDeg));
imageline($img, $cx+$pt[0], $cy+$pt[1], $cx+$ptNext[0], $cy+$ptNext[1], $clr);
}
imageline($img, $cx, $cy, $cx+$p2[0], $cy+$p2[1], $clr);
// 填充頂面
$midTop = ellipsePoint($a/2, $b/2, ($startDeg+$endDeg)/2);
imagefill($img, $cx+$midTop[0], $cy+$midTop[1], $clr);
// 圖例
$boxX = 10; $boxY = $legendY + $i * 18;
imagefilledrectangle($img, $boxX, $boxY, $boxX+12, $boxY+12, $clr);
imagerectangle($img, $boxX, $boxY, $boxX+12, $boxY+12, $black);
// 中文需 TrueType 字體
$font = __DIR__ . '/simsun.ttc'; // 請自備字體檔
if (file_exists($font)) {
imagettftext($img, 12, 0, $boxX+18, $boxY+12, $black, $font,
"{$labels[$i]}: {$data[$i]} (" . round($data[$i]/$sum*10000)/100 . "%)");
} else {
imagestring($img, 3, $boxX+18, $boxY+2, $labels[$i], $black);
}
$startDeg = $endDeg;
}
// 輸出
header('Content-Type: image/png');
imagepng($img);
imagedestroy($img);
使用方式
<img src="pie3d.php" alt="3D 餅圖">
// 或帶參數
<img src="pie3d.php?data=30,20,50&labels=A,B,C">
現代替代方案建議
- Chart.js / ApexCharts / ECharts:前端 JS 圖表庫、互動、動態、響應式、無伺服端負擔
- QuickChart.io:API 產生圖表圖片、URL 參數配置
- PHP 圖表庫:pChart、PHPChart、SVGGraph (輸出 SVG、可縮放)
- 大數據視覺化:Apache Superset、Metabase、Grafana
效能優化提示
- 啟用 OPcache、避免重複計算
- 靜態圖表預先產生快取
- 大量請求考慮前端渲染或 CDN 快取
- TrueType 字體載入開銷大,建議快取字體資源