Dygraphs vertical line with label - javascript

I need to place a vertical line with a label on my dygraph like the National holiday line in this example - http://www.fusioncharts.com/dev/chart-attributes.html?chart=msline
I have searched google for about 2 hours and can't find any examples. Can someone shoot me an example or put me on the right track? Thanks.

Best practice today is to add highlighted region using underlay callback (example).
try specifying the "underlayCallback" option within your "new Dygraph()" call. Use HTML Canvas context to draw the line.
On creating your graph:
// Graph range on X axis:
var xMin=0, xMax=1;
// horizontal line Y value:
var yValue = 0.5;
new Dygraph(document.getElementById('garph'), data, {
// Graph options
underlayCallback: function(ctx, area, dygraph) {
var xLeft = dygraph.toDomCoords(Min, yValue);
var xRight = dygraph.toDomCoords(Max, yValue);
ctx.strokeStyle = 'black';
ctx.beginPath();
ctx.moveTo(xLeft[0], xLeft[1] );
ctx.lineTo(xRight[0], xRight[1]);
ctx.closePath();
ctx.stroke();
}
});
Note this is a very general example, as you haven't shown your own code.

Related

Tooltip overriding the text in chart js

I have created a graph like below using chart.js
The above graph the texts like (Bare, Mounted) I creates this in oncomplete function like
onComplete: function () {
// render the value of the chart above the bar
var ctx = this.chart.ctx;
ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, 'normal', Chart.defaults.global.defaultFontFamily);
ctx.fillStyle = this.chart.config.options.defaultFontColor;
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';
this.data.datasets.forEach(function (dataset) {
for (var i = 0; i < dataset.data.length; i++) {
var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model;
if(dataset._meta[0].controller.index==1){
ctx.fillText(dataset.data[i], model.x-10, model.y+8);
ctx.fillText('Mounted', model.x-25, model.y+38);
ctx.fillText('360 (Available)', model.x-42, model.y-25);
}else{
ctx.fillText(dataset.data[i], 10, model.y+8);
ctx.fillText('Bare', 12, model.y+38);
ctx.fillText($scope.labels[i], 12, model.y-25);
}
}
});
}}
But these values are rendered above the tooltip. How can I avoid this??
Your problem is that onComplete will be called at the end of the animation. That is how onComplete is supposed to work. If there is a tooltip to be drawn, onComplete will be called after the tooltip has been drawn, since drawing the tooltip is part of the animation. This is the reason that your texts are drawn above everything else. What you could do is create (and register) a plugin to draw your texts (ditch the onComplete approach). Look at the docs about creating a plugin. You will have to override one method only (play around to find out which method to override, see some plugin examples, also pay attention to how easing is used) where you should be able to use your current code with just a few changes.

Chart.js - Add text/label to bubble chart elements without using tooltips?

Question
Is there any way to add labels to the individual bubbles in a Chart.js bubble chart without resorting to displaying tooltips at all times?
Background
The chart data is for visualizing our project backlog. Additional details, i.e. Project Name, about each project are in a table.
We previously used google charts, and just included the row number from the table on the bubble so you could match things up.
With Chart.js I only get the bubbles and tooltips.
I've reviewed the following related questions, but the solution they suggested requires having tooltips display at all times. I've got a lot more information in the tooltips and displaying them all the time would significantly clutter the chart.
Can individual bubbles in a chartjs bubble chart have labels?
How to show tooltips always on Chart.js 2
Chart.js doesn't support this directly, but Evert Timberg was very helpful in providing an example Chart.js plugin does exactly this.
From Chart.js Data Labeling Example
// Define a plugin to provide data labels
Chart.plugins.register({
afterDatasetsDraw: function(chartInstance, easing) {
// To only draw at the end of animation, check for easing === 1
var ctx = chartInstance.chart.ctx;
chartInstance.data.datasets.forEach(function (dataset, i) {
var meta = chartInstance.getDatasetMeta(i);
if (!meta.hidden) {
meta.data.forEach(function(element, index) {
// Draw the text in black, with the specified font
ctx.fillStyle = 'rgb(0, 0, 0)';
var fontSize = 16;
var fontStyle = 'normal';
var fontFamily = 'Helvetica Neue';
ctx.font = Chart.helpers.fontString(fontSize, fontStyle, fontFamily);
// Just naively convert to string for now
// <---- ADJUST TO DESIRED TEXT --->
var dataString = dataset.data[index].toString();
// Make sure alignment settings are correct
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
var padding = 5;
var position = element.tooltipPosition();
ctx.fillText(dataString, position.x, position.y - (fontSize / 2) - padding);
});
}
});
}
});
For example, if i just passed in "#22" as the text to render, we get this.

Javascript render text in canvas and avoid collision of text

I'm developing a mapping software that renders text from database to a specific coordinate on the canvas. The objective is for the rendered text not to step on each other (not to overlap) but still follow the coordinate where it should display. The idea is, if the rendered texts overlaps, the program may opt to display it at an angle. Currently I'm rendering text via the code below:
create_point:function(x,y,stitle){
var canvas = document.getElementById('text-layer');
var context = canvas.getContext('2d');
context.fillText(stitle,x,y); // text and position
context.save();
}
Any ideas on this?
Thanks in advance :-)
Interesting mind puzzle!
Problem
You have mapped coordinates (with text labels) from your database and occasionally 2 or more coordinates are so close together that their text labels intersect (causing their text labels to be unreadable).
One solution
For each new text label to be drawn on the map:
Assume each new text label is to be drawn on the top-side of the map coordinate. Test if the new label would overwrite any existing label.
If no overwriting would take place, draw it on the top-side (and you're done with this label).
If the top-side would cause overwriting, continue to step 2.
Repeat step#1 assuming the new label would be on right-side of the map coordinate.
Repeat step#1 assuming the new label would be on bottom-side of the map coordinate.
Repeat step#1 assuming the new label would be on left-side of the map coordinate.
If all 4 steps above fail then this text label cannot be draw without overwriting existing labels.
Given failure, you have to decide on an alternate way to give the user your text label information.
These options come to mind:
Draw a small marker on the map that the user can hover over and view a popup tooltip with the text label information. This is a very common way of dealing with information that doesn't fit on the page.
Draw a small marker on the map that refers the user to a separate legend containing the text label information.
Draw a small marker on the map with an arrow-line that leads the user to a text-label that is drawn on the map but is further away from the map coordinate.
Don't include this new label at all! This new label might not be as important as existing map labels and therefore might be omitted. You can easily achieve this by sorting your map database in order of importance to the user.
Here is demo that illustrates this solution
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
var fontSize=12;
var fontFace='verdana';
var dotRadius=3;
var legendX=350;
var legendY=0;
var legendYincrement=10;
var labels=[];
var nextId=0;
ctx.textAlign='left';
ctx.textBaseline='top';
ctx.font='10px arial';
ctx.strokeRect(legendX-5,0,cw-legendX+5,ch);
ctx.fillText('Other labels',legendX-3,legendY+2);
legendY+=legendYincrement;
ctx.fillText('(Color Coded)',legendX-3,legendY+2);
legendY+=legendYincrement;
var label=addLabel('Label #0',cw/2,ch/2,fontSize,fontFace,dotRadius);
drawLabel(label);
$("#canvas").mousedown(function(e){handleMouseDown(e);});
//
function addLabel(text,dotX,dotY,fontsize,fontface,dotRadius){
var font=fontsize+'px '+fontface;
ctx.font=font;
var w=ctx.measureText(text).width;
var h=fontsize*1.286;
var label={
id:nextId++,
text:text,
x:dotX-w/2,
y:dotY-dotRadius-h,
w:w,
h:h,
offsetY:0,
font:font,
isColliding:false,
dotRadius:dotRadius,
dotX:dotX,
dotY:dotY,
};
labels.push(label);
// try to position this new label in a non-colliding position
var positions=[
{ x:dotX-w/2, y:dotY-dotRadius-h }, // N
{ x:dotX+dotRadius, y:dotY-h/2 }, // E
{ x:dotX-w/2, y:dotY+dotRadius }, // S
{ x:dotX-dotRadius-w, y:dotY-h/2 }, // W
];
for(var i=0;i<positions.length;i++){
var p=positions[i];
label.x=p.x;
label.y=p.y;
label.isColliding=thisLabelCollides(label);
if(!label.isColliding){ break; }
}
//
return(label);
}
function handleMouseDown(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
var x=parseInt(e.clientX-offsetX);
var y=parseInt(e.clientY-offsetY);
var label=addLabel('Label #'+nextId,x,y,fontSize,fontFace,dotRadius)
drawLabel(label);
}
//
function drawLabel(label){
ctx.textAlign='left';
ctx.textBaseline='top';
if(label.isColliding){
legendY+=legendYincrement;
ctx.beginPath();
ctx.arc(legendX,legendY,3,0,Math.PI*2);
ctx.fillStyle=randomColor();
ctx.fill();
ctx.font='10px arial';
ctx.fillText(label.text,legendX+5,legendY-5);
}else{
ctx.font=label.font;
ctx.fillStyle='black';
ctx.fillText(label.text,label.x,label.y)
ctx.strokeRect(label.x,label.y,label.w,label.h);
}
ctx.beginPath();
ctx.arc(label.dotX,label.dotY,label.dotRadius,0,Math.PI*2);
ctx.fill();
}
//
function thisLabelCollides(r1){
for(var i=0;i<labels.length;i++){
var r2=labels[i];
if(r1.id==r2.id || r2.isColliding){continue;}
var collides=(!(
r1.x > r2.x+r2.w ||
r1.x+r1.w < r2.x ||
r1.y > r2.y+r2.h ||
r1.y+r1.h < r2.y
));
if(collides){return(true);}
}
return(false);
}
//
function randomColor(){
return('#'+Math.floor(Math.random()*16777215).toString(16));
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Click on the canvas to add more map labels.</h4>
<canvas id="canvas" width=450 height=300></canvas>

chart.js Line chart with different background colors for each section

Lets say I have a Line chart with mon-fri for 4 weeks.
I want that these 4 weeks are diveded in sections. I want the first monday to friday have a white background color.
The second monday to friday a gray background.
The thirth a white bg again.
And the fourth weeks with monday to friday to have a gray background color.
What Im talking about is the background of the graph.
Is there a way to do this?
Chart.js clears the canvas before drawing (or redrawing) a chart.
We can jump in on this and draw our background once the chart is cleared. Just extend the Line chart and override the clear function in the initialize override.
Preview
Script
Chart.types.Line.extend({
name: "LineAlt",
initialize: function(data){
Chart.types.Line.prototype.initialize.apply(this, arguments);
// keep a reference to the original clear
this.originalClear = this.clear;
this.clear = function () {
this.originalClear();
// 1 x scale unit
var unitX = this.datasets[0].points[1].x - this.datasets[0].points[0].x;
var yTop = this.scale.startPoint;
var yHeight = this.scale.endPoint - this.scale.startPoint;
// change your color here
this.chart.ctx.fillStyle = 'rgba(100,100,100,0.8)';
// we shift it by half a x scale unit to the left because the space between gridline is actually a shared space
this.chart.ctx.fillRect(this.datasets[0].points[5].x - 0.5 * unitX, yTop, unitX * 5, yHeight);
this.chart.ctx.fillRect(this.datasets[0].points[15].x - 0.5 * unitX, yTop, unitX * 5, yHeight);
}
}
});
Then just use LineAlt instead of Line
var myNewChart = new Chart(ctx).LineAlt(data);
Fiddle - http://jsfiddle.net/oe2606ww/
Some people here have requested something that works for later versions, here's my hacked together solution that works on ChartJS 2.7.2 (EDIT: Apr 2020: Also 2.9.3) and could probably be adapted. Chart.types.Line.extend used in the answer above, doesn't seem to be valid in v2.
I managed to figure this out with help from this thread to get the plugin code, and also found this thread useful for gathering co-ordinates of the data points.
With some work this fiddle should allow you to pass the label array keys as start/stop positions via the following code (where 0 and 1 are the keys):
var start = meta.data[0]._model.x;
var stop = meta.data[1]._model.x;
You could loop this, along with the ctx.fillRect function to draw multiple rectangles.
Here's the working fiddle: http://jsfiddle.net/oe2606ww/436/
I combined #potatopeelings's and #v25's solutions for a chart.js v2 solution. It utilizes the format of #potatopeelings's solution, allowing to use an alternate chart type (LineAlt), and the updated implementation from #v25's solution.
Chart.controllers.LineAlt = Chart.controllers.line.extend({
draw: function (ease) {
if (this.chart.config.options.chartArea && this.chart.config.options.chartArea.backgroundColor) {
var ctx = this.chart.chart.ctx;
var chartArea = this.chart.chartArea;
var meta = this.chart.getDatasetMeta(0);
var start = meta.data[1]._model.x;
var stop = meta.data[2]._model.x;
ctx.save();
ctx.fillStyle = this.chart.config.options.chartArea.backgroundColor;
ctx.fillRect(start, chartArea.top, stop - start, chartArea.bottom - chartArea.top);
ctx.restore();
}
// Perform regular chart draw
Chart.controllers.line.prototype.draw.call(this, ease);
}
});
Then you can use the custom chart type just as in #potatopeelings's solution:
var myNewChart = new Chart(ctx, {type: 'LineAlt', data: data});
I'd try a little work around,I'd draw an image with four line each one with width 1px and a different color; then in a CSS sheet define:
canvas {
background-image: url(backgroundimage.jpg);
background-size: contain;
}

Morris.js Donut labels

I'm having an extremely frustrating time trying to move the data text labels from this donut chart to be outside of the center.
http://jsbin.com/ukaxod/144/embed?javascript,live
the xpos and ypos are generated dynamically, and I can't seem to overwrite them with CSS and I can't find what is setting them in JS. A little help would be very appreciated. thanks
The label in the center is placed there by design and cannot be simply changed.
If you look at the source code you can find these lines:
Donut.prototype.redraw = function() {
// ...
cx = this.el.width() / 2;
cy = this.el.height() / 2;
// ...
this.text1 = this.drawEmptyDonutLabel(cx, cy - 10, this.options.labelColor, 15, 800);
this.text2 = this.drawEmptyDonutLabel(cx, cy + 10, this.options.labelColor, 14);
// ...
}
It calculates cx/cy so that they point to the center of the chart, and generates 2 labels (for text and value).
You can change the source code, or you can try to find the corresponding text tag in the svg code and change its x and y properties.

Categories

Resources