What method would be best to build this complex graph

前端 未结 5 1385
我寻月下人不归
我寻月下人不归 2021-02-03 23:28

After 15 years doing UI development, there\'s very little I look at and think, \"how on earth do I do that.\" This is one of those times.

A graphic designer has sold

相关标签:
5条回答
  • 2021-02-03 23:48

    If it absolutely must be as compatible as possible, I would generate an SVG image and render it to PNG. This is not nearly as slow as it sounds for an image like this with so few points.

    Here's a very quick, very dirty example. It assumes that you have the ImageMagick extension available though in a pinch you could dump the SVG to a file and exec() a command-line tool like rsvg. Obviously the "right" answer involves some sort of caching scheme for the rendered graph. Also, forgive me for not being more of a SVG ninja.

    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;
    

    Output looks like this:

    Rendered graph

    0 讨论(0)
  • 2021-02-04 00:03

    What about <canvas>? You can easily draw one triangle and then draw the others by just rotating the canvas 360/5 degrees.

    Example: 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();
        } 
    }
    

    Becomes:

    enter image description here

    Cons: Maybe not cross-browser yet.

    0 讨论(0)
  • 2021-02-04 00:04

    Unless I could find an implementation already written, I'd use Raphaël.

    It will take significant work, but the end result should be very good.

    Take a look at some of the demos, they're incredibly slick.

    Raphaël currently supports Firefox 3.0+, Safari 3.0+, Chrome 5.0+, Opera 9.5+ and Internet Explorer 6.0+.


    This seemed interesting, so I decided to implement it myself with Raphaël:

    See: http://jsfiddle.net/2Tsjy/

    It should work in "all browsers". The only part I didn't do was the text.

    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>
    
    0 讨论(0)
  • 2021-02-04 00:06

    I'd have to recommend RaphaelJS (see http://raphaeljs.com/). It is IE7 compatible, and you can do the triangles just fine: you need to do the math, but quite possible.

    EDIT: look at http://www.chittram.com/editor.jsp for a quick sample of some of the shapes that can be done. That site is an interactive editor but the core capabilities you need are demonstrated.

    0 讨论(0)
  • 2021-02-04 00:11

    I think if you have to do it in JS/CSS and Flash/HTML5 isn't an option, take a look at a handy trick of using triangles in CSS:

    http://www.howtocreate.co.uk/tutorials/css/slopes

    And an alternative reference:

    http://css-tricks.com/snippets/css/css-triangle/

    By setting the border thickness's of boxes cleverly, you can get any shape triangle you want, at any rotation. It's tricky to figure out, and I don't have code handy but it is possible.

    You could nest triangles within each other (looking the sample picture I note it is composed entirely of triangles, the inner triangle is an inverted nest of the outer triangle) so I think it's perfectly possible, although that maths might get a bit tricky in regards to positioning and if you need the chart to be flexible (arbitrary numbers of triangles and sizes).

    0 讨论(0)
提交回复
热议问题