什么方法最适合构建这个复杂的图表?

26

做了15年UI开发后,很少有东西让我觉得,“天哪,我该怎么做。” 这就是其中之一。

一位平面设计师用五个三角形组成一个五边形的复杂图表,已经向我的客户出售。 设计师指定每个三角形应该是特定的颜色以匹配品牌,并且每个三角形应基于每种颜色所代表的进程百分比“填充”。 你几乎必须看到这张图片才能理解: enter image description here

我已经想了一整天如何完成这个任务。 客户已经指定它必须在所有主要浏览器中兼容,为了保持清醒,我会告诉他这将是IE7+。 这严重限制了CSS3技术,尽管由于缺乏其他想法,我肯定会考虑CSS3方法。 我不想熬夜研究Action Script,所以Flash在我的愿望清单上非常靠后。 我实际上已经构思了使用Sprites完成这个任务的方法,但是生成250或500个三角形及其相关的CSS的想法与将Chrome换成IE6的想法差不多。

网站构建在PHP / MySQL上,我们大量使用Jquery。 我们还可以使用FusionCharts和HighCharts的完整版本。 如果有商业产品可以实现这一点,我肯定愿意购买它以使其工作。

达成这个困难任务的最佳方法是什么?


7
感谢这篇写得非常好的问题,讨论了许多可能的替代方案,而这个话题本来很容易被误解为“帮我写代码”的问题。点赞 +1。谢谢。 - NickAldwin
哈哈,设计师不能让客户满足于一个饼图吗? :D - Denis de Bernardy
1
相信我,我尝试过了。如果在我看到光明之前我不是一名平面设计师,可能会有人受伤... - bpeterson76
看起来是IBM品牌的颜色...只是这么说。 - Dancrumb
1
@bpeterson76:我更新了我的答案,并且添加了代码。花了我将近一个小时,希望它有用。 - thirtydot
2
@thirtydot:哇!太棒了……绝对是有用的东西。 - bpeterson76
5个回答

27

如果我找不到一个已经写好的实现,我会使用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>

7

<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
为了一个很棒的小提琴点个赞。我还没有太多时间来玩它,但只是作为一个快速而肮脏(并且有些不准确)的概念验证,我已经fork了Stijntjhe的小提琴:http://jsfiddle.net/kXFQe/。百分比需要计算以在三角形内反映出来,但我想展示使用canvas是可能的。 - Dormouse
它仍需要一些数学,我现在只是猜测了坐标。 - seymar
1
@Ben Stephenson:我猜测这里指的是“尚未跨浏览器”。 - pimvdb
如果你打算在stackoverflow上对每个有帮助的答案都发表评论并引用部分答案,那么祝你好运。 - Dormouse

7
我推荐使用RaphaelJS(请参见http://raphaeljs.com/)。它与IE7兼容,并且您可以轻松地绘制三角形:您需要进行一些数学计算,但这是完全可能的。
编辑:请查看http://www.chittram.com/editor.jsp以获取一些可绘制形状的快速示例。该网站是一个交互式编辑器,但演示了您所需的核心功能。

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 width="240" height="240"
     xmlns="http://www.w3.org/2000/svg" version="1.1">

  <g transform="translate(120,120) translate(0,15)">
    <polygon fill="none" stroke="green" stroke-width="10" 
              points="0,0 -50,68.82 50,68.82" />
    <g transform="scale(<?php echo $fill['green']; ?>)">
      <polygon fill="green" stroke="green" stroke-width="10" 
                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-width="10" 
              points="0,0 -50,68.82 50,68.82" />
    <g transform="scale(<?php echo $fill['red']; ?>)">
      <polygon fill="red" stroke="red" stroke-width="10" 
                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-width="10" 
              points="0,0 -50,68.82 50,68.82" />
    <g transform="scale(<?php echo $fill['yellow']; ?>)">
      <polygon fill="yellow" stroke="yellow" stroke-width="10" 
                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-width="10" 
              points="0,0 -50,68.82 50,68.82" />
    <g transform="scale(<?php echo $fill['purple']; ?>)">
      <polygon fill="purple" stroke="purple" stroke-width="10" 
                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-width="10" 
              points="0,0 -50,68.82 50,68.82" />
    <g transform="scale(<?php echo $fill['blue']; ?>)">
      <polygon fill="blue" stroke="blue" stroke-width="10" 
                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;

输出结果如下:

Rendered graph


2
我认为如果你必须在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这样的库。 - thirtydot
另一种制作三角形的方法是使用线性CSS渐变(不确定浏览器是否支持):http://jlongster.com/s/dom3d/ - NickAldwin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接