How to dynamically set pie piece text colour on a C3 chart - javascript

I have a C3 pie chart similar to this online example. In my case I set the data.colors based on incoming external data.
My setup looks like the following (I pass in the colors to use)...
this.pieChart = generate({
data: {
columns: columns,
colors: colours,
type: 'pie',
onclick: (e) => {
this.handlePieClick(e.id);
},
},
bindto: '#pie-chart',
tooltip: {
show: false
},
transition: {
duration: 1000
},
legend: {
item: {
onclick: id => {
this.handlePieClick(id);
}
}
},
});
My app has not control over these colors. When the colors are light, the white text is hard to see. I may have a mixture of light and dark colours. What I need to do is examine each color and then be able to set the text either white or black for each pie piece text color, however I can see no callback function I can override to do this.
Does anyone know a way to do this? The big thing being I have no control over the colors coming in, and need to set the text colors in code once I have the color data.
Thanks in advance for any help!

Use the onrendered: option in the c3 configuration
In there, loop through the colours, and for each
Construct a new colour that shows up against the pie colour
Apply it to the text in the right segment using the class definitions (doable as the colour map and the pie segments element classes both contain the names of the data series)
http://jsfiddle.net/shxLfss3/2/
onrendered: function () {
var colEntries = d3.entries(this.config.data_colors);
colEntries.forEach (function (colEntry) {
// get pie segment colour, make a contrasting colour from it
var hsl = d3.hsl(colEntry.value);
hsl.l = hsl.l > 0.5 ? 0 : 1; // make black if light, white if dark
var newCol = hsl.toString();
// apply that color to the segmenbt's text
var segment = d3.select(this.config.bindto+" .c3-chart-arc.c3-target-"+colEntry.key);
segment.select("text").style("fill", newCol);
}, this);
}

Related

ChartJS different border color for legend

I need to set a different border color for the legend than the actual chart; whenever I add border color to the chart data, it gets applied to both legend and chart. And I couldn't find any options for setting different border colors for the legend in documentation
Here's a screenshot of what I'm trying to achieve, any idea?:
The only way I have found to do this and some other customisation on the legend is to implement the legend item interface.
The accepted answer here in combination with the legend item interface should be a good start.
Here's a jsfiddle that I made from that answer, but updated for chartjs 3.7.1. The main part to focus on is this:
options: {
plugins: {
legend: {
labels: {
usePointStyle: true,
generateLabels: function(chart) {
var data = chart.data;
if (data.labels.length && data.datasets.length) {
return data.labels.map(function(label, i) {
var meta = chart.getDatasetMeta(0);
var ds = data.datasets[0];
return {
text: label,
fillStyle: ds.backgroundColor[i],
strokeStyle: ds.legendOutline[i],
lineWidth: ds.borderWidth,
hidden: isNaN(ds.data[i]) || meta.data[i].hidden,
index: i
};
});
} else {
return [];
}
}
}
}
}
strokeStyle is the outline property, and I've made a field in the dataset called legendOutline. legendOutline and the data from the dataset are both referenced with i, so the legendOutline[i] points to the label representing data[i]

How to give a single line serie multiple colors with Tradingview Lightweight charts in Javascript?

This image below is what I have at this moment.
This image below is what I want. (1st image from my code, second from tradingview).
To be clear: I want a single line to have multiple colors. For my situation I need only two colors. I need each new line I create changable in color. Its a nice to have if the price tag on the right also changes to the color of the line that hits the right part of the chart.
This is my code:
var chart = LightweightCharts.createChart(document.getElementById("Chart"), {width: 1500, height: 700});
/**
* Creates startline and adds extra options to the lineSeries.
*/
function initChartSettings() {
// Init lines
priceArea = chart.addAreaSeries();
buySellLine = chart.addLineSeries(); //LINE THAT NEEDS TWO COLORS
// Set start position of lines
priceArea.setData([{time: '2019-04-11', value: startPrice}]);
buySellLine.setData([{time: '2019-04-11', value: startPrice}]);
// Visualization edits
priceArea.applyOptions({
topColor: 'rgba(70, 130, 180, 0.5)',
bottomColor: 'rgba(70, 130, 180, 0.1)',
lineColor: '#4682B4'
});
// Buy sell line
buySellLine.applyOptions({
color: '#42f54b', //CHANGES COLOR OF THE COMPLETE LINE, FROM BEGIN TO END.
priceLineVisible: true,
lastValueVisible: true
});
updateChartStatic();
}
/**
* Updates the chart its lines.
*/
function updateChartStatic() {
setTimeout(() => {
priceArea.update({
time: yearMonthDay, //How this works should not matter for question
value: newPrice, //Same as above
});
// Updates the price line of the chart.
buySellLine.update({
time: yearMonthDay,
value: currentMovingAverage //Even though it would be nice to do the below commented thing...
// color: 'red or green (example)'
});
buySellLine.applyOptions({
color: changeLineColor(currentMovingAverage, lastMovingAverage) // CHANGES COMPLETE LINE :(
});
}, 1);
}
the changeLineColor() function is doing an if statement, which has nothing to do with setting a color to a line.
EDIT: As this feature was landed in v3.8 you can simply add color properties to any of your data items by using the following properties in the data:
LineData.color to change a color of line data item
CandlestickData.color to change a body color of a candlestick item
CandlestickData.borderColor to change a border color of a candlestick item
CandlestickData.wickColor to change a wick color of a candlestick item
Note that you don't need to provide all the colors and you can provide only necessary ones (e.g. only body color for some candlestick items).
Original answer:
It's impossible right now, but there is issue for this feature https://github.com/tradingview/lightweight-charts/issues/195. You can subscribe on it to being notified when it'll be fixed (or even make a proposal and/or provide a PR with changed).

Contrast chart.js datalabels colors with the background

Using Chart.js qnd Angular.chart, I have a stacked chart in which I'm using different colors, I want to put the number of each value inside the cart, so I'm making use of the Plugin Datalabels.
The problem is that my chart looks like this:
Where I have the dark blue I'd like the numbers to be white. But don't know how to achieve this.
This is my code:
$scope.Options = {
...
plugins: {
datalabels: {
color: '#171d28',
anchor: 'center',
align: 'center',
clamp: true
}
}
};
I tried putting the colors in an array, like this:
$scope.Options = {
...
plugins: {
datalabels: {
//The first 2 colors as dark and the third one as white
color: ['#171d28', '#171d28', '#fff'],
anchor: 'center',
align: 'center',
clamp: true
}
}
};
But this way it apply the changes by bar! not by the color section.
It says in the datalabels page that you can add functions, but not very sure how: Link to datalabels page about color
Any help is welcome, Thanks in advance!
I've found the answer!based in the code from the link I put in the question.
You need the names of the values given in the Series. This is my Series array right now:
$scope.Series = ['Low Blue', 'Blue', 'Strong Blue'];
We need to add a function to the datalabels.color, and in the function we'll indicate how to color the labels of the ones corresponding to "Strong Blue":
plugins: {
datalabels: {
color: function(context) {
var index = context.dataIndex;
var value = context.dataset.data[index];
//Inside the "dataset" we look for the "labels" we are using
//and store them in a variable
var valueWhite = context.dataset.label;
//We make the condition: if a value of the label is "Strong blue"
//then we color this label 'white'
if(valueWhite === 'Strong Blue') {
return value = 'white';
} else {
//If it's any other label, we color it 'black'
return value = '#000';
}
}
}
This way all the sections with a Strong blue as background will have color white, and the rest will be black.

Set ECG paper-like grid intervals (highcharts.js)

I need to show an ECG result with highcharts which is basically a simple line/spline diagram but with a little tweak. The ECG paper looks like this: https://cdn.printablepaper.net/samples/ECG_Paper.png
And this is what I want to achieve with hightcharts too. One little square means 0.1 mV and 0.04 s, so one big square is 0.5 mV and 0.2 s. I know there is a tickInterval setting, but that just does not work in this scenario, I think the intervals are too small for it, because it randomly hides grid lines.
The optimal solution would be that if there is a need for hiding grid lines (too broad data), it only hides the small squares. To try not to confuse the users with this, we also would like to colorize grid lines differently, so every fifth line should be red, others gray.
Is this possible with highcharts?
minorTicks functionality seems to be ideal for this case. Use this option along with minorTickInterval. Configuration for both axes will look like this:
var axesOptions = {
tickInterval: 0.5,
minorTicks: true,
minorTickInterval: 0.1,
gridLineWidth: 1,
gridLineColor: 'red'
};
Live demo: http://jsfiddle.net/kkulig/6dbnmyxf/
If minor ticks are not visible on any axis this code will hide them on all axes (and restore them when needed):
chart: {
events: {
render: function() {
var axes = this.axes,
showMinorTicks = true;
// find if minor ticks are present on both axes
axes.forEach(function(a) {
console.log(a.minorTicks);
if (Object.keys(a.minorTicks).length === 0) {
showMinorTicks = false;
}
});
// hide/show ticks
axes.forEach(function(a) {
for (var key in a.minorTicks) {
var mt = a.minorTicks[key].gridLine;
showMinorTicks ? mt.show() : mt.hide();
}
});
}
}
},
API references:
https://api.highcharts.com/highcharts/xAxis.minorTickInterval
https://api.highcharts.com/highcharts/xAxis.minorTicks

How to style images in highcharts flags?

In my chart, I show the user pictures for their comment date, so the users can read the comments in a tooltip using these icons. It seems like soundcloud charts. I did it using flags type and I set the shape variables as user picture url for each data item.
But I want to add also colorized border for each icons. I know, there is lineWidth and lineColor config in flags type but it doesn't work with images, it works only with default shapes flag, circlepin or squarepin.
I tried to use shape as flag and to add user images as background image, but It doesn't work.
In SVG you can not set border for the images, you need to create rect around the image: http://jsfiddle.net/xuL76rkL/1/
For example you can use load and redraw events to manage those rects, code:
var H = Highcharts;
function drawBorder() {
var chart = this;
H.each(chart.series[1].points, function(p) {
var bbox,
rect,
fontSize = 13, // it's used as default font size offet, even for the images
dim = 32; //width and height for the image
if(p.graphic) {
if(p.rectangle) {
p.rectangle.destroy();
}
bbox = p.graphic.getBBox();
rect = chart.renderer.rect(p.plotX - dim/2, p.plotY - dim/2 - fontSize, dim, dim).attr({
"stroke": "black",
"stroke-width": 2,
"fill": "transparent"
}).add(chart.series[1].markerGroup);
p.rectangle = rect;
}
});
}
$('#container').highcharts('StockChart', {
chart: {
events: {
load: drawBorder,
redraw: drawBorder
}
},
...
});
You can set an image with the shape option:
Example: shape: "url(/assets/my_image.svg)"
Documentation available here: https://api.highcharts.com/highstock/series.flags.shape

Categories