How to give fixed width to legend in plotly.js - javascript

I am using plotly.js to create charts. Some charts have long text inside legend, so these text make legend unnecessary bigger. I want to give legend fixed width and if a length of text is long, I want it to be wrapped.
I have been trying to do this by manipulating svg element on DOM, but since legend is svg:g tag, I can not really do much. I also try give textLength to <text>, but it does wrap text but it does not make a new line.
Is there a way to give legend fixed width?

Perhaps just adding </br> to the legend text would be an easy solution to get a fixed width?
You could use any text wrapping trick in Javascript to do so, e.g.
for (i = 0; i < data.length; i += 1) {
data[i].name = data[i].name.replace(/([\w\s,]{13,}?)\s?\b/g, "$1</br>");
}
var numbers = [];
var total = 10000;
var i = 0;
var data = [];
for (i = 0; i < total; i++) {
numbers[i] = ((Math.random() + Math.random() + Math.random() + Math.random() + Math.random() + Math.random()) - 3) / 3;
}
data.push({
x: numbers,
name: "that's a histogram with lots of points, it therefore needs lots of text as well",
type: 'histogram',
marker: {color: 'blue'}
});
numbers = [];
for (var i = 0; i < total; i++) {
numbers[i] = 0.25 + ((Math.random() + Math.random() + Math.random() + Math.random() + Math.random() + Math.random()) - 3) / 3;
}
data.push({
x: numbers,
name: "that's yet another histogram with lots of points, it therefore needs lots of text as well, like for example that it was shifted by 0.25",
type: 'histogram',
marker: {color: 'red'}
});
for (i = 0; i < data.length; i += 1) {
data[i].name = data[i].name.replace(/([\w\s,]{13,}?)\s?\b/g, "$1</br>");
}
Plotly.newPlot('myDiv', data, {showlegend: true});
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id="myDiv"></div>

Maximilian's answer almost works with the current version of Plotly.js. There doesn't seem to be a property for setting the width of the legend, but you can rig it by inserting breaks (</br>) into the legend text manually. For some reason Plotly seems to mishandle the first break in the text, however; to get around this, you can add a "ghost" break at the beginning of the text. For instance:
// Rudimentary function to break a string on spaces
// attempting to get no more than `maxChars` characters in a line
const breakString = (str, maxChars) => {
if (str.length > maxChars && str.includes(' ')) {
let index = str.lastIndexOf(' ', maxChars);
if (index === -1) index = str.indexOf(' ', maxChars + 1);
return str.substr(0, index) + '</br>' + breakString(str.substr(index + 1), maxChars);
} else {
return str;
}
}
const original_text = "Respondents who think Tenet is Christopher Nolan's most interesting movie";
const final_text = '</br>' + breakString(original_text, 20);
The color symbols next to each legend entry by default are aligned to the center of the corresponding text; with multiline legend text, you'll probably want to change the symbols to be aligned to the top of the text. To do that, add this to your layout:
legend: {
valign: 'top',
}

This works for me using Plotly with javascript.
title: 'first part' + '<br>' + 'Second part'
eg
yaxis2: {
domain: [0.7, 1],
title: 'Sea Surface' + '<br>' + 'Temperature',
showgrid: true,
showline: true,
titlefont: {color: 'blue'},
tickfont: {color: 'blue'},
side: 'right',
anchor: 'free',
position: 0.91
},

Related

dygraphs JavaScript distance between dates

I have the following code:
http://jsfiddle.net/eM2Mg/7027/
g = new Dygraph(
// containing div
document.getElementById("graphdiv"),
// CSV or path to a CSV file.
("Response Time,Loop\n" +
"09/02/2015,175\n" +
"09/04/2015,170\n" +
"09/06/2015,180\n" +
"09/08/2015,177\n" +
"09/11/2015,179\n"+
"09/30/2015,165\n"),
{
strokeWidth: 3,
}
);
Is it possible in dygraphs to set the same distance between x-axis values?
I want to set the same line length among all points no matter of the date time space.
I doubt you can get it to fully work as your requirement is against how data is arranged on graphs, but you can use parse & format callbacks to come close:
var i = 0;
var labels = [];
g = new Dygraph(
// containing div
document.getElementById("graphdiv"),
// CSV or path to a CSV file.
("Response Time,Loop\n" +
"09/02/2015,175\n" +
"09/04/2015,170\n" +
"09/06/2015,180\n" +
"09/08/2015,177\n" +
"09/11/2015,179\n" +
"09/30/2015,165\n"), {
strokeWidth: 3,
xValueParser: function (str) {
console.log(str);
labels[++i] = str;
return i;
},
axes: {
x: {
valueFormatter: function (i) {
return labels[i];
},
axisLabelFormatter: function (i) {
return labels[i];
}
}
}
});
See fiddle

