先说一下本人在工作中遇到的瓶颈
之前做过一个【霍兰德职业兴趣测试】测试的项目,大体结构就是用户答题,系统根据用户回答的问题进行职业性格测试,最后显示结果,在结果页存在一个类似于六芒星的能力分析图(这个是我自己起的名字,具体是叫什么我也不太清楚,下面将以“六芒星图”代替),起初我们用的是Echarts插件,本来相安无事。突然有一天,客户提了一个需求,需要把结果页实现一个长按保存的功能,我们计划用html2canvas实现,这时发现页面中使用Echarts生成的六芒星图在利用html2canvas时不能成功保存为图片,前端的同事们束手无策,好吧,工作丢给了我们PHP,欲哭无泪。我在各种网站中找资料,发现没有任何现成的代码(可能是我没有找到),好吧,自己写。之前由于时间比较紧,没有时间考虑其他的东西,用的是GD库的函数,别提多苦逼了。现在把整理好的代码放上来供大家参考,希望能帮到同样问题的小伙伴。
使用条件
由于之前使用GD库的代码实现的,后来发现GD库过于麻烦,而且性能简直不敢恭维,所以,后来完善代码的使用的是Imagick,所以需要安装这个控件,这篇文章也会将Imagick的安装方法简单介绍,如果不懂的小伙伴可以在自行百度其他答案。
Windows安装
- ImageMagick下载地址(对照好VC库的版本)https://windows.php.net/downloads/pecl/deps/
- php扩展下载地址http://pecl.php.net/package/imagick
- 将ImageMagick的软件包下载好之后解压,然后将软件目录下的bin目录加入到环境变量中,不会设置环境变量的小伙伴请看这里
- 测试ImageMagick是否安装成功,打开CMD命令,输入下面的命令(stream.ext -version),如果出现了版本信息证明已经安装成功了。
- 将php的扩展包解压,然后将解压包中的php_imagick.dll文件及其他的.dll文件都复制到php目录的扩展控件目录下,修改配置文件,重启使之生效
Linux安装(以CentOS为例)
- 使用yum安装ImageMagick与ImageMagick-devel(也可以使用源码编译安装,不过依赖有些麻烦,有兴趣的小伙伴可以研究一下),注意ImageMagick-devel这个必须安装,否则在安装php扩展时会报错。
- 使用convert -v测试是否安装成功
- 同样使用windows中的那个php扩展的地址,下载.tar文件,使用phpize编译安装,然后重启服务(如果PHP不是源码安装的小伙伴可以自行百度)。
上代码
以下代码完全是本人自行编写,可能存在性能、资源上的不足,请各位大神多多指点,谢谢。
<?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;
}
}
实例展示
<?php
require '../PolygonChart.php';
$chart = new PolygonChart;
$image = $chart->draw([58, 63, 44, 45, 75, 56]);
header('Content-Type:image/png');
echo $image->getImageBlob();
效果图展示
<?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();
效果图展示
还有很多其他的设置,欢迎小伙伴们尝试,如果有不足的地方,希望小伙伴们能够指正,谢谢。