利用 GD 庫繪製圓弧(imagearc)、半徑線(imageline)、填充扇區(imagefilltoborder),將數值比例轉換為角度(360° × 值/總和)。
<?php
/**
* 生成扇形統計圖並直接輸出 PNG
* @param array $data 數據鍵值對 ['標籤' => 數值, ...]
* @param int $diameter 圖表直徑(像素)
* @param array $colors 十六進制顏色陣列 ['#FF0000', '#00FF00', ...]
* @param string $title 圖表標題
*/
function renderPieChart(array $data, int $diameter = 200, array $colors = [], string $title = ''): void {
// 1. 參數校驗與預設值
if (empty($data)) return;
$total = array_sum($data);
if ($total <= 0) return;
$defaultColors = [
'#FF6B6B', '#4ECDC4', '#45B7D1', '#FFBE0B', '#FB5607',
'#8338EC', '#3A86FF', '#06D6A0', '#118AB2', '#073B4C'
];
$colors = $colors ?: $defaultColors;
// 2. 畫布準備
$padding = 20; // 邊距留白(含標題空間)
$width = $diameter + $padding * 2;
$height = $diameter + $padding * 2 + ($title ? 30 : 0);
$img = imagecreatetruecolor($width, $height); // 真彩色模式
// 3. 顏色分配
$bgColor = imagecolorallocate($img, 255, 255, 255); // 白底
$textColor = imagecolorallocate($img, 51, 51, 51); // 深灰文字
$borderColor = imagecolorallocate($img, 0, 0, 0); // 黑邊框
$allocatedColors = [];
foreach ($colors as $hex) {
$hex = ltrim($hex, '#');
$r = hexdec(substr($hex, 0, 2));
$g = hexdec(substr($hex, 2, 2));
$b = hexdec(substr($hex, 4, 2));
$allocatedColors[] = imagecolorallocate($img, $r, $g, $b);
}
imagefill($img, 0, 0, $bgColor); // 填充背景
// 4. 圓心座標
$cx = $width / 2;
$cy = ($title ? $padding + 25 : $padding) + $diameter / 2;
$radius = $diameter / 2;
// 5. 繪製標題
if ($title) {
$font = 5; // 內置字體 1-5
$titleWidth = imagefontwidth($font) * strlen($title);
imagestring($img, $font, $cx - $titleWidth / 2, $padding, $title, $textColor);
}
// 6. 繪製扇區
$startAngle = -90; // 從 12 點鐘方向開始(-90°)
$legendData = []; // 圖例數據 [label, value, color, percent]
foreach ($data as $label => $value) {
$angle = 360 * $value / $total;
$endAngle = $startAngle + $angle;
$color = $allocatedColors[array_key_first($data) % count($allocatedColors)];
$colorIdx = array_search($color, $allocatedColors); // 獲取索引
// 繪製圓弧邊框
imagearc($img, $cx, $cy, $diameter, $diameter, $startAngle, $endAngle, $borderColor);
// 繪製兩條半徑線
$x1 = $cx + $radius * cos(deg2rad($startAngle));
$y1 = $cy + $radius * sin(deg2rad($startAngle));
imageline($img, $cx, $cy, $x1, $y1, $borderColor);
$x2 = $cx + $radius * cos(deg2rad($endAngle));
$y2 = $cy + $radius * sin(deg2rad($endAngle));
imageline($img, $cx, $cy, $x2, $y2, $borderColor);
// 填充扇區(從中點向邊界填充)
$midAngle = $startAngle + $angle / 2;
$fillX = $cx + ($radius * 0.5) * cos(deg2rad($midAngle));
$fillY = $cy + ($radius * 0.5) * sin(deg2rad($midAngle));
imagefilltoborder($img, $fillX, $fillY, $borderColor, $color);
// 記錄圖例信息
$legendData[] = [
'label' => $label,
'value' => $value,
'color' => $color,
'percent' => round(100 * $value / $total, 1)
];
$startAngle = $endAngle;
}
// 7. 繪製圖例(右側垂直排列)
$legendX = $cx + $radius + 15;
$legendY = $cy - (count($legendData) * 18) / 2;
$boxSize = 12;
foreach ($legendData as $item) {
// 顏色方塊
imagefilledrectangle($img, $legendX, $legendY, $legendX + $boxSize, $legendY + $boxSize, $item['color']);
imagepolygon($img, [$legendX, $legendY, $legendX+$boxSize, $legendY, $legendX+$boxSize, $legendY+$boxSize, $legendX, $legendY+$boxSize], 4, $borderColor);
// 文字
$text = "{$item['label']} {$item['value']} ({$item['percent']}%)";
imagestring($img, 3, $legendX + $boxSize + 5, $legendY - 1, $text, $textColor);
$legendY += 20;
}
// 8. 輸出
header('Content-Type: image/png');
imagepng($img);
imagedestroy($img);
}
// ========== 使用示例 ==========
// 直接輸出圖片
renderPieChart([
'直接訪問' => 335,
'郵件營銷' => 310,
'聯盟廣告' => 234,
'視頻廣告' => 135,
'搜索引擎' => 1548
], 240, ['#5470C6', '#91CC75', '#FAC858', '#EE6666', '#73C0DE'], '流量來源分布');
// 或保存文件
function savePieChart(array $data, string $path, int $d = 200, array $c = [], string $t = ''): bool {
ob_start();
renderPieChart($data, $d, $c, $t);
$png = ob_get_clean();
return file_put_contents($path, $png) !== false;
}
savePieChart(['A'=>30, 'B'=>70], __DIR__.'/pie.png', 150);
?>
| 函數 | 作用 | 關鍵參數 |
|---|---|---|
imagecreatetruecolor() | 創建真彩色畫布 | 避免調色板模式顏色失真 |
imagearc() | 繪製橢圓弧 | 角度:0°=3點鐘,逆時針 |
imageline() | 繪製直線 | 連接圓心到弧端點 |
imagefilltoborder() | 邊界填充 | 從內部點向邊框色填充 |
deg2rad() | 角度轉弧度 | 三角函數需弧度制 |
imagepolygon() | 繪製多邊形 | 用於畫圖例方框邊框 |
<?php
// 啟用抗鋸齒(需 GD 2.0.1+)
imageantialias($img, true);
// 3D 立體效果:繪製底部陰影扇區
function draw3DPie($img, $cx, $cy, $radius, $start, $end, $color, $depth = 15) {
// 繪製側面(多邊形近似)
$points = [];
for ($a = $start; $a <= $end; $a += 2) {
$points[] = $cx + $radius * cos(deg2rad($a));
$points[] = $cy + $radius * sin(deg2rad($a)) + $depth;
}
// 封閉路徑...
// 此處省略完整實現,核心思路:側面用深色系填充
}
?>
| 問題 | 原因 | 解決 |
|---|---|---|
| 扇區邊界鋸齒明顯 | GD 繪圖無抗鋸齒 | imageantialias($img, true) 或放大 2 倍後縮小 |
| 小扇區標籤重疊 | 角度過小文字擠壓 | 合併 <5% 項為「其他」;或改用圖例 |
| 顏色不夠用 | 數據項超過預設色 | 動態生成 HSL 色相均勻分布色系 |
| 中文標籤亂碼 | 內置字體無中文 | 用 imagettftext() + TTF 字體文件 |
| 大數據量生成慢 | 循環內重複計算三角函數 | 預計算 sin/cos 表;或改用 SVG/前端渲染 |
GD 繪製餅圖適合「無前端依賴、輕量級、靜態圖片輸出」的場景。掌握角度計算、填充邏輯、圖例佈局三大核心,即可按需定製專業統計圖表。