Angularjs NVD3 Custom X Axis

I have the following plunker where I want to wrap the X Axis labels to next line. I have the following configuration in my controller to customize the x axis but it is not working
xAxis: {
axisLabel: 'X Axis',
tickFormat: function(d){
var words = d.split(' '),label = '';
for (var i=0; i< words.length; i++) {
label += words[i] + "\n"; // does not work either
// label += "<tspan class='x'>" + words[i] + "<tspan>"; // does not work either
}
return label;
}
}
How should i make the code such that x axis does not overlap the labels. I am looking for same result as this but I could not understand how can I apply the logic without touching the NVD3 Directive I am using. Is it possible ?

Retrieve lines of text stored in an array and display them on a canvas with line breaks?

Below is an array with its elements that store some long lines of text.
var fcontent = [
["Definition: The term computer is obtained from the word compute. A computer is an electronic device that inputs (takes in) facts (known as data), and then processes (does something to or with) it. Afterwards it outputs, or displays, the results for you to see. Data is all kinds of facts, including, pictures, letters, numbers, sounds, etc."],
["Moreover: A computer is a system that is made up of a number of components. Next is a diagram of a computer system with some of its components"],
];
I am using the following function to display the elements on Canvas,:
firstLevelContent:function()
{
var startPoint = 48; $('.gamelayer').hide();
$('#gamecanvas').show();
for (var i=0; i<fcontent.length; i++)
{
game.context.strokeRect(1, 25, 637, 385);
game.context.fillStyle = 'brown';
game.context.font = 'bold 20px sans-serif';
game.context.textBaseline = 'bottom';
game.context.fillText(fcontent[i], 2, startPoint);
startPoint+=17;
}
},
But the text is displayed as it is in the code, I want find a way of break the lines to fit the width (640) of the canvas, so that I can see all the text displayed. pls help me. Thanks.
You need a word wrap function that will separate your text into suitable-length lines. There are other suggestions on SO about how to do this, but it seems like most of them don't properly account for overly-long words (i.e. when an individual word is longer than the required width) or whitespace (i.e. most assume a single space character between words). I had a think and came up with this:
function wordWrap(text, width, ctx, useHyphen) {
var words = text.split(/\b(?=\w)/),
wrappedText = [],
wordLength = 0,
fullLength = 0,
line = "",
lineLength = 0,
spacer = useHyphen ? "-" : "",
spacerLength = ctx.measureText(spacer).width,
letterLength = 0;
// make sure width to font size ratio is reasonable
if (ctx.measureText("M"+spacer).width > width) {return [];}
// produce lines of text
for (var i=0, l=words.length; i<l; i++) {
wordLength = ctx.measureText(words[i].replace(/\s+$/,"")).width;
fullLength = ctx.measureText(words[i]).width;
if (lineLength + wordLength < width) {
line += words[i];
lineLength += fullLength;
} else if (wordLength < width) {
wrappedText.push(line);
line = words[i];
lineLength = fullLength;
} else {
// word is too long so needs splitting up
for (var k=0, lim=words[i].length; k<lim; k++) {
letterLength = ctx.measureText(words[i][k]).width;
if (words[i][k+1] && /\S/.test(words[i][k+1])) {
if (lineLength + letterLength + spacerLength < width) {
line += words[i][k];
lineLength += letterLength;
} else {
if (true || words[i][k+1] && /\s/.test(words[i][k+1])) {line += spacer;}
wrappedText.push(line);
line = words[i][k];
lineLength = letterLength;
}
} else {
// last 'letter' in word
if (lineLength + letterLength < width) {
line += words[i].substr(k);
lineLength += ctx.measureText(words[i].substr(k)).width;
} else {
line += spacer;
wrappedText.push(line);
line = words[i].substr(k);
lineLength = ctx.measureText(words[i].substr(k)).width;
}
break;
}
}
}
}
wrappedText.push(line);
// strip trailing whitespace from each line
for (var j=0, len=wrappedText.length; j<len; j++) {
wrappedText[j] = wrappedText[j].replace(/\s+$/,"");
}
return wrappedText;
}
It can probably be improved upon, but it works for me: http://jsfiddle.net/cP5Fz/9/
Note: You'll need to set the font size that you'll be using before calling the wordWrap function.
As for the arguments you pass:
text: the text you want wrapping
width: the width that the text must fit into
ctx: the context of the canvas
useHyphen: set to true if you want long words to be broken up using a hyphen (optional)
It'll then return an array of strings that will each individually fit into the required space.

