PHP 使用 Imagick 绘制六芒星能力图及设置图片背景透明度

cuixiaogang

在开发霍兰德职业兴趣测试项目时,我们遇到了一个需求:用户需要将测试结果页长按保存为图片。最初,我们使用了 Echarts 插件来生成六芒星能力分析图,但当尝试用 html2canvas 将页面保存为图片时,发现 echarts 生成的图形无法成功保存。于是,我们决定用 PHP 后端来解决这个问题,最终选择了 Imagick 库来实现。

Imagick 的安装

为了使用 Imagick,需要先安装 ImageMagick 和对应的 PHP 扩展。以下是 Windows 和 Linux 系统的安装步骤:

Windows 安装

  • 下载 ImageMagick:根据系统版本选择合适的 VC 库版本,从 ImageMagick 下载。
  • 下载 PHP 扩展:从 PECL 官网 下载对应的 php_imagick.dll 文件。
  • 配置环境变量:将 ImageMagick 的 bin 目录添加到系统环境变量中。
  • 测试安装:打开命令行,输入 stream.ext -version,若显示版本信息,则安装成功。

ImageMagick安装

  • 配置 PHP:将 php_imagick.dll 文件复制到 PHP 的扩展目录下,并在 php.ini 文件中添加extension=imagick,重启服务后生效。

PHP扩展安装

Linux 安装(以 CentOS 为例)

  • 安装 ImageMagick:使用 yum 安装 ImageMagick 和 ImageMagick-devel(注意,ImageMagick-devel 是必须的,否则 PHP 扩展安装会报错)。
  • 测试安装:使用 convert -v 命令测试是否安装成功。

PHP扩展安装

  • 编译 PHP 扩展:从 PECL 下载源码包,使用 phpize 编译安装,并重启服务。

实现源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
<?php
/**
* +--------------------------------------------------------
* | 多边形能力分析图
* +--------------------------------------------------------
* | @author Renling
* +--------------------------------------------------------
* | @email gang888.2011@qq.com
* +--------------------------------------------------------
* | @date 22:14 2018/8/21
* +--------------------------------------------------------
* | @method
* | PolygonChart::setLevelColor($color) 设置辅助区域的颜色
* | $color array 5个颜色数据组成的数组 ,可参考PolygonChart::$levelFillColor
* | PolygonChart::setSize($size) 设置图片大小
* | PolygonChart::setNode($node) 设置边数
* | PolygonChart::setGuideColor($color) 设置辅助线颜色
* | PolygonChart::setMaxValue($color) 设置能力值
* | PolygonChart::setAbilityLineColor($color) 设置能力轮廓线的颜色
* | PolygonChart::setAbilityFillColor($color) 设置能力轮廓的填充颜色
* | PolygonChart::setBackgroundColor($color) 设置图片的背景颜色
* | PolygonChart::draw($data) 绘制图形
* | $data array 元素数量与边数对应
* +--------------------------------------------------------
*/
class PolygonChart
{
private $backgroundColor = 'rgba(255, 255, 255, 0)'; //设置图像的背景颜色
private $levelFillColor = [
'rgba(35, 135, 147, 0.5)',
'rgba(59, 162, 174, 0.5)',
'rgba(89, 182, 193, 0.5)',
'rgba(138, 206, 214, 0.5)',
'rgba(178, 226, 232, 0.5)'
]; //设置每个级别的多边形填充的颜色
private $sideLength = 200; //图片的边长=多边形外接圆直径
private $nodeNumber = 6; //节点的数量
private $guideColor = '#217882'; //辅助线的颜色
private $nodeMaxValue = 100; //每个节点所代表的最大值
private $abilityLineColor = '#aa604d'; //能力线的颜色
private $abilityFillColor = 'rgba(227, 144, 62, 0.5)'; //能力图的填充颜色
private $guideColorImagickPixel; //辅助线颜色资源
private $circumscribedCircleRadius; //正多边形外接圆的半径
private $points; //各点坐标存储
/**
* 设置图像的背景颜色
* @author Renling
* @date 17:22 2018/8/22
*
* @access public
* @param $color string 颜色值
*
* @return $this
*/
public function setBackgroundColor($color)
{
$this->backgroundColor = $color;
return $this;
}
/**
* 设置每个级别的多边形填充的颜色
* @author Renling
* @date 14:06 2018/8/22
*
* @access public
* @param $color array 每个级别的多边形填充的颜色
*
* @return $this
*/
public function setLevelColor($color)
{
$this->levelFillColor = $color;
return $this;
}
/**
* 设置图片大小
* @author Renling
* @date 14:08 2018/8/22
*
* @access public
* @param $size int 图片的大小
*
* @return $this
*/
public function setSize($size)
{
$this->sideLength = $size;
return $this;
}
/**
* 设置多边形的边数
* @author Renling
* @date 14:10 2018/8/22
*
* @access public
* @param $node int 多边形的节点数
*
* @return $this
*/
public function setNode($node)
{
$this->nodeNumber = $node;
return $this;
}
/**
* 设置辅助线颜色
* @author Renling
* @date 14:12 2018/8/22
*
* @access public
* @param $color string 颜色值
*
* @return $this
*/
public function setGuideColor($color)
{
$this->guideColor = $color;
return $this;
}
/**
* 设置每个能力的最大值
* @author Renling
* @date 14:13 2018/8/22
*
* @access public
* @param $value int 能力值
*
* @return $this
*/
public function setMaxValue($value)
{
$this->nodeMaxValue = $value;
return $this;
}
/**
* 设置能力轮廓线的颜色
* @author Renling
* @date 14:15 2018/8/22
*
* @access public
* @param $color string 颜色值
*
* @return $this
*/
public function setAbilityLineColor($color)
{
$this->abilityLineColor = $color;
return $this;
}
/**
* 设置能力轮廓的填充颜色
* @author Renling
* @date 14:17 2018/8/22
*
* @access public
* @param $color string 颜色值
*
* @return $this
*/
public function setAbilityFillColor($color)
{
$this->abilityFillColor = $color;
return $this;
}
/**
* 获取辅助线的颜色资源
* @author Renling
* @date 22:24 2018/8/21
*
* @access private
*
* @return ImagickPixel
*/
private function getGuideColor()
{
if (is_null($this->guideColorImagickPixel)) {
$this->guideColorImagickPixel = new ImagickPixel($this->guideColor);
}
return $this->guideColorImagickPixel;
}
/**
* 根据多变形等级绘制正多边形
* @author Renling
* @date 22:34 2018/8/21 0021
*
* @access private
* @param $level int 正多边形的等级 1-5,等级越高,多边形等级越大
*
* @return ImagickDraw
*/
private function drawPolygon($level = 5)
{
//根据正多边形的等级计算坐标中需要进行偏移的距离
$offsetLength = (5-$level)*$this->circumscribedCircleRadius/5;
//获取当前等级的外接圆半径
$levelCircumscribedCircleRadius = $level*$this->circumscribedCircleRadius/5;
//根据边数获取节点的坐标
$points = [];
//获取角度
$angle = 360/$this->nodeNumber;
//获取第一个点坐标
$points[] = ['x' => $levelCircumscribedCircleRadius+$offsetLength, 'y' => $offsetLength];
//当前临时变量为了优化代码,不做解释
$coordinateTmp = $levelCircumscribedCircleRadius+$offsetLength;
//遍历获取后面的sideNumber-1个节点的坐标
for ($i=1; $i<$this->nodeNumber; $i++) {
//当前临时变量为了优化代码,不做解释
$angleTmp = deg2rad($i*$angle);
$y = $coordinateTmp-round(cos($angleTmp)*$levelCircumscribedCircleRadius, 2);
$x = $coordinateTmp+round(sin($angleTmp)*$levelCircumscribedCircleRadius, 2);
$points[] = ['x' => $x, 'y' => $y];
}
$this->points[] = $points;
//获取辅助线的颜色资源
$guideColor = $this->getGuideColor();
//获取辅助图像的填充颜色
$fillColor = new ImagickPixel($this->levelFillColor[$level-1]);
//实例化ImagickDraw
$draw = new ImagickDraw();
//设置透明度
$draw->setStrokeOpacity(1);
//设置辅助线的颜色
$draw->setStrokeColor($guideColor);
//设置边的宽度
$draw->setStrokeWidth(1);
//设置辅助图形的填充颜色
$draw->setFillColor($fillColor);
//绘制正多边形
$draw->polygon($points);
return $draw;
}
/**
* 绘制中心点到最大多边形节点的连线
* @author Renling
* @date 17:17 2018/8/22
*
* @access private
*
* @return ImagickDraw
*/
public function drawCenterToMaxLine()
{
//绘制中心点到最大多边形节点的连线
$draw = new ImagickDraw();
//设置透明度
$draw->setStrokeOpacity(1);
//设置辅助线的颜色
$draw->setStrokeColor($this->getGuideColor());
//设置边的宽度
$draw->setStrokeWidth(1);
//获取最大外接圆的节点坐标
$points = $this->points[0];
foreach ($points as $nodeItem) {
$draw->line($this->circumscribedCircleRadius, $this->circumscribedCircleRadius, $nodeItem['x'], $nodeItem['y']);
}
return $draw;
}
/**
* 构建一个多边形的背景图
* @author Renling
* @date 22:26 2018/8/21
*
* @access private
*
* @return Imagick
*/
private function drawPolygonBkImage()
{
//构建图像资源
$image = new Imagick();
//对图像资源进行属性设置(宽 高 颜色)
$image->newImage($this->sideLength, $this->sideLength, new ImagickPixel($this->backgroundColor));
//设置图像资源图片格式(PNG)
$image->setImageFormat("png");
return $image;
}
/**
* 绘制能力图像
* @author Renling
* @date 22:57 2018/8/21 0021
*
* @access private
* @param $data array 能力数据
*
* @return ImagickDraw
*/
private function drawAbility($data)
{
if (!is_array($data)) {
return null;
}
//获取中心点坐标
$centerPoint = ['x' => $this->circumscribedCircleRadius, 'y' => $this->circumscribedCircleRadius];
//获取每个节点的最大外接圆的点坐标
$maxCircumscribedCirclePoints = $this->points[0];
//获取每项的值的节点坐标
$dataPoints = [];
foreach ($maxCircumscribedCirclePoints as $key => $maxCircumscribedCirclePointsItem) {
if (!isset($data[$key]))
$value = 0;
else
$value = $data[$key];
if ($value<0)
$value = 0;
elseif ($value>$this->nodeMaxValue)
$value = $this->nodeMaxValue;
$dataPoints[] = [
'x' => round($centerPoint['x'] + ($maxCircumscribedCirclePointsItem['x'] - $centerPoint['x'])*$value/$this->nodeMaxValue, 2),
'y' => round($centerPoint['y'] + ($maxCircumscribedCirclePointsItem['y'] - $centerPoint['y'])*$value/$this->nodeMaxValue, 2),
];
}
//构建一个多边形
$draw = new ImagickDraw();
//获取能力图形线的颜色资源
$abilityLineColor = new ImagickPixel($this->abilityLineColor);
//设置透明度
$draw->setStrokeOpacity(1);
//设置能力图形线的颜色
$draw->setStrokeColor($abilityLineColor);
//设置边的宽度
$draw->setStrokeWidth(1);
//设置填充颜色
$draw->setFillColor(new ImagickPixel($this->abilityFillColor));
//绘制能力多边形
$draw->polygon($dataPoints);
return $draw;
}
/**
* 自动制作一个正多边形能力图
* @author Renling
* @date 22:37 2018/8/21
*
* @access public
* @param $data array 能力数据
*
* @return Imagick
*/
public function draw($data)
{
//设置正多边形外接圆的半径
$this->circumscribedCircleRadius = $this->sideLength/2;
//构建一个图像资源
$image = $this->drawPolygonBkImage();
//从大到小的依次绘制5个正多边形
for ($level=5; $level>0; $level--) {
$draw = $this->drawPolygon($level);
$image->drawImage($draw);
unset($draw);
}
$image->drawImage($this->drawCenterToMaxLine());
//构建能力图形
//将能力图形绘制到图片上
$image->drawImage($this->drawAbility($data));
return $image;
}
}

效果展示

  • 第一个
1
2
3
4
5
6
<?php
require '../PolygonChart.php';
$chart = new PolygonChart;
$image = $chart->draw([58, 63, 44, 45, 75, 56]);
header('Content-Type:image/png');
echo $image->getImageBlob();

示例一

  • 第二个
1
2
3
4
5
6
<?php
require '../PolygonChart.php';
$chart = new PolygonChart;
$image = $chart->setBackgroundColor('#ff0000')->setNode(5)->draw([58, 63, 44, 45, 75]);
header('Content-Type:image/png');
echo $image->getImageBlob();

示例二

On this page
PHP 使用 Imagick 绘制六芒星能力图及设置图片背景透明度