构建这个复杂图的最佳方法是啥

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】:

&lt;canvas&gt; 呢? 您可以轻松地绘制一个三角形,然后通过旋转画布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

以上是关于构建这个复杂图的最佳方法是啥的主要内容,如果未能解决你的问题,请参考以下文章

用 Angular 构建多语言和多方向网站的最佳方法是啥?

使用 Heroku 构建可扩展分析后端的最佳方法是啥? [关闭]

构建具有大量数据通信的系统的最佳方法是啥?

在构建淘汰赛 js 绑定时隐藏屏幕的最佳方法是啥?

在 iPhone 上构建“查找最近”位置感知应用程序的最佳方法是啥?

在多构建 Vue js 应用程序中定义多个配置的最佳方法是啥?