Randomizing 3 arrays in JavaScript/jQuery

I would like to randomize three arrays for fonts, font size, font weight.
I then need to display the results of the three arrays in a div, with a class name of randomFont.
So each time I use the class randomFont, it will return a random font/size/weight.
Any ideas on how I would go about doing this?
Let's say I have 3 variables in each array.
array 1 (fonts) > font 1, font 2, font 3
array 2 (font weight) > bold, light, 100
array 3 (font-size) > 12px, 24px, 100px
Then I want an an array to randomly choose one from each and display the output in a div>class called randomFont.
How would I do this?
Don't really need jQuery but this should do it, since you didn't supply any example code I've had to make it up but this should get you on your way.
var item = items[Math.floor(Math.random() * items.length)];
do this for each array.
You can see one approach at http://jsfiddle.net/CrossEye/bwsvy/
var fonts = ['Arial', 'Helvetica', 'Georgia', 'Tahoma', 'Verdana'];
var weights = ['normal', 'bold', 'lighter'];
var sizes = ['16px', '20px', '24px', '36px', '40px'];
var choose = function(arr) {
return arr[Math.floor(Math.random() * arr.length)];
};
// ...
var text = choose(sizes) + ' ' + choose(weights) + ' ' + choose(fonts);
output.innerHTML = '<div class="randomFont">' + text + '</div>';
// e.g. '24px bold Tahoma'
It's not clear what you mean by randomizing the arrays. If you have a fixed list of values and you want to rearrange them in place, then you want a shuffle functions, something like this:
var shuffle = function(arr) {
for (var i = arr.length; i--;) {
var j = Math.floor(Math.random() * (i + 1));
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
return arr;
}
That might be a partial answer to what you're looking for, but it's really not clear to me what you're after overall.

Highstock Series Compare To Previous Day

I've implemented the HighStock chart shown at:
http://www.highcharts.com/stock/demo/compare
This chart shows percent change from the first, or left-most, data point. I've got some stock analysts telling me this isn't of much use, and that they would rather see percent change from the previous data point, or in other words, if I was looking at "today" on the chart, I would want to see percent change from yesterday. I've looked over the highstock API and I can't seem to find a way to define the percent change function. Is there a proper way to have highstock do this or should I use a highcharts line chart to accomplish this?
I had to do something similar in highcharts before. I wanted to get the actual change from one datapoint to the next.
What I do, is I take a look at the current point (this.x) and I find it's position in the series array. From there, I easily find the index of the previous point (index = a - 1) and do the math. Hope this helps :)
EDIT:
Looked into the stockcharts API and figured it out for that product:
tooltip: {
formatter: function() {
var s = '<b>'+ Highcharts.dateFormat('%A, %b %e, %Y', this.x) +'</b><br>';
$.each(this.points, function(i, point) {
s += '<span style="font-weight: bold; font-size: 1.2em; color: ' + point.point.series.color + ';">' + point.point.series.name + '</span>:<span style="font-weight: bold; font-size: 1.2em;">' + point.y + '</span>' ;
s += '<span style="font-size: .8em;">';
var target = this.x;
index = 0;
for (var a = 0; a < point.series.points.length; a++) {
if (point.series.points[a].x == target)
{
index = a - 1;
}
}
var delta = 0;
var change = 0;
if (index >= 0)
{
delta = this.y - point.series.points[index].y;
change = (delta / point.series.points[index].y) * 100;
}
s += Math.round(change,2) + '%</span>';
s +='</span><br>';
});
return s;
}
},
And a working jsfiddle: http://jsfiddle.net/nLjsc/2/

Categories