Наша идея состояла в том, чтобы нарисовать диаграмму водопада с нерегулярной шириной. Мы достигли этого стиля диаграммы путем рендеринга прямоугольников в соответствующие точки данных (видимые в скрипке для демонстрационных целей).
Кроме того, мы хотим добавить всплывающую подсказку и сделать эту подсказку после мыши.Highcharts custom renderer chart и tooltip
Мы столкнулись с тремя проблем:
При масштабировании очень близко к графику, вы увидите, что именно rect2 и rect3, а также rect3 и rect4 показывают небольшие зазоры между краями прямоугольников. Это кажется странным, потому что все прямоугольники созданы одной и той же процедурой для цикла (строки 68-84 в скрипке). Есть идеи? (Если вы измените ширину диаграммы, зазоры могут исчезнуть или произойти между другими прямоугольниками ...)
Для первого и последнего прямоугольников мы хотели создать отдельные границы. Поэтому мы устанавливаем белые границы для первого и последнего прямоугольников (строки 97,155 в скрипке) и затем добавляем наши пути визуализации (пунктирные, сплошные) строки (строки 221-298). Как вы можете видеть, в случае rect0 вертикальная линия точно не покрывает белую границу, хотя мы использовали те же графические координаты, что и прямоугольная. (Если изменить ширину графика, величина проблемы становится лучше или еще хуже)
Мы оказаны таможенными подсказки к визуализатору группе (прямоугольники, dataLabels) и отображать их на наведении мышей и MouseOut событий. Первая проблема заключалась в том, что всплывающая подсказка исчезла при зависании dataLabel. Мы сделали обходное решение (строки 190-195), но нам интересно, есть ли более элегантный способ отображения всплывающей подсказки на обоих прямоугольниках и ярлыках. Кроме того, мы хотим сделать подсказку движением мыши (событие mousemove), но мы не можем заставить это событие работать над нашим примером.
$(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
});
});
});
Мы знаем, что это довольно сложный пример, но был бы признателен за все идеи, которые приходят к вам, ребята. Благодаря!
Это необходимо, чтобы эта ширина отличалась? С самого начала у вас есть несортированные данные, которые неверны для Highcharts, и когда сортировка применяется, я получаю разрушенную диаграмму. Разве вы не можете использовать простой водопад, поддерживаемый Highcharts?Смотрите: http://www.highcharts.com/demo/ waterfall –
Для этого типа диаграммы необходима разная ширина. Поэтому мы не можем использовать или изменять водопад HS, который основан на диаграмме столбцов. Вот почему мы попытались сделать прямоугольники для достижения какого-то водопада различной ширины ... – user2285607
Итак, в этом случае советую начать с сортировки данных и фиксации, а затем ваших исправлений. После этого, если вы создадите jsFiddle с этим, я постараюсь вам помочь. –