首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP GD 庫繪製扇形統計圖(餅圖)完整實現:從數據到圖片輸出

     編輯:PHP 相關知識

核心原理

利用 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()繪製多邊形用於畫圖例方框邊框

進階增強:抗鋸齒與 3D 效果

<?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/前端渲染

現代替代建議

  • 項目需交互/動畫/導出:前端 ECharts / Chart.js / ApexCharts,PHP 僅輸出 JSON
  • 需嵌入 PDF/郵件/靜態報表:本文 GD 方案或 ImageMagick (imagick) 矢量輸出
  • 高性能批量生成:Node.js + Canvas 或 Python + Matplotlib 異步處理

GD 繪製餅圖適合「無前端依賴、輕量級、靜態圖片輸出」的場景。掌握角度計算、填充邏輯、圖例佈局三大核心,即可按需定製專業統計圖表。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved