2013-11-15 5 views
3

우리의 아이디어는 불규칙한 너비의 폭포수 차트를 그리는 것이 었습니다. 직사각형을 해당 데이터 포인트 (데모 목적으로 피들에 표시)로 렌더링하여이 차트 스타일을 달성했습니다.
또한 툴팁을 추가하고이 툴팁을 마우스 다음에 만들고 싶습니다. 하이 차트 맞춤 렌더러 차트 및 툴팁

  1. 차트에 매우 가까이 확대

    , 당신은 그 즉 rect2 및 rect3뿐만 아니라 rect3 표시되고 rect4는 rectangulars 가장자리 사이의 작은 차이를 보여

    우리는 세 가지 문제에 직면하고있다. 이것은 모든 직사각형이 같은 for-loop 절차 (바이올린의 68-84 행)에 의해 생성 되었기 때문에 이상하게 보입니다. 어떤 아이디어? (차트 너비를 변경하면 간격이 사라지거나 다른 직사각형간에 발생할 수 있습니다 ...)

  2. 첫 번째 및 마지막 사각형의 경우 개별 테두리를 만들고 싶습니다. 따라서 첫 번째와 마지막 사각형 (흰돌의 97,155 행)에 흰색 테두리를 설정하고 나중에 렌더러 경로 (점선, 실선)를 추가합니다 (221-298 줄). rect0의 경우에서 볼 수 있듯이 직사각형과 동일한 플롯 좌표를 사용했지만 세로선은 흰색 테두리를 정확히 덮지 않습니다. 우리는 렌더러 그룹 (rectangulars, dataLabels)에 사용자 지정 도구 설명을 렌더링 및 마우스 오버와로 마우스 이벤트에 의해 이러한 표시

  3. (차트 폭을 변경하는 경우, 문제의 크기는 더 나은 또는 악화). 첫 번째 문제는 dataLabel을 가리키면 툴팁이 사라 졌다는 것입니다. 해결 방법을 만들었지 만 (190-195 줄) rect와 레이블 모두에 툴팁을 표시하는보다 세련된 방법이 있는지 궁금해하고 있습니다. 또한 툴팁이 마우스 움직임 (이벤트 mousemove)을 따르기를 원하지만이 이벤트가 우리의 예제에서 작동하도록 할 수는 없습니다.

    Here is our fiddle example

    $(function() { 
    
    
    var chart = new Highcharts.Chart({ 
        chart: { 
         renderTo: 'container', 
         type: 'scatter' 
        }, 
        title: { 
         text: 'Custom waterfall with unequal width' 
        }, 
        xAxis: { 
         min: 0, 
         max: 50, 
         reversed: true 
        }, 
        yAxis: { 
         title: { 
          text: 'Share' 
         }, 
         min: 0, 
         max: 100 
        }, 
        tooltip: { 
         enabled: false 
        }, 
        legend: { 
         enabled: false 
        }, 
        credits: { 
         enabled: false 
        }, 
        series: [{ 
         name: 'basicData', 
         visible: true, //for demonstration purpose 
         data: [ 
          [50, 40], 
          [45, 48], 
          [39, 52], 
          [33, 68], 
          [22, 75], 
          [15, 89], 
          [5, 100] 
         ] 
        }] 
    }, 
    //add function for custom renderer 
    function (chart) { 
    
        var points = this.series[0].data, 
         addMarginX = this.plotLeft, 
         addMarginY = this.plotTop, 
         xZero = this.series[0].points[0].plotX, 
         yZero = this.chartHeight - addMarginY - this.yAxis[0].bottom, 
         xAll = [], 
         yAll = [], 
         widthAll = [], 
         heightAll = []; 
    
        //renderer group for all rectangulars 
        rectGroup = chart.renderer.g() 
         .attr({ 
         zIndex: 5 
        }) 
         .add(); 
    
        //draw for each point a rectangular 
        for (var i = 0; i < points.length; i++) { 
    
         var x = points[i].plotX + addMarginX, 
          y = points[i].plotY + addMarginY, 
          width, 
          height; 
    
         if (i === 0) { //for the first rect height is defined by pixel difference of yAxis and yValue 
          height = yZero - points[i].plotY 
         } else { // else height is pixel difference of yValue and preceeding yValue 
          height = points[i - 1].plotY - points[i].plotY 
         }; 
         if (i === points.length - 1) { // for the last rectangular pixel difference of xValue and xAxis at point=0 
          width = this.xAxis[0].translate(0) - points[i].plotX 
         } else { // else pixel difference of xValue and subsequent xValue 
          width = points[i + 1].plotX - points[i].plotX 
         }; 
    
         xAll.push(x); 
         yAll.push(y); 
         widthAll.push(width); 
         heightAll.push(height); 
    
         //general styling of rects, exception for first and last rect 
         var attrOptions; 
         if (i === 0) { 
          attrOptions = { 
           id: i, 
            'stroke-width': 0.75, 
           stroke: 'rgb(255, 255, 255)', //white border which is later covered by dotted lines 
           fill: { 
            linearGradient: { 
             x1: 1, 
             y1: 0, 
             x2: 0, 
             y2: 0 
            }, 
            stops: [ 
             [0, Highcharts.getOptions().colors[0]], 
             [1, 'rgba(255,255,255,0.5)'] 
            ] 
           } 
          }; 
         } else if (i === points.length - 1) { 
          attrOptions = { 
           id: i, 
            'stroke-width': 0.75, 
           stroke: 'rgb(255, 255, 255)', //white border which is later covered by dotted lines 
           fill: { 
            linearGradient: { 
             x1: 0, 
             y1: 0, 
             x2: 1, 
             y2: 0 
            }, 
            stops: [ 
             [0, Highcharts.getOptions().colors[0]], 
             [1, 'rgba(255,255,255,0.5)'] 
            ] 
           } 
          }; 
         } else { 
          attrOptions = { 
           id: i, 
            'stroke-width': 0.75, 
           stroke: 'black', 
           fill: Highcharts.getOptions().colors[0] 
          }; 
         } 
    
         // draw rect, y-position is set to yAxis for animation 
         var tempRect = chart.renderer.rect(x, this.chartHeight - this.yAxis[0].bottom, width, 0, 0) 
          .attr(attrOptions) 
          .add(rectGroup); 
    
         //animate rect 
         tempRect.animate({ 
          y: y, 
          height: height 
    
         }, { 
          duration: 1000 
         }); 
        }; // for loop ends over all rect 
    
    
        //renderer centered dataLabels to rectangulars 
        for (var i = 0; i < points.length; i++) { 
    
         var labelColor = 'rgb(255,255,255)'; 
         if (i === 0 || i === points.length - 1) { 
          labelColor = '#666666' 
         } 
         var label = chart.renderer.label('rect' + i) 
          .attr({ 
          align: 'center', 
          zIndex: 5, 
          padding: 0 
         }) 
          .css({ 
          fontSize: '11px', 
          color: labelColor 
         }) 
          .add(rectGroup); 
    
         var labelBBox = label.getBBox(); 
    
         label.attr({ 
          x: xAll[i] + widthAll[i] * 0.5, 
          y: yAll[i] + heightAll[i] * 0.5 - labelBBox.height * 0.5 
         }); 
        }; // loop for dataLabels ends 
    
    
        // add tooltip to rectangulars AND labels (rectGroup) 
        var tooltipIndex; 
    
        rectGroup.on('mouseover', function (e) { 
    
         //get the active element (or is there a simpler way?) 
         var el = (e.target.correspondingUseElement) ? e.target.correspondingUseElement : e.target; 
    
         //determine with the 'id' to which dataPoint this element belongs 
         //problem: if label is hovered, use tootltipIndex of rect 
         var i = parseFloat(el.getAttribute('id')); 
         if (!isNaN(i)) { 
          tooltipIndex = i; 
         } 
         // render text for tooltip based on coordinates of rect 
         text = chart.renderer.text('This could be <br>an informative text', xAll[tooltipIndex], yAll[tooltipIndex] - 30) 
          .attr({ 
          zIndex: 101 
         }) 
          .add(); 
    
         var box = text.getBBox(); 
         //box surrounding the tool tip text      
         border = chart.renderer.rect(box.x - 5, box.y - 5, box.width + 10, box.height + 10, 5) 
          .attr({ 
          fill: 'rgba(255, 255, 255, 0.95)', 
          stroke: 'blue', 
           'stroke-width': 1, 
          zIndex: 100 
         }) 
          .add(); 
        }) 
         .on('mouseout', function() { 
    
         text.destroy(); 
         border.destroy(); 
        }) 
    
    
        //render first and last rect as open and partly dotted rect 
        var M = 'M', 
         L = 'L', 
         pathStartSol = [], 
         pathEndSol = [], 
         pathStartDot = [], 
         pathEndDot = [], 
         y0 = this.chartHeight - this.yAxis[0].bottom, 
         last = xAll.length - 1; 
    
        pathStartDot = [ 
        M, xAll[0], y0, 
        L, xAll[0] + widthAll[0] * 0.6, y0, 
        M, xAll[0], y0, 
        L, xAll[0] + widthAll[0] * 0.6, y0, 
        M, xAll[last] + widthAll[last] * 0.4, y0, 
        L, xAll[last] + widthAll[last], y0, 
        M, xAll[last] + widthAll[last] * 0.4, y0, 
        L, xAll[last] + widthAll[last], y0]; 
    
        pathStartSol = [ 
        M, xAll[0] + widthAll[0] * 0.6, y0, 
        L, xAll[1], y0, 
        L, xAll[1], y0, 
        L, xAll[0] + widthAll[0] * 0.6, y0, 
        M, xAll[last] + widthAll[last] * 0.4, y0, 
        L, xAll[last], y0, 
        L, xAll[last], y0, 
        L, xAll[last] + widthAll[last] * 0.4, y0]; 
    
        pathEndDot = [ 
        M, xAll[0], yAll[0], 
        L, xAll[0] + widthAll[0] * 0.6, yAll[0], 
        M, xAll[0], y0, 
        L, xAll[0] + widthAll[0] * 0.6, y0, 
        M, xAll[last] + widthAll[last] * 0.4, yAll[last], 
        L, xAll[last] + widthAll[last], yAll[last], 
        M, xAll[last] + widthAll[last] * 0.4, yAll[last - 1], 
        L, xAll[last] + widthAll[last], yAll[last - 1]]; 
    
        pathEndSol = [ 
        M, xAll[0] + widthAll[0] * 0.6, yAll[0], 
        L, xAll[1], yAll[0], // does not match exactly the underlying white border of rect 
        L, xAll[1], y0, // does not match exactly the underlying white border of rect 
        L, xAll[0] + widthAll[0] * 0.6, y0, 
        M, xAll[last] + widthAll[last] * 0.4, yAll[last], 
        L, xAll[last], yAll[last], 
        L, xAll[last], yAll[last - 1], 
        L, xAll[last] + widthAll[last] * 0.4, yAll[last - 1]]; 
    
        var pathSol = chart.renderer.path(pathStartSol) 
         .attr({ 
         'stroke-width': 1, 
         stroke: 'black', 
         zIndex: 100 
        }).add(); 
    
        var pathDot = chart.renderer.path(pathStartDot) 
         .attr({ 
         'stroke-width': 1, 
         stroke: 'black', 
         zIndex: 100, 
         dashstyle: 'Dot' 
        }).add(); 
    
        pathSol.animate({ 
         d: pathEndSol 
        }, { 
         duration: 1000 
        }); 
    
        pathDot.animate({ 
         d: pathEndDot 
        }, { 
         duration: 1000 
        }); 
    
    }); 
    
    }); 
    

우리는 그것이 다소 복잡한 예입니다하지만 너희들까지 오는 모든 아이디어 감사하겠습니다 알고있다. 감사!

+0

너비가 다른가 필요합니까? 아주 처음부터 당신은 Highcharts에 대해 잘못된 데이터를 분류하지 않았고 정렬을 적용 할 때 차트가 파괴되었습니다. Highcharts에서 지원하는 간단한 폭포를 사용할 수 없습니까?참조 : http://www.highcharts.com/demo/waterfall –

+0

다른 너비는이 종류의 차트에 필수적입니다. 따라서 기둥 형 차트를 기반으로 한 HS 폭포는 사용하거나 변경할 수 없습니다. 그래서 우리는 직사각형을 렌더링하여 폭이 다른 어떤 종류의 폭포를 만들려고 노력했습니다 ... – user2285607

+0

그래서이 경우 데이터 정렬과 직사각형 고정으로 시작하는 것이 좋습니다. 그 후 jsFiddle을 작성하면 도움을 드리겠습니다. –

답변

8

이제 우리는 작업 버전이 (들으 파블를!) :

  1. 문제 : 일부 rectangulars가 연결되지 않은; 솔루션 : 모든 plotX 및 plotY 좌표는 계산하기 전에 반올림해야합니다.

  2. 문제 : 개별 테두리와 직사각형의 missmatch; 해결 방법 : 다시 반올림하여 트릭을 만들었습니다.

  3. 문제 : a) 사용자 정의 렌더링 된 툴팁을위한 mousemove b) 레이블 및 직사각형을위한 호버 이벤트의 툴팁을 바인딩하십시오. 용액 : A)는 대신에, 각 장방형 인드 마우스 오버 이벤트 직사각형 B에 대한 마우스 오버 이벤트 데이터 포인트) (완전히 투명 고스트를 생성)을 대응 highcharts 툴팁 결합 관습 툴팁 생각 거부

    //draw ghost for each rectangular and bind tooltip of highcharts on it 
    for (var i = 0; i < points.length; i++) { 
    
        var ghostRect = chart.renderer.rect(xAll[i], yAll[i], widthAll[i], heightAll[i], 0) 
         .attr({ 
         id: i, 
         'stroke-width': 0, 
         stroke: 'rgba(255, 255, 255, 0)', 
         fill: 'rgba(255, 255, 255, 0)', 
         zIndex: 10 
        }) 
         .add() 
         .on('mouseover', function() { 
          var index = parseInt(this.getAttribute('id')); 
          var point = chart.series[0].points[index]; 
          chart.tooltip.refresh(point); 
        }) 
         .on('mouseout', function() { 
          chart.tooltip.hide(); 
        }); 
    
    
    }; 
    

    Here is the working fiddle

+0

잘 했어! 내 표를 가져라) –

+0

큰 재료. 그것은 내 자신의 '표면 형 차트'를 만드는 데 도움이되었습니다. 개략적 인 모형 : http://jsfiddle.net/kzoon/JvS3a/ – Koos