I am trying to use Font Awesome icons as markers in HighCharts line chart. With help from fellow developers on Stack Overflow, I have managed to do that. One problem left is that now, whenever I hover over the markers and then leave hovering, they just move up a bit from the line and stay there forever. I really have no idea why.
This is the fiddle: https://jsfiddle.net/vf0g4u5k/12/. Appreciate any help.
The plug-in to use Font Awesome with HighCharts
(function (H) {
function symbolWrap(proceed, symbol, x, y, w, h, options) {
if (symbol.indexOf('text:') === 0) {
var text = symbol.split(':')[1],
svgElem = this.text(text, x, y + h)
.css({
fontFamily: '"Font Awesome 5 Free"',
fontSize: (h * 2 ) + "px"
});
if (svgElem.renderer.isVML) {
svgElem.fillSetter = function (value, key, element) {
element.style.color = H.Color(value).get('rgb');
};
}
return svgElem;
}
if (symbol.indexOf('textn:') === 0) {
var text = symbol.split(':')[1],
svgElem = this.text(text, x, y + h)
.css({
fontFamily: '"Font Awesome 5 Free"',
fontSize: (h * 2 ) + "px",
fontWeight: 900
});
if (svgElem.renderer.isVML) {
svgElem.fillSetter = function (value, key, element) {
element.style.color = H.Color(value).get('rgb');
};
}
return svgElem;
}
return proceed.apply(this, [].slice.call(arguments, 1));
}
H.wrap(H.SVGRenderer.prototype, 'symbol', symbolWrap);
if (H.VMLRenderer) {
H.wrap(H.VMLRenderer.prototype, 'symbol', symbolWrap);
}
// Load the font for SVG files also
H.wrap(H.Chart.prototype, 'getSVG', function (proceed) {
var svg = proceed.call(this);
svg = '<?xml-stylesheet type="text/css" ' +
'href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" ?>' +
svg;
return svg;
});
}(Highcharts));
I ended up doing like this. Posting in case it may help someone else.
point: {
events: {
mouseOut: function() {
var index = this.index
for( var series of this.series.chart.series){
for(var j=0;j<series.points.length;j++){
if(j === index && series.points[j].graphic){series.points[j].graphic.attr({'translateY': 8})}
}
};
}
}
}
You can adjust your custom marker settings by setting translateX and translateY like this
var text = symbol.split(':')[1],
svgElem = this.text(text, x, y)
.attr({
translateY: h,
translateX: -1
})
.css({
fontFamily: '"Font Awesome 5 Free"',
fontSize: (h * 2 ) + "px"
});
Related
I am using the highcharts export server node module to export treemap charts. I'm trying to create a custom layoutAlgorithm. I already have this working on the client side, but when I move the code to the server I get errors. Here's some example code:
import * as exporter from 'highcharts-export-server';
import Highcharts from 'highcharts';
function quadrants(parent, children) {
let width = parent.width / 2;
let height = parent.height / 2;
let areas = [
{
x: parent.x + width,
y: parent.y,
width,
height,
},
{
x: parent.x,
y: parent.y,
width,
height,
},
{
x: parent.x,
y: parent.y + height,
width,
height,
},
{
x: parent.x + width,
y: parent.y + height,
width,
height,
},
];
for (let i = 0; i < children.length; i++) {
areas.push();
}
return areas;
}
// This is the problem line
Highcharts.seriesTypes.treemap.prototype.quadrants = quadrants;
const exportSettings = {
// ...
series: [
type: 'treemap',
levels: [{
level: 1,
layoutAlgorithm: 'quadrants',
}],
],
// ...
}
return new Promise((resolve, reject) => {
exporter.export(exportSettings, (err, res) => {
if (err) reject(err);
resolve(res);
});
});
When I run this code I get the following error
TypeError: Cannot read property 'treemap' of undefined
Is this the incorrect way to set the layout algorithm or is there another way to set a layout algorithm with the highcharts export server?
Thanks
I am using horizontal bar chart with continuous update of series data. This is achieved successfully, but now i want these series to be sorted on data (continuously with every update of series) in desc order with animation. I mean when a bar get max value then move it to the top with animation.
how can i achieve this?
That type of functionality is not supported by default in Highcharts. Below you can find an example that shows how you can achieve the wanted result by custom code:
var options = {
chart: {
type: 'bar'
},
xAxis: {
categories: ['Cat1', 'Cat2', 'Cat3'],
},
series: [{
data: [1000, 900, 800]
}]
};
var chart = Highcharts.chart('container', options);
// Add custom data labels
chart.series[0].points.forEach(function(point, i) {
var x = chart.plotWidth - point.plotY + chart.plotLeft,
y = chart.xAxis[0].ticks[i].label.xy.y;
point.customDataLabel = chart.renderer.text(
point.y,
x,
y
)
.css({
color: '#000000',
fontSize: '14px'
})
.attr({
zIndex: 3
})
.add();
setAlign(point.customDataLabel);
});
function setAlign(label, xPos) {
var align = 'left',
bbox = label.getBBox();
if (chart.chartWidth < (xPos ? xPos : bbox.x + bbox.width) + 50) {
align = 'right';
}
label.attr({
align: align
})
}
var update = function() {
var points = chart.series[0].points;
chart.series[0].setData([Math.round(Math.random() * 1000), Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)]);
};
var rotate = function() {
var points = chart.series[0].points,
labelX,
ticks = chart.xAxis[0].ticks;
var sortedPoints = points.slice();
sortedPoints.sort(function(a, b) {
return b.y - a.y;
});
points.forEach(function(point, i) {
sortedPoints.forEach(function(sPoint, j) {
if (point === sPoint) {
labelX = chart.plotWidth - points[i].plotY + chart.plotLeft;
// Animate the column
points[i].graphic.animate({
x: points[j].shapeArgs.x
});
// Animate the label
points[i].customDataLabel.attr({
text: points[i].y
}).animate({
y: ticks[j].label.xy.y,
x: labelX
});
setAlign(points[i].customDataLabel, labelX);
// Animate the axis label
ticks[i].label.animate({
y: ticks[j].label.xy.y
});
}
});
});
};
document.getElementById("button").addEventListener("click", function() {
update();
rotate();
}, false);
Live demo: https://jsfiddle.net/BlackLabel/mg5bv3s8/
API Reference: https://api.highcharts.com/class-reference/Highcharts.SVGElement#animate
I'm trying to change some things in the graphic "3D scatter chart Draggable Box"
http://www.highcharts.com/demo/3d-scatter-draggable/grid-light
Is possible to add a title to an axis, but that it rotates when I turn the box?
Now, if i add a title in this way:
yAxis: {
title: {
text: "Latitude"
}
},
When i rotate the box, the title doesn't follow the yAxis....
This is the code to rotate the chart:
$(chart.container).bind('mousedown.hc touchstart.hc', function (e) {
e = chart.pointer.normalize(e);
var posX = e.pageX,
posY = e.pageY,
alpha = chart.options.chart.options3d.alpha,
beta = chart.options.chart.options3d.beta,
newAlpha,
newBeta,
sensitivity = 5; // lower is more sensitive
$(document).bind({
'mousemove.hc touchdrag.hc': function (e) {
// Run beta
newBeta = beta + (posX - e.pageX) / sensitivity;
newBeta = Math.min(100, Math.max(-100, newBeta));
chart.options.chart.options3d.beta = newBeta;
// Run alpha
newAlpha = alpha + (e.pageY - posY) / sensitivity;
newAlpha = Math.min(100, Math.max(-100, newAlpha));
chart.options.chart.options3d.alpha = newAlpha;
window.alphaOn3dGraph = newAlpha;
window.betaOn3dGraph = newBeta;
chart.redraw(false);
},
'mouseup touchend': function () {
$(document).unbind('.hc');
}
});
});
Is it possible to omit the vertical lines from the "square wave" line? I think you could call that a level line, here's an illustration:
The easiest way to achieve this look is to use a scatter chart with a custom "line" symbol:
// define a custom line symbol
Highcharts.SVGRenderer.prototype.symbols.line = function (x, y, w, h) {
return ['M', x, y, 'L', x - w * 2, y, 'M', x, y, 'L', x + w * 2, y, 'z'];
};
if (Highcharts.VMLRenderer) {
Highcharts.VMLRenderer.prototype.symbols.cross = Highcharts.SVGRenderer.prototype.symbols.cross;
}
$('#container').highcharts({
chart: {
type: 'scatter'
},
title: {
text: 'Look At Lines!!'
},
series: [{
name: 'Line Symbol',
data: [54.4, 29.9, {y: 129.2, radius: 8}, 144.0, 176.0, 135.6],
marker: {
symbol: 'line',
lineColor: null,
lineWidth: 2
}
}]
});
Note that you can adjust the length of an individual point by upping the radius.
Fiddle here.
Produces:
While above answer works fine, the issue I faced was that width of the line symbol is not the same as column width when displayed in combination with a column chart.
example image
Another option is to extend column type chart in a new chart type like so:
$(function () {
(function (H) {
var each = H.each,
pick = H.pick,
Series = H.Series,
seriesType = H.seriesType;
seriesType('floatingStep', 'column', {
fixedPointLength: 2
}, /** #lends seriesTypes.floatingStep.prototype */ {
/**
* Translate each point to the plot area coordinate system and find shape positions
*/
translate: function () {
var series = this,
chart = series.chart,
options = series.options,
dense = series.dense = series.closestPointRange * series.xAxis.transA < 2,
borderWidth = series.borderWidth = pick(
options.borderWidth,
dense ? 0 : 1 // #3635
),
yAxis = series.yAxis,
threshold = options.threshold,
translatedThreshold = series.translatedThreshold = yAxis.getThreshold(threshold),
minPointLength = pick(options.minPointLength, 5),
fixedPointLength = options.fixedPointLength,
metrics = series.getColumnMetrics(),
pointWidth = metrics.width,
seriesBarW = series.barW = Math.max(pointWidth, 1 + 2 * borderWidth), // postprocessed for border width
pointXOffset = series.pointXOffset = metrics.offset;
if (chart.inverted) {
translatedThreshold -= 0.5; // #3355
}
// When the pointPadding is 0, we want the columns to be packed tightly, so we allow individual
// columns to have individual sizes. When pointPadding is greater, we strive for equal-width
// columns (#2694).
if (options.pointPadding) {
seriesBarW = Math.ceil(seriesBarW);
}
Series.prototype.translate.apply(series);
// Record the new values
each(series.points, function (point) {
var yBottom = pick(point.yBottom, translatedThreshold),
safeDistance = 999 + Math.abs(yBottom),
plotY = Math.min(Math.max(-safeDistance, point.plotY), yAxis.len + safeDistance), // Don't draw too far outside plot area (#1303, #2241, #4264)
barX = point.plotX + pointXOffset,
barW = seriesBarW,
barY = Math.min(plotY, yBottom),
up,
barH = Math.max(plotY, yBottom) - barY;
// Handle options.minPointLength
if (Math.abs(barH) < minPointLength) {
if (minPointLength) {
barH = minPointLength;
up = (!yAxis.reversed && !point.negative) || (yAxis.reversed && point.negative);
barY = Math.abs(barY - translatedThreshold) > minPointLength ? // stacked
yBottom - minPointLength : // keep position
translatedThreshold - (up ? minPointLength : 0); // #1485, #4051
}
}
if (fixedPointLength) {
barH = fixedPointLength;
}
// Cache for access in polar
point.barX = barX;
point.pointWidth = pointWidth;
// Fix the tooltip on center of grouped columns (#1216, #424, #3648)
point.tooltipPos = chart.inverted ? [yAxis.len + yAxis.pos - chart.plotLeft - plotY, series.xAxis.len - barX - barW / 2, barH] : [barX + barW / 2, plotY + yAxis.pos - chart.plotTop, barH];
// Register shape type and arguments to be used in drawPoints
point.shapeType = 'rect';
point.shapeArgs = series.crispCol.apply(
series,
point.isNull ?
// #3169, drilldown from null must have a position to work from
// #6585, dataLabel should be placed on xAxis, not floating in the middle of the chart
[barX, translatedThreshold, barW, 0] : [barX, barY, barW, barH]
);
});
},
});
})(Highcharts);
});
View in JS Fiddle
I have the following issue:
I initialize the chart with null values because I don't have meaningful initial values.
I add points with addPoint after getting the data through ajax
The problem is that the chart shifts continuously and not just on the end of the time range.
I use the code:
generateInitialData = function() {
var initialData = [];
var time = (new Date()).getTime();
var i;
for (i = -99; i <= 0; i++) {
initialData.push([time + i * 3000,null]);
}
return initialData;
}
addPoint = function(){
var max = 1000;
var min = 300;
var value = Math.floor(Math.random() * (max - min + 1)) + min;
chart.series[0].addPoint([(new Date()).getTime(), value], true, true);
}
$('#container').highcharts({
chart: {
type: 'line',
},
xAxis: {
type: 'datetime',
minRange:1000 * 60 * 5,
maxRange:1000 * 60 * 5
}
});
var chart = $('#container').highcharts();
chart.addSeries({"name": "example","data":generateInitialData()});
setInterval(addPoint,3000);
Please see jsFiddle demo of the issue :
http://jsfiddle.net/wXmLQ/7/
Please help
Tali
You don't need to set null data from the beginning, instead of that you can just addd one empty point and just check if you want to shift or not, see: http://jsfiddle.net/Fusher/wXmLQ/8/
addPoint = function(){
var max = 1000;
var min = 300;
var value = Math.floor(Math.random() * (max - min + 1)) + min;
var len = chart.series[0].data.length;
chart.series[0].addPoint([(new Date()).getTime(), value], len < 99);
}
$('#container').highcharts({
chart: {
type: 'line',
},
xAxis: {
type: 'datetime',
minTickInterval: 1000 * 60, //one minute
minRange:1000 * 60 * 5,
maxRange:1000 * 60 * 5
}
});
var chart = $('#container').highcharts();
chart.addSeries({"name": "example","data":[[+new Date(), null]]});
setInterval(addPoint,3000);
The addPoint and addSeries methods have a re-draw parameter that defaults to true. Make this parameter false whenever you call an updating method
chart.series[0].addPoint([(new Date()).getTime(), value], false);
chart.addSeries({"name": "example","data":generateInitialData()}, false);
Then once you've made all the updates, explicitly redraw the chart
chart.redraw();