當(dāng)前公司在使用HighCharts來做圖表展示大量數(shù)據(jù). 其中HighStock是很好的展示時間軸圖表的工具. 最近一個需求是要求在圖表的Navigator上面拖動的時候顯示日期tooltip, 好讓用戶知道拖到了哪里. 在網(wǎng)上搜索找到了一個別人寫的三年前的highcharts插件庫, 已經(jīng)不能用了, 但是思路可以借鑒.
首先借鑒
- 首先把上面提到的人家寫的庫的代碼拿出來復(fù)制到自己的項(xiàng)目內(nèi)
- 你會發(fā)現(xiàn)很多變量已經(jīng)變了(Highcharts正常更新, 這個插件庫年久失修, 很多變量很多方法都發(fā)生了翻天覆地的變化), 把已經(jīng)不存在的變量修復(fù)成為當(dāng)前的變量
- 修改插件中定義的各種位置數(shù)值(很多位置不對, 要自己慢慢調(diào)整)
修改完成后的代碼
(function(H) {
const widthMap = {
left: 70,
right: 70,
center: 150,
}
/**
* Render tooltip into navigatorGroup
*
* @name renderTooltip
* @param {Scroller} scroller
* @param {String} position: position of tooltip [left, right, center]
* @param {String} str: string to initially display in tooltip
* @returns {undefined}
*/
const renderTooltip = function(scroller, position, str) {
const {
chart: { renderer },
} = scroller;
scroller[`${position}Tooltip`] = renderer.rect(0, 0, 95, 40, 3, 0).add(scroller.navigatorGroup);
scroller[`${position}TooltipText`] = renderer.text(str, 5, 15).add(scroller.navigatorGroup);
scroller[`${position}TooltipArrow`] = renderer
.path(['M', 0, 0, 'H', 10, 'L', 5, 5, 'L', 0, 0])
.add(scroller.navigatorGroup);
};
/**
* Fade in/out tooltip
*
* @name fade
* @param {String} direction: direction of fade [in, out]
* @returns {Function}
*/
const fade = direction => {
return function(scroller, position) {
const { [`${position}Tooltip`]: tooltip, [`${position}TooltipText`]: text, [`${position}TooltipArrow`]: tooltipArrow } = scroller;
const fadeClass = `fade-${direction}`;
tooltip.attr('class', fadeClass);
text.attr('class', fadeClass);
tooltipArrow.attr('class', fadeClass);
};
};
const fadeInTooltip = fade('in');
const fadeOutTooltip = fade('out');
const debounceFadeOut = _.debounce(fadeOutTooltip, 1000);
const tooltipPadding = 12;
/**
* Find the correct position for the tooltip and the tooltip's arrow
*
* @name findTooltipPosition
* @param {Scroller} scroller
* @param {String} position: position of tooltip [left, right, center]
* @param {String} tooltipWidth
* @returns {Object}
* @returns {Number} x
* @returns {Number} y
* @returns {Number} arrow
* @returns {Number} x
* @returns {Number} y
*/
const findTooltipPosition = function(scroller, position, tooltipWidth) {
const { size, left, range } = scroller;
const handleIndex = { left: 0, right: 1, center: 0 };
const handle = scroller.handles[handleIndex[position]];
const offset = (tooltipWidth + tooltipPadding * 2) / 2;
let x = handle.translateX - offset;
const arrow = {
x: handle.translateX - 5,
y: handle.translateY - 13,
};
if (position === 'center') {
x = handle.translateX + (range - tooltipWidth) / 2;
arrow.x = x + tooltipWidth / 2 - 5;
}
// tooltip is outside right edge of minimap
if (x + tooltipWidth > size) {
x = size - tooltipWidth;
arrow.x = size - 5;
}
// tooltip is outside of left edge of minimap
if (x < left) {
x = left;
arrow.x = x;
}
return { x, y: handle.translateY - 25, arrow };
};
const tooltipFill = THEME.fifth;
/**
* Adjust the position, width of the tooltip
*
* @name adjustTooltip
* @param {Scroller} scroller
* @param {String} position: position of tooltip [left, right, center]
* @param {String} str: string to initially display in tooltip
* @returns {undefined}
*/
const adjustTooltip = function(scroller, position, str) {
const { [`${position}TooltipArrow`]: arrow, [`${position}Tooltip`]: tooltip, [`${position}TooltipText`]: text } = scroller;
text.attr({ text: str });
// const { clientWidth: textWidth, clientHeight: textHeight } = text.element;
const textWidth = widthMap[position];
const textHeight = 14;
const {
x,
y,
arrow: { x: arrowX, y: arrowY },
} = findTooltipPosition(scroller, position, textWidth);
tooltip.attr({
width: textWidth + tooltipPadding * 2,
height: textHeight + tooltipPadding * 2,
fill: tooltipFill,
x,
y: y - textHeight - tooltipPadding,
});
text.attr({ text: str, x: x + tooltipPadding, y: y }).css({
color: THEME.primary,
fontSize: '14px',
});
arrow.attr({
d: ['M', arrowX, arrowY, 'H', arrowX + 10, 'L', arrowX + 5, arrowY + 5, 'L', arrowX, arrowY],
fill: tooltipFill,
});
};
/**
* Render the scroller as it changes through interaction
*
* @name render
* @param {Function} proceed: effectively `_super`
* @param {Number} min: min xAxis value of currently represented by the navigator
* @param {Number} max: max xAxis value of currently represented by the navigator
* @param {Number} pxMin: min pixel position of current navigator selection
* @param {Number} pxMax: max pixel position of current navigator selection
* @returns {undefined}
*/
H.wrap(H.Navigator.prototype, 'render', function(proceed, min, max, pxMin, pxMax) {
const [, ...args] = arguments;
proceed.call(this, ...args);
const {
navigatorOptions: { tooltipFormatter },
} = this;
if (!tooltipFormatter) return;
const { chart } = this;
const { dataMin, dataMax } = chart.xAxis[0];
const diff = dataMax - dataMin;
const unit = diff / chart.xAxis[0].width;
const range = { min: dataMin + unit * this.zoomedMin, max: dataMin + unit * this.zoomedMax };
const formattedTooltipText = tooltipFormatter(range.min, range.max, pxMin, pxMax);
formattedTooltipText.center = `${formattedTooltipText.left} - ${formattedTooltipText.right}`;
if (!this.tooltipRendered) {
renderTooltip(this, 'left', formattedTooltipText.left);
renderTooltip(this, 'right', formattedTooltipText.right);
renderTooltip(this, 'center', formattedTooltipText.center);
this.tooltipRendered = true;
}
if (!formattedTooltipText.left) return;
const positions = ['left', 'right', 'center'];
positions.forEach(position => {
const capitalizedPosition = position.charAt(0).toUpperCase() + position.slice(1);
const grabbed = this[`grabbed${capitalizedPosition}`];
if (grabbed) {
fadeInTooltip(this, position);
adjustTooltip(this, position, formattedTooltipText[position]);
debounceFadeOut(this, position);
} else {
fadeOutTooltip(this, position);
}
});
});
})(Highcharts);
然后應(yīng)用
進(jìn)行完上面的修改調(diào)整之后, 就可以在調(diào)用的地方使用啦
- 首先, 把上面經(jīng)過調(diào)整后的代碼粘貼到你要使用Highcharts的地方的上面
- 在你要用的地方這樣使用
navigator: {
height: 32,
margin: 20,
xAxis: {
labels: {
enabled: true,
},
},
tooltipFormatter: function(min, max) {
return { left: formatter.dateFormatter(min), right: formatter.dateFormatter(max) };
},
},
當(dāng)然你可以按照你自己的需要調(diào)整屬性數(shù)值
最后的效果

拉動左邊

拉動右邊

拉動中間