Forcing tooltip position on Amcharts - javascript

In short, I would like to force tooltip to be shown always above the hovered point on line series, even if it goes outside chart area. Tooltip is joint for all series. Example can be seen here:
const {useRef, useState} = React;
const CHART_CONTAINER = 'campaign-budget-chart';
const CHART_COLORS = {
value1: '#05a8fa',
value2: '#ed3434',
value3: '#0ec76a',
}
function getRandomNumber(max){
return Math.floor(Math.random() * Math.floor(max))
}
const initialData = [{
timestamp: new Date(2020, 09, 25),
value1: 0,
value2: getRandomNumber(50),
value3: getRandomNumber(250),
},{
timestamp: new Date(2020, 09, 26),
value1: getRandomNumber(100),
value2: getRandomNumber(50),
value3: getRandomNumber(250),
},{
timestamp: new Date(2020, 09, 27),
value1: getRandomNumber(100),
value2: getRandomNumber(50),
value3: getRandomNumber(250),
},
{
timestamp: new Date(2020, 09, 28),
value1: getRandomNumber(100),
value2: getRandomNumber(50),
value3: getRandomNumber(250),
}];
let i = 0;
function BudgetChart() {
const chartRef = useRef(null);
const [data, setData] = useState(initialData);
const [cursor, setCursor] = React.useState({ x: 0, y: 0 });
const [cursorVisible, setCursorVisible] = React.useState(false);
function createSeries(
fieldX,
fieldY,
name,
lineColor,
) {
if (!chartRef.current) return;
console.log('Create series');
// Init series
let series = chartRef.current.series.push(new am4charts.LineSeries());
series.name = name;
series.dataFields.valueY = fieldY;
series.dataFields.dateX = fieldX;
series.strokeWidth = 3;
series.stroke = am4core.color(lineColor);
series.tooltip.pointerOrientation = 'down';
series.tooltip.background.filters.clear(); // remove shadow
series.tooltip.getFillFromObject = false;
series.tooltip.background.fill = am4core.color('#2a2b2e');
series.tooltip.background.stroke = am4core.color('#2a2b2e');
series.tooltip.label.fontSize = 12;
series.tooltip.background.pointerLength = 0;
series.tooltip.dy = -5;
series.tooltipText = '{valueY}';
series.tensionX = 0.8;
series.showOnInit = false;
// Add bullet for optimization
let circleBullet = series.bullets.push(new am4charts.CircleBullet());
circleBullet.circle.radius = 6;
circleBullet.circle.fill = lineColor;
circleBullet.circle.stroke = am4core.color('#fff');
circleBullet.circle.strokeWidth = 3;
circleBullet.propertyFields.disabled = 'optimizationTooltipDisabled';
// Set up tooltip
series.adapter.add("tooltipText", function(ev) {
var text = "[bold]{dateX}[/]\n"
chartRef.current.series.each(function(item) {
text += "[" + item.stroke.hex + "]●[/] " + item.name + ": {" + item.dataFields.valueY + "}\n";
});
return text;
});
// Bullet shadow
let shadow = circleBullet.filters.push(new am4core.DropShadowFilter());
shadow.opacity = 0.1;
}
React.useEffect(() => {
if (!chartRef.current) {
chartRef.current = am4core.create(CHART_CONTAINER, am4charts.XYChart);
chartRef.current.paddingLeft = 0;
// Add date axis
let dateAxis = chartRef.current.xAxes.push(new am4charts.DateAxis());
dateAxis.renderer.labels.template.fontSize = 12;
dateAxis.renderer.labels.template.fill = am4core.color(
'rgba(183,186,199,0.8)'
);
dateAxis.renderer.grid.template.strokeOpacity = 0;
// Add value axis
let valueAxis = chartRef.current.yAxes.push(new am4charts.ValueAxis());
valueAxis.renderer.grid.template.stroke = am4core.color(
'#f0f2fa'
);
valueAxis.renderer.grid.template.strokeOpacity = 1;
valueAxis.renderer.labels.template.fill = am4core.color(
'rgba(183,186,199,0.8)'
);
valueAxis.renderer.labels.template.fontSize = 12;
// Add cursor
chartRef.current.cursor = new am4charts.XYCursor();
chartRef.current.cursor.maxTooltipDistance = -1;
// Add legend
chartRef.current.legend = new am4charts.Legend();
chartRef.current.legend.position = 'bottom';
chartRef.current.legend.contentAlign = 'left';
chartRef.current.legend.paddingTop = 20;
// Disable axis lines
chartRef.current.cursor.lineX.disabled = true;
chartRef.current.cursor.lineY.disabled = true;
// Disable axis tooltips
dateAxis.cursorTooltipEnabled = false;
valueAxis.cursorTooltipEnabled = false;
// Disable zoom
chartRef.current.cursor.behavior = 'none';
chartRef.current.cursor.events.on('cursorpositionchanged', function(ev) {
let xAxis = ev.target.chart.xAxes.getIndex(0);
let yAxis = ev.target.chart.yAxes.getIndex(0);
setCursor({
x: xAxis.toAxisPosition(ev.target.xPosition),
y: yAxis.toAxisPosition(ev.target.yPosition),
});
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// Load data into chart
React.useEffect(() => {
console.log('data ', data)
if (chartRef.current) {
chartRef.current.data = data;
Object.keys(data[0]).forEach(key => {
if(key === 'timestamp') return;
createSeries(
'timestamp',
key,
key,
CHART_COLORS[key]
);
})
}
}, [data]);
// Handle component unmounting, dispose chart
React.useEffect(() => {
return () => {
chartRef.current && chartRef.current.dispose();
};
}, []);
function handleRemoveSeries(){
setData(data.map(item => ({timestamp: item.timestamp, value1: item.value1, value2: item.value2})))
}
return (
<>
<button onClick={handleRemoveSeries}>Remove 3rd series</button>
<div
id={CHART_CONTAINER}
style={{
width: '100%',
height: '350px',
marginBottom: '50px',
}}
/>
</>
);
}
ReactDOM.render(<BudgetChart />, document.getElementById('app'));
For all values near the top of the chart, tooltip is trying to squeeze itself inside chart area. According to the docs:
IMPORTANT: in some situations, like having multiple tooltips stacked for multiple series, the "up" and "down" values might be ignored in order to make tooltip overlap algorithm work.
Is there a way to disable the tooltip overlap algorithm work?

As of version 4.10.8 (released after posting the initial question) there is a property ignoreBounds on series tooltip.
More on ignoreBounds on amchart docs

Related

error code in console after using destroy function from chart.js

let myChart = null;
function draw(){
const labels = [];
for (var i = 0; i < stats.length; i++) {
labels.push(legend[i]);
}
const data = {
labels: labels,
datasets: [{
backgroundColor: ['rgb(204,0,0)', 'rgb(241,194,50)', 'rgb(41,134,204)', 'rgb(106,168,79)', 'rgb(255,62,153)'],
data: stats,
}]
};
const config = {
type: 'doughnut',
data: data,
options: {
radius: 200,
hoverOffset: 30,
aspectRatio: 1,
maintainAspectRatio: false,
responsive: false,
}
};
if (myChart !== null) {
myChart.destroy();
}
myChart = new Chart(document.getElementById('defaultCanvas0'), config);
}
When i run this code my chart just keeps flickering and drawing itself over and over again with a new id each time. I need to be able to destroy my original chart before drawing a new one
This is what console.log displays:
Edit:
function HealthStudy() {
//name for the visualisation to appear in the menu bar
this.name = "Health study";
//each visualisation must have a unique ID with no special characters
this.id = "Health-study";
//property to represent whether stats has been loaded
this.loaded = false;
// Preload the stats. This function is called automatically by the
// gallery when a visualisation is added.
this.preload = function () {
var self = this;
this.stats = loadTable(
'./data/health/causes-of-death.csv', 'csv', 'header',
// Callback function to set the value
// this.loaded to true.
function (table) {
self.loaded = true;
});
};
this.setup = function () {
if (!this.loaded) {
console.log('stats not yet loaded');
return;
}
// Create a select DOM element.
this.select = createSelect();
this.select.position(800, 380);
// Fill the options with all country names.
var countries = this.stats.columns;
// First entry is empty.
for (let i = 1; i < countries.length; i++) {
this.select.option(countries[i]);
}
};
this.destroy = function () {
this.select.remove();
};
// Create a new donut object.
this.donut = new Donut(width / 2, height / 2, width * 0.4);
this.draw = function () {
if (!this.loaded) {
console.log('stats not yet loaded');
return;
}
// Get the value of the country we're interested in from the
// select item.
var country = this.select.value();
// Get the column of raw stats for country.
var col = this.stats.getColumn(country);
// Convert all stats strings to numbers.
col = stringsToNumbers(col);
// Copy the row labels from the table (the first item of each row).
var legend = this.stats.getColumn(0);
// Colour to use for each category.
var colours = ['#CC0000', '#55a5f2', '#4dff00', '#f4e410', '#6a329f'];
// Make a title.
var title = 'Top 5 causes of death in ' + country;
// Draw the Donut!
this.donut.draw(col, legend, colours, title);

I can't get this map generator to work properly

The image that I want to get:
Image 1
The image that I'm getting when I run my code:
Image 2
To get the first image, you have to choose the tiny option in "number of regions", disable the check marks "noisy edges", "noisy fills" and "icons" in the site: link
My code is below, if you go to the site link and inspect it, you will see all the modules, is easy to see. In the draw.js file I edited document.createElement('canvas') to new Canvas.Canvas(), and I put var Canvas = require('canvas') in there too, theses things are the only thing that I edited (If I'm not mistaken) in all the modules. I'm running the code with node.
var fs = require('fs');
var Canvas = require('canvas')
const SimplexNoise = require('./simplex-noise/simplex-noise.js');
const Poisson = require('./poisson-disk-sampling/poisson-disk-sampling.js');
const DualMesh = require('./dual-mesh/index.js');
const MeshBuilder = require('./dual-mesh/create.js');
const Map = require('./mapgen2/index.js');
const Draw = require('./draw.js');
const Colormap = require('./colormap.js');
const {makeRandInt, makeRandFloat} = require('./prng/index.js');
let defaultUiState = {
seed: 187,
variant: 0,
size: 'tiny',
'noisy-fills': false,
'noisy-edges': false,
icons: false,
biomes: false,
lighting: false,
'north-temperature': 0,
'south-temperature': 0.05,
rainfall: 0,
canvasSize: 0,
persistence: 0, /* 0 means "normal" persistence value of 1/2 */
};
let uiState = {};
Object.assign(uiState, defaultUiState);
let _mapCache = [];
function getMap(size) {
const spacing = {
tiny: 38,
small: 26,
medium: 18,
large: 12.8,
huge: 9,
};
if (!_mapCache[size]) {
let mesh = new MeshBuilder({boundarySpacing: spacing[size]})
.addPoisson(Poisson, spacing[size], makeRandFloat(12345))
.create();
_mapCache[size] = new Map(
new DualMesh(mesh),
{amplitude: 0.2, length: 4, seed: 12345},
makeRandInt
);
console.log(`Map size "${size}" has ${_mapCache[size].mesh.numRegions} regions`);
}
return _mapCache[size];
}
let requestAnimationFrameQueue = [];
const mapIconsConfig = {left: 9, top: 4, filename: "./map-icons.png"};
mapIconsConfig.image = new Canvas.Image();
mapIconsConfig.image.onload = draw;
mapIconsConfig.image.src = mapIconsConfig.filename;
function draw() {
let map = getMap(uiState.size);
let noisyEdges = uiState['noisy-edges'],
noisyFills = uiState['noisy-fills'];
let canvas = new Canvas.Canvas(1000, 1000);
let ctx = canvas.getContext('2d');
let size = Math.min(canvas.width, canvas.height);
if (size != uiState.canvasSize) {
uiState.canvasSize = size;
size = 1024;
canvas.width = size;
canvas.height = size;
}
let noise = new SimplexNoise(makeRandFloat(uiState.seed));
let persistence = Math.pow(1/2, 1 + uiState.persistence);
let islandShapeAmplitudes = Array.from({length: 5}, (_,octave) => Math.pow(persistence, octave));
let biomeBias = {
north_temperature: uiState['north-temperature'],
south_temperature: uiState['south-temperature'],
moisture: uiState.rainfall,
};
let colormap = uiState.biomes? new Colormap.Discrete() : new Colormap.Smooth();
let queue = [];
if ((!noisyEdges || uiState.size === 'large' || uiState.size === 'huge')) {
queue.push(() => Draw.approximateIslandShape(ctx, 1000, 1000, noise, {round: 0.5, inflate: 0.4, amplitudes: islandShapeAmplitudes.slice(0, 3)}));
}
queue.push(
() => map.calculate({
noise: noise,
drainageSeed: uiState.variant,
riverSeed: uiState.variant,
biomeBias: biomeBias,
shape: {round: 0.5, inflate: 0.4, amplitudes: islandShapeAmplitudes},
}),
() => {
Draw.background(ctx, colormap);
Draw.noisyRegions(ctx, map, colormap, noisyEdges);
// Draw the rivers early for better user experience
Draw.rivers(ctx, map, colormap, noisyEdges, true);
}
);
for (let phase = 0; phase < 16; phase++) {
queue.push(() => Draw.noisyEdges(ctx, map, colormap, noisyEdges, phase));
}
queue.push(() => Draw.rivers(ctx, map, colormap, noisyEdges, false));
queue.push(() => Draw.coastlines(ctx, map, colormap, noisyEdges));
if (noisyFills) {
queue.push(() => Draw.noisyFill(ctx, 1000, 1000, makeRandInt(12345)));
}
if (uiState.icons) {
queue.push(() => Draw.regionIcons(ctx, map, mapIconsConfig, makeRandInt(uiState.variant)));
}
if (uiState.lighting) {
queue.push(() => Draw.lighting(ctx, 1000, 1000, map));
}
requestAnimationFrameQueue = queue.map(
(layer, i) => () => {
ctx.save();
ctx.scale(canvas.width / 1000, canvas.height / 1000);
layer();
ctx.restore();
});
while (requestAnimationFrameQueue.length > 0) {
let f = requestAnimationFrameQueue.shift();
f();
}
return canvas;
}
function saveToFile(canvas) {
const buff = canvas.toBuffer();
fs.writeFileSync("test.png", buff, {encoding: "utf-8", flag: "w+", mode: 0o666});
}
let canvas = draw();
saveToFile(canvas);

Pass data from ReactAudioPlayer to Amcharts slider?

I am trying to combine controls of amcharts to the react audio player.
Here, I have amcharts line graph with a slider. Now I am trying control the slider in such a way that whenever I hit the play button of react audio player, I could move the slider with the audio player's seeker. I hope, this makes sense to you.
import React from "react";
import ReactAudioPlayer from "react-audio-player";
import audio from "/home/aniruddha/workspace/playwith_audio/anni_web_player/src/audio.flac";
import * as am4core from "#amcharts/amcharts4/core";
import * as am4charts from "#amcharts/amcharts4/charts";
import am4themes_spiritedaway from "#amcharts/amcharts4/themes/spiritedaway";
import am4themes_animated from "#amcharts/amcharts4/themes/animated";
/* Chart code */
// Themes begin
am4core.useTheme(am4themes_spiritedaway);
am4core.useTheme(am4themes_animated);
class App extends React.Component {
constructor(props) {
super(props);
this.state = {slider: 1586169460800};
}
componentDidMount() {
let chart = am4core.create("chartdiv", am4charts.XYChart);
// Add data
chart.data = this.generateChartData();
// Create axes
let dateAxis = chart.xAxes.push(new am4charts.DateAxis());
let valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
// Create series
let series = chart.series.push(new am4charts.LineSeries());
series.dataFields.valueY = "visits";
series.dataFields.dateX = "date";
series.strokeWidth = 1;
series.minBulletDistance = 10;
series.tooltipText = "{valueY}";
series.fillOpacity = 0.1;
series.tooltip.pointerOrientation = "vertical";
series.tooltip.background.cornerRadius = 20;
series.tooltip.background.fillOpacity = 0.5;
series.tooltip.label.padding(12, 12, 12, 12);
let seriesRange = dateAxis.createSeriesRange(series);
seriesRange.contents.strokeDasharray = "2,3";
seriesRange.contents.stroke = chart.colors.getIndex(8);
seriesRange.contents.strokeWidth = 1;
let pattern = new am4core.LinePattern();
pattern.rotation = -45;
pattern.stroke = seriesRange.contents.stroke;
pattern.width = 1000;
pattern.height = 1000;
pattern.gap = 6;
seriesRange.contents.fill = pattern;
seriesRange.contents.fillOpacity = 0.5;
// Add scrollbar
chart.scrollbarX = new am4core.Scrollbar();
// add range
let range = dateAxis.axisRanges.push(new am4charts.DateAxisDataItem());
range.grid.stroke = chart.colors.getIndex(0);
range.grid.strokeOpacity = 1;
range.bullet = new am4core.ResizeButton();
range.bullet.background.fill = chart.colors.getIndex(0);
range.bullet.background.states.copyFrom(
chart.zoomOutButton.background.states
);
range.bullet.minX = 0;
range.bullet.adapter.add("minY", function (minY, target) {
target.maxY = chart.plotContainer.maxHeight;
target.maxX = chart.plotContainer.maxWidth;
return chart.plotContainer.maxHeight;
});
range.bullet.events.on("dragged", function () {
range.value = dateAxis.xToValue(range.bullet.pixelX);
seriesRange.value = range.value;
console.log(seriesRange.value)
});
let firstTime = chart.data[0].date.getTime();
let lastTime = chart.data[chart.data.length - 1].date.getTime();
let date = new Date(firstTime + (lastTime - firstTime) / 2);
range.date = date;
seriesRange.date = date;
seriesRange.endDate = chart.data[chart.data.length - 1].date;
this.chart = chart
console.log(this.state.slider);
this.setState({ seriesRange } )
console.log(this.state.slider);
range.value = this.state.slider;
seriesRange.value = this.state.slider;
}
generateChartData() {
let chartData = [];
let firstDate = new Date();
firstDate.setDate(firstDate.getDate() - 200);
let visits = 1200;
for (var i = 0; i < 200; i++) {
let newDate = new Date(firstDate);
newDate.setDate(newDate.getDate() + i);
visits += Math.round(
(Math.random() < 0.5 ? 1 : -1) * Math.random() * 10
);
chartData.push({
date: newDate,
visits: visits,
});
}
return chartData;
}
sound(event){
console.log(event.timeStamp);
//this.setState({slider: 1586025000000 })
}
seek(event){
console.log(event);
}
lis(event){
console.log(event);
}
componentWillUnmount() {
if (this.chart) {
this.chart.dispose();
}
}
render() {
return (
<div>
<div id="chartdiv" style={{ width: "100%", height: "500px" }}></div>
<ReactAudioPlayer src={audio} onPlay={this.sound} onListen={this.lis} onSeeked={this.seek} controls></ReactAudioPlayer>
</div>
);
}
}
export default App;
I am not able to access this.setState({slider: 1586025000000 }) in the sound function. I am quite new to react. Please any suggestion is welcome;
You need to wrap your callbacks with arrow functions that call your methods directly (or call bind: this.sound.bind(this)) so that it resolves to the correct this scope:
<ReactAudioPlayer
src="{audio}"
onPlay={(ev) => this.sound(ev)}
onListen={(ev) => this.lis(ev)}
onSeeked={(ev) => this.seek(ev)}
controls></ReactAudioPlayer>

amCharts Live Data -- adding multiple series

Disclaimer: I am still learning some of the basics and I am still learning proper terminology.
I am trying to use an example of amCharts with live data and add another series into the chart. My goal is to have a single chart with two series/lines streaming data.
The basic example I am working from is here:
https://codepen.io/team/amcharts/pen/MGVbNV
I have managed to get two line series working, however its extremely clunky at the moment.
Here is where I am at:
am4core.useTheme(am4themes_animated);
am4core.useTheme(am4themes_dark);
var chart = am4core.create("chartdiv", am4charts.XYChart);
chart.hiddenState.properties.opacity = 0;
chart.padding(0, 0, 0, 0);
chart.zoomOutButton.disabled = true;
var data = [];
var upstream = 10;
var i = 0;
for (i = 0; i <= 30; i++) {
upstream -= Math.round((Math.random() < 0.5 ? 1 : -1) * Math.random() * 10);
data.push({ date: new Date().setSeconds(i - 30), Upstream: upstream });
}
var downstream = 10;
var j = 0;
for (j = 0; j <= 30; j++) {
downstream -= Math.round((Math.random() < 0.5 ? 1 : -1) * Math.random() * 10);
data.push({ date: new Date().setSeconds(j - 30), value: downstream });
}
chart.data = data;
var dateAxis = chart.xAxes.push(new am4charts.DateAxis());
dateAxis.renderer.grid.template.location = 0;
dateAxis.renderer.minGridDistance = 30;
dateAxis.dateFormats.setKey("second", "ss");
dateAxis.periodChangeDateFormats.setKey("second", "[bold]h:mm a");
dateAxis.periodChangeDateFormats.setKey("minute", "[bold]h:mm a");
dateAxis.periodChangeDateFormats.setKey("hour", "[bold]h:mm a");
dateAxis.renderer.inside = true;
dateAxis.renderer.axisFills.template.disabled = true;
dateAxis.renderer.ticks.template.disabled = true;
var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
valueAxis.tooltip.disabled = true;
valueAxis.interpolationDuration = 500;
valueAxis.rangeChangeDuration = 500;
valueAxis.renderer.inside = true;
valueAxis.renderer.minLabelPosition = 0.05;
valueAxis.renderer.maxLabelPosition = 0.95;
valueAxis.renderer.axisFills.template.disabled = true;
valueAxis.renderer.ticks.template.disabled = true;
var series1 = chart.series.push(new am4charts.LineSeries());
series1.dataFields.dateX = "date";
series1.dataFields.valueY = "Upstream";
series1.interpolationDuration = 500;
series1.defaultState.transitionDuration = 0;
series1.tensionX = 0.8;
var series2 = chart.series.push(new am4charts.LineSeries());
series2.dataFields.dateX = "date";
series2.dataFields.valueY = "Downstream";
series2.interpolationDuration = 500;
series2.defaultState.transitionDuration = 0;
series2.tensionX = 0.8;
chart.events.on("datavalidated", function () {
dateAxis.zoom({ start: 1 / 15, end: 1.2 }, false, true);
});
dateAxis.interpolationDuration = 500;
dateAxis.rangeChangeDuration = 500;
document.addEventListener("visibilitychange", function() {
if (document.hidden) {
if (interval) {
clearInterval(interval);
}
}
else {
startInterval();
}
}, false);
// add data
var interval;
function startInterval() {
interval = setInterval(function() {
upstream =
upstream + Math.round((Math.random() < 0.5 ? 1 : -1) * Math.random() * 5);
var lastdataItem = series1.dataItems.getIndex(series1.dataItems.length - 1);
chart.addData(
{ date: new Date(lastdataItem.dateX.getTime() + 1000), Upstream: upstream },
1
);
downstream =
downstream + Math.round((Math.random() < 0.5 ? 1 : -1) * Math.random() * 1);
var lastdataItem = series2.dataItems.getIndex(series2.dataItems.length - 1);
chart.addData(
{ date: new Date(lastdataItem.dateX.getTime() + 1000), Downstream: downstream },
1
);
}, 1000);
}
startInterval();
// all the below is optional, makes some fancy effects
// gradient fill of the series
series1.fillOpacity = 1;
var gradient = new am4core.LinearGradient();
gradient.addColor(chart.colors.getIndex(0), 0.2);
gradient.addColor(chart.colors.getIndex(0), 0);
series1.fill = gradient;
// this makes date axis labels to fade out
dateAxis.renderer.labels.template.adapter.add("fillOpacity", function (fillOpacity, target) {
var dataItem = target.dataItem;
return dataItem.position;
})
// need to set this, otherwise fillOpacity is not changed and not set
dateAxis.events.on("validated", function () {
am4core.iter.each(dateAxis.renderer.labels.iterator(), function (label) {
label.fillOpacity = label.fillOpacity;
})
})
// this makes date axis labels which are at equal minutes to be rotated
dateAxis.renderer.labels.template.adapter.add("rotation", function (rotation, target) {
var dataItem = target.dataItem;
if (dataItem.date.getTime() == am4core.time.round(new Date(dataItem.date.getTime()), "minute").getTime()) {
target.verticalCenter = "middle";
target.horizontalCenter = "left";
return -90;
}
else {
target.verticalCenter = "bottom";
target.horizontalCenter = "middle";
return 0;
}
})
// bullet at the front of the line
var bullet = series1.createChild(am4charts.CircleBullet);
bullet.circle.radius = 5;
bullet.fillOpacity = 1;
bullet.fill = chart.colors.getIndex(0);
bullet.isMeasured = false;
series1.events.on("validated", function() {
bullet.moveTo(series1.dataItems.last.point);
bullet.validatePosition();
});
Any insight and help on getting this to be smoother? I think I need to do something with SetInterval() or combine functions somehow.

how to change color of dots in graph using chart.js

want to change the graph dot point color
this is my js code
i had created a graph using chart.js but now i want to show different dot color in graph so that user can understand which value are more than average and which are not.
how can i achieve that i am sending you my code
var label = [];
var dataset_data = [];
$scope.number = details.number;
var total_picked = 0;
angular.forEach(details.picked_details,function(value,key)
{
label.push("Pair with "+value.paired_with);
dataset_data.push(value.no_of_times);
total_picked+=value.no_of_times;
})
var data = {
labels: label,
datasets: [{
data: dataset_data
}]
};
var ctx = document.getElementById("LineWithLine").getContext("2d");
Chart.types.Line.extend({
name: "LineWithLine",
draw: function () {
Chart.types.Line.prototype.draw.apply(this, arguments);
var lines = this.options.limitLines;
for (var i = lines.length; --i >= 0;) {
var xStart = Math.round(this.scale.xScalePaddingLeft);
var linePositionY = this.scale.calculateY(lines[i].value);
//this.chart.ctx.fillStyle = lines[i].color ? lines[i].color : this.scale.textColor;
this.chart.ctx.fillStyle = "green";
this.chart.ctx.font = this.scale.font;
this.chart.ctx.textAlign = "left";
this.chart.ctx.textBaseline = "top";
if (this.scale.showLabels && lines[i].label) {
this.chart.ctx.fillText(lines[i].label, xStart + 20, linePositionY);
}
this.chart.ctx.lineWidth = this.scale.gridLineWidth;
this.chart.ctx.strokeStyle = lines[i].color ? lines[i].color : this.scale.gridLineColor;
//this.chart.ctx.strokeStyle = "green";
if (this.scale.showHorizontalLines) {
this.chart.ctx.beginPath();**strong text**
this.chart.ctx.moveTo(xStart, linePositionY);
this.chart.ctx.lineTo(this.scale.width, linePositionY);
this.chart.ctx.stroke();
this.chart.ctx.closePath();
}
this.chart.ctx.lineWidth = this.lineWidth;
this.chart.ctx.strokeStyle = this.lineColor;
//this.chart.ctx.strokeStyle = "yellow";
this.chart.ctx.beginPath();
this.chart.ctx.moveTo(xStart - 5, linePositionY);
this.chart.ctx.lineTo(xStart, linePositionY);
this.chart.ctx.stroke();
this.chart.ctx.closePath();
}
}
});
new Chart(ctx).LineWithLine(data, {
datasetFill : false,
limitLines: [
{
value: parseInt(total_picked/47),
label: "Average Pair of "+details.number+" With Other is "+parseInt(total_picked/47),
color: '#FF0000'
}
],
});
Refer to this issue. Basically, you can create an array of colors for pointBackgroundColor.
https://github.com/chartjs/Chart.js/issues/2670

Categories

Resources