构建这个复杂图的最佳方法是啥
Posted
技术标签:
【中文标题】构建这个复杂图的最佳方法是啥【英文标题】:What method would be best to build this complex graph构建这个复杂图的最佳方法是什么 【发布时间】:2011-09-03 10:50:18 【问题描述】:在从事 UI 开发 15 年后,我很少看到并思考“我到底是怎么做到的”。这是其中之一。
一位平面设计师向我的客户出售了一个非常复杂的五边形图形,由 5 个独立的三角形组成。设计师已指定每个三角形应该是与品牌相匹配的特定颜色,并且每个三角形都应该根据每种颜色所代表的过程百分比来“填充”。您几乎必须看到图像才能理解:
我已经绞尽脑汁想弄清楚如何完成这项任务。客户已指定它必须兼容所有主流浏览器,为了理智,我将告诉他这将是 IE7+。这严重限制了 CSS3 技术,尽管由于缺乏其他想法,我肯定会接受 CSS3 方法。我不想在深夜敲打动作脚本,所以 Flash 在我的愿望清单的最底部。实际上,我已经集思广益了如何使用 Sprites 来实现它,但是生成 250 或 500 个三角形和相关 CSS 的想法在将 Chrome 换成 IE6 时排名靠前。
该网站建立在 php/mysql 之上,我们大量使用 Jquery。如有必要,我们也可以使用完整版的 FusionCharts 和 HighCharts。如果有可以实现这一目标的商业产品,我当然愿意购买它来完成这项工作。
完成这项艰巨任务的最佳方法是什么?
【问题讨论】:
+1 一个写得很好的问题,讨论了一个很容易(并且错误地)成为“gimme-codez”问题的主题的许多可能替代方案。谢谢你。 大声笑,设计师不能让客户用饼图解决吗? :D 相信我,我试过了。如果我在看到光之前不是一名平面设计师,可能有人受伤了...... @bpeterson76:我已经更新了我的答案以包含代码。我花了将近一个小时,所以我希望它有用。 @thirtydot:哇!很棒的工作...绝对有用的东西。 【参考方案1】:除非我能找到已经编写好的实现,否则我会使用Raphaël。
这需要大量工作,但最终结果应该非常好。
看看一些演示,它们非常漂亮。
Raphaël 目前支持 Firefox 3.0+、Safari 3.0+、Chrome 5.0+、Opera 9.5+ 和 Internet Explorer 6.0+。
这看起来很有趣,所以我决定自己和 Raphaël 一起实现它:
见:http://jsfiddle.net/2Tsjy/
它应该适用于“所有浏览器”。我唯一没有做的就是文字。
JavaScript:
var paper = Raphael("pentagon"),
fullNum = [40, 53],
borderColours = ['#329342','#9e202c','#f47933','#811f5a','#11496c'],
fillColours = ['#74ae3d','#d01f27','#eaa337','#32133f','#2c7aa1'],
triangles = [],
border, fill, st, i;
for (i=0; i<5; i++)
border = paper.path(getPercentPath(0)).attr(
'fill': borderColours[i],
'stroke-width': 0
),
fill = paper.path(["M", 116, 123] + "l-44,61 88,0z").attr(
'stroke': fillColours[i],
'stroke-width': 6
);
triangles.push(border);
st = paper.set();
st.push(border, fill);
st.rotate(i * 72, 116, 113);
setPercent(i, 30+Math.floor(Math.random()*70));
function getPercentPath(percent)
var ratio = percent/100;
return ["M", 116, 128] + "l-" + ratio*fullNum[0] + "," + ratio*fullNum[1] + " " + ratio*fullNum[0]*2 + ",0z";
function setPercent(i, percent)
triangles[i].attr(
path: getPercentPath(percent)
);
setInterval(function()
for (var i=0; i<5; i++)
setPercent(i, 30+Math.floor(Math.random()*70));
, 2000);
CSS:
#pentagon
width: 226px;
height: 227px;
border: 1px solid red;
background: #fff;
background: rgba(255,255,255,0.8)
HTML:
<div id="pentagon"></div>
【讨论】:
【参考方案2】:我不得不推荐 RaphaelJS(参见 http://raphaeljs.com/)。它是 IE7 兼容的,你可以很好地做三角形:你需要做数学,但很有可能。
编辑:查看http://www.chittram.com/editor.jsp 以获得一些可以完成的形状的快速示例。该网站是一个交互式编辑器,但展示了您需要的核心功能。
【讨论】:
【参考方案3】:<canvas>
呢?
您可以轻松地绘制一个三角形,然后通过旋转画布360/5
度来绘制其他三角形。
示例:http://jsfiddle.net/Stijntjhe/dC6kX/
window.onload = function()
var ce = document.getElementById('ce');
var c = ce.getContext('2d');
c.translate(ce.offsetWidth / 2, ce.offsetHeight / 2);
for(var pie = 0; pie < 5; pie++)
c.save();
c.rotate(pie/5 * Math.PI * 2);
c.beginPath();
c.moveTo(0, -10);
c.lineTo(-50, -80);
c.lineTo(50, -80);
c.lineTo(0, -10);
c.lineWidth = 5;
c.lineCap = 'square';
c.strokeStyle = colors[pie];
c.stroke();
c.restore();
变成:
缺点: 可能还没有跨浏览器。
【讨论】:
+1 很棒的小提琴。我没有很长时间来玩它,但作为一个快速而肮脏(而且有点不准确)的概念证明,我已经分叉了 Stijntjhe 的小提琴:jsfiddle.net/kXFQe。需要计算百分比以反映在三角形内,但我只是想我会用画布证明它是可能的。 它仍然需要一些数学,我现在只是猜到了绳子。 @Ben Stephenson:我猜是not cross-browser yet
。
如果您打算在 *** 上的每个有用答案上发布 cmets 并引用部分答案,那么祝您好运【参考方案4】:
如果它绝对必须尽可能兼容,我会生成一个 SVG 图像并将其渲染为 PNG。对于像这样只有这么少点的图像,这并不像听起来那么慢。
这是一个非常快速、非常肮脏的例子。它假定您有 ImageMagick 扩展可用,但在紧要关头您可以将 SVG 转储到文件和 exec()
像 rsvg 这样的命令行工具。显然,“正确”的答案涉及渲染图的某种缓存方案。另外,请原谅我不是一个 SVG 忍者。
graph.svg.php:
<?php echo '<'; ?>?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg
xmlns="http://www.w3.org/2000/svg" version="1.1">
<g transform="translate(120,120) translate(0,15)">
<polygon fill="none" stroke="green" stroke-
points="0,0 -50,68.82 50,68.82" />
<g transform="scale(<?php echo $fill['green']; ?>)">
<polygon fill="green" stroke="green" stroke-
points="0,0 -50,68.82 50,68.82" />
</g>
</g>
<g transform="translate(120,120) rotate(72) translate(0,15)">
<polygon fill="none" stroke="red" stroke-
points="0,0 -50,68.82 50,68.82" />
<g transform="scale(<?php echo $fill['red']; ?>)">
<polygon fill="red" stroke="red" stroke-
points="0,0 -50,68.82 50,68.82" />
</g>
</g>
<g transform="translate(120,120) rotate(144) translate(0,15)">
<polygon fill="none" stroke="yellow" stroke-
points="0,0 -50,68.82 50,68.82" />
<g transform="scale(<?php echo $fill['yellow']; ?>)">
<polygon fill="yellow" stroke="yellow" stroke-
points="0,0 -50,68.82 50,68.82" />
</g>
</g>
<g transform="translate(120,120) rotate(216) translate(0,15)">
<polygon fill="none" stroke="purple" stroke-
points="0,0 -50,68.82 50,68.82" />
<g transform="scale(<?php echo $fill['purple']; ?>)">
<polygon fill="purple" stroke="purple" stroke-
points="0,0 -50,68.82 50,68.82" />
</g>
</g>
<g transform="translate(120,120) rotate(288) translate(0,15)">
<polygon fill="none" stroke="blue" stroke-
points="0,0 -50,68.82 50,68.82" />
<g transform="scale(<?php echo $fill['blue']; ?>)">
<polygon fill="blue" stroke="blue" stroke-
points="0,0 -50,68.82 50,68.82" />
</g>
</g>
</svg>
render.php:
<?php
$fill = array(
'green' => 0.5,
'red' => 0.8,
'yellow' => 0.55,
'purple' => 0.4,
'blue' => 0.75,
);
ob_start();
include('graph.svg.php');
$svg = ob_get_contents();
ob_end_clean();
$im = new Imagick();
$im->readImageBlob($svg);
$im->setImageFormat('png24');
$png = $im->getImagesBlob();
$im->clear();
$im->destroy();
header('Content-Type: image/png');
header('Content-Length: ' . strlen($png));
echo $png;
exit;
输出如下所示:
【讨论】:
【参考方案5】:我认为如果您必须在 JS/CSS 中执行此操作并且 Flash/html5 不是一个选项,请看一下在 CSS 中使用三角形的一个方便技巧:
http://www.howtocreate.co.uk/tutorials/css/slopes
还有一个替代参考:
http://css-tricks.com/snippets/css/css-triangle/
通过巧妙地设置框的边框粗细,您可以在任意旋转时获得您想要的任意形状三角形。很难弄清楚,而且我手边没有代码,但这是可能的。
您可以将三角形相互嵌套(看示例图片,我注意到它完全由三角形组成,内部三角形是外部三角形的倒置嵌套)所以我认为这是完全可能的,尽管数学可能会有点在定位方面很棘手,如果您需要图表灵活(三角形和大小的任意数量)。
【讨论】:
这是一个值得选择的选项,但我只会在我试图避免使用 javascript 并将其保持为纯 HTML/CSS(加上服务器端语言来做边界数学)时才使用它。一旦你不得不使用 JavaScript,你还不如只使用 Raphaël 之类的库。 另一种制作三角形的方法是使用线性 css 渐变(但不确定浏览器是否支持):jlongster.com/s/dom3d以上是关于构建这个复杂图的最佳方法是啥的主要内容,如果未能解决你的问题,请参考以下文章
使用 Heroku 构建可扩展分析后端的最佳方法是啥? [关闭]