How to get equal width charts - Apex Charts - javascript

I have two different graphs that I need to make. A simple bar graph, one with and one without labels on the xaxis. The design calls for this to be equal width, and no padding/margin on either side. As in it needs to line up with other objects.
The one chart uses the
sparkline: {
enabled: true
},
option. The one with the labels gets the wrong width because of the labels. I have tried with css, but using magic numbers isn't smart because the values of the labels can change based on values, and thus the width.
Code for both:
https://codepen.io/cibgraphics/pen/JjZaxGB
Image of problem

I think I have just found a quite experimental solution to solve this problem. It is not very clean, but it is interesting to share...
The idea is to play with the viewBox attribute of your second svg element. It is documented here: viewBox - SVG: Scalable Vector Graphics | MDN
I also use the SVGGraphicsElement.getBBox() method.
The SVGGraphicsElement.getBBox() method allows us to determine the coordinates of the smallest rectangle in which the object fits.
SVGGraphicsElement.getBBox() - Web APIs | MDN
When the page loads, we can set the viewBox using the coordinates returned by SVGGraphicsElement.getBBox(). It works, but if you resize the window, another svg element is created and replace the current one. Thus, the viewBox is lost.
So the idea is now to use a MutationObserver to watch for changes in the DOM. Here we observe #barChart2 because we are interested in one of its children (which is the svg element). When a change is detected, the custom noPadding() function is called.
Here is the code:
function noPadding() {
let svg = document.querySelector('#barChart2 svg'),
{x, y, width, height} = svg.getBBox();
svg.setAttribute('viewBox', `${x} ${y} ${width} ${height}`);
}
let node = document.querySelector('#barChart2'),
config = { childList: true },
observer = new MutationObserver(() => noPadding());
observer.observe(node, config);
noPadding();
If I put the above code in what you did, we get this:
var barChartOptions = {
series: [{
name: 'Actual',
data: [{
x: '2011',
y: 12,
},
]
}],
chart: {
type: 'bar',
height: '22px',
sparkline: {
enabled: true
},
},
plotOptions: {
bar: {
horizontal: true,
borderRadius: 10,
barHeight: '100%',
colors: {
backgroundBarColors: ['#E7E7E6'],
backgroundBarRadius: '11px',
}
},
},
colors: ['#3CDBC0'],
tooltip: {
custom: function({ series, seriesIndex, dataPointIndex, w }) {
return (
'<div class="arrow_box">' +
"<span>" +
series[seriesIndex][dataPointIndex] +
"</span>" +
"</div>"
);
}
}
};
var chart = new ApexCharts(document.querySelector("#barChart1"), barChartOptions);
chart.render();
var barChartOptions2 = {
series: [{
name: 'Actual',
data: [{
x: '2011',
y: 10,
},
]
}],
chart: {
type: 'bar',
height: '90px',
toolbar: {
show: false
},
},
grid: {
show: false,
padding: {
right: 0,
left: 0
}
},
yaxis: {
show: false,
},
xaxis: {
axisTicks: {
color: '#000',
},
},
dataLabels: {
enabled: false,
},
plotOptions: {
bar: {
horizontal: true,
borderRadius: 10,
columnWidth: '100%',
barHeight: '100%',
colors: {
backgroundBarColors: ['#E7E7E6'],
backgroundBarRadius: '11px',
},
},
},
colors: ['#3CDBC0'],
legend: {
show: false,
},
tooltip: {
custom: function({ series, seriesIndex, dataPointIndex, w }) {
return (
'<div class="arrow_box">' +
"<span>" +
series[seriesIndex][dataPointIndex] +
"</span>" +
"</div>"
);
}
}
};
var chart = new ApexCharts(document.querySelector("#barChart2"), barChartOptions2);
chart.render();
// ==================================================
function noPadding() {
let svg = document.querySelector('#barChart2 svg'),
{x, y, width, height} = svg.getBBox();
svg.setAttribute('viewBox', `${x} ${y} ${width} ${height}`);
}
let node = document.querySelector('#barChart2'),
config = { childList: true },
observer = new MutationObserver(() => noPadding());
observer.observe(node, config);
noPadding();
.barChart {
margin-bottom: 50px;
}
.apexcharts-tooltip {
filter: drop-shadow(0px 3.15331px 15.778px rgba(0, 45, 93, 0.2));
}
.arrow_box {
position: relative;
background: #fff;
}
.arrow_box:after, .arrow_box:before {
right: 100%;
top: 50%;
transform: translate(0, -50%);
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
.arrow_box:after {
border-color: rgba(85, 85, 85, 0);
border-right-color: #fff;
border-width: 10px;
// margin-top: -10px;
transform: translate(0, -50%);
}
.arrow_box:before {
border-color: rgba(0, 0, 0, 0);
border-right-color: #fff;
border-width: 13px;
// margin-top: -13px;
transform: translate(0, -50%);
}
.barChart .apexcharts-tooltip {
color: #000;
transform: translateX(10px) translateY(10px);
overflow: visible !important;
white-space: normal !important;
}
.barChart .apexcharts-tooltip span {
padding: 5px 10px;
display: inline-block;
}
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<div id="barChart1" class="barChart"></div>
<div id="barChart2" class="barChart"></div>

Related

How to make a Plotly plot follow the parent container's size?

Here the resizing of the image happens automatically when you resize the parent div. How to make a similar behaviour for the Plotly plot, with high FPS?
Plotly.newPlot('myDiv', [{x: [1, 2, 3, 4], y: [10, 15, 13, 17], mode: 'markers'}], {margin: {l: 50, r: 50, b: 50, t: 50, pad: 4}});
#a, #b { border: 1px solid black; resize: both; overflow: auto; height: 150px; width: 150px; }
#a img { width: 100%; }
<script src="https://cdn.plot.ly/plotly-2.16.2.min.js"></script>
<div id="a"><img src="https://picsum.photos/id/240/200"></div>
<div id="b"><div id="myDiv"></div></div>
Edit: With the current answer, it also becomes slow with a big plot:
var z = [];
for (var i = 0; i < 500; i++)
z.push(Array.from({length: 600}, () => Math.floor(Math.random() * 100)));
var data = [{z: z, colorscale: 'YlGnBu', type: 'heatmap'}];
Plotly.newPlot('b', data, {margin: {l: 50, r: 50, b: 50, t: 50, pad: 4}});
function fixer() {
el = document.getElementById('b');
Plotly.newPlot(el, el.data, el.layout, {responsive: true});
}
setTimeout(function() {
el = document.getElementById('b');
new ResizeSensor(el, fixer)
}, 200);
#a, #b {
border: 1px solid black;
resize: both;
overflow: auto;
height: 150px;
width: 150px; }
#a img { width: 100%; }
<script src="https://cdn.plot.ly/plotly-2.16.2.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/css-element-queries/1.2.3/ResizeSensor.js"></script>
<div id="a"><img src="https://picsum.photos/id/240/200"></div>
<div id="b" onresize=fixer></div>
Click on the image to see a video screencast:
Updated
Based on your comments that your actual data is far more extensive than in your question, I brought in some data from a Plotly example for scatter3d--- just with the purpose of slowing it down. Wow, what a difference.
Initially, I wrote (less the misspelled words, sigh):
Since resizing is only detectable at the window level, you need an intervention. This brings in ResizeSensor.js. I'm not the most JS-savvy, so you could probably streamline this further.
I've added some script: the function fixer re-plots the graph when the container is resized. Additionally, instead of myDiv, I've got the Plotly graph in b.
The only changes are in <!-- language: lang-html -->
I've changed the data and plot creation, adding responsive: true to the configuration at the onset. Additionally, I've changed fixer to Plotly.react instead of Plotly.newPlot. The responsive difference with the data I used here is significant.
Let me know if this is what you're looking for. If it's still really slow on your end let me know if there was any improvement.
d3.csv('https://raw.githubusercontent.com/plotly/datasets/master/3d-scatter.csv', function(err, rows){
function unpack(rows, key) {
return rows.map(function(row)
{ return row[key]; });}
var trace1 = {
x:unpack(rows, 'x1'), y: unpack(rows, 'y1'), z: unpack(rows, 'z1'),
mode: 'markers',
marker: {
size: 12,
line: {
color: 'rgba(217, 217, 217, 0.14)',
width: 0.5},
opacity: 0.8},
type: 'scatter3d'
};
var trace2 = {
x:unpack(rows, 'x2'), y: unpack(rows, 'y2'), z: unpack(rows, 'z2'),
mode: 'markers',
marker: {
color: 'rgb(127, 127, 127)',
size: 12,
symbol: 'circle',
line: {
color: 'rgb(204, 204, 204)',
width: 1},
opacity: 0.8},
type: 'scatter3d'};
var data = [trace1, trace2];
var layout = {margin: {
l: 0,
r: 0,
b: 0,
t: 0
}};
Plotly.newPlot('b', data, layout, {responsive: true});
});
#a, #b {
border: 1px solid black;
resize: both;
overflow: auto;
height: 150px;
width: 150px; }
#a img { width: 100%; }
<script src="https://cdn.plot.ly/plotly-2.16.2.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/css-element-queries/1.2.3/ResizeSensor.js"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js'></script>
<script>
function fixer() {
el = document.getElementById('b');
Plotly.react(el, el.data, el.layout, {responsive: true});
}
setTimeout(function() {
el = document.getElementById('b');
new ResizeSensor(el, fixer);
}, 200);
</script>
<div id="a"><img src="https://picsum.photos/id/240/200"></div>
<div id="b" onresize=fixer></div>

zing chart are not being render in Canvas

I am trying to plot some heavy data on zing Chart. Zing charts are normally plot inside a div but for my case my page became leggy after loading 200k points inside zing chart div-tag. In the doc it says to load large data in Canvas.
In the performance documentation of zing chart; in the end of document there is Render type, it says to load in canvas but its not working. according to doc
Render Type
The render method is where you can define the output, which can either render Canvas or SVG. If you are rendering a large dataset, the performance of Canvas will benefit you because DOM explosion happens when rendering in SVG.
Here is my code any suggestion or help.
function chart_timeFreq_ff_fh(timeArray, frequency_array_ff, frequency_array_fh) {
zingchart.DEV.SORTTOKENS = 0;
zingchart.DEV.PLOTSTATS = 0;
zingchart.DEV.RESOURCES = 0;
zingchart.DEV.KEEPSOURCE = 0;
zingchart.DEV.COPYDATA = 0;
zingchart.DEV.MEDIARULES = 0;
zingchart.SYNTAX = 'dashed';
$('#lineChart_f').remove();
$('#canvas_div_f').append(
'<canvas id="lineChart_f" style="min-height: 400px; height: 550px; max-height: 500px; max-width: 100%;"></canvas>'
);
let configTimeAndAngle = {
"type": "line",
plot: {
mode: 'fast',
'hint-ts': true
},
legend: {
layout: "1x2", //row x column // items means in one two we added two items as legends
x: "35%",
y: "6%",
},
"preview":{
"live":true
},
'scale-x': {
zooming: true,
labels: timeArray,
'max-items':8,
transform: {
type: 'date'
},
item: {
'font-size':10
}
},
'scale-y': {
'auto-fit': true,
guide: {
'line-style': "solid"
},
item: {
'font-size':10
}
},
tooltip: {
// text: 'Time : %kt (X) Freq : %%node-value (Y).',
text: 'Time : %kt (X) Freq : %v (Y).',
alpha: 0.9,
backgroundColor: '#F44336',
borderColor: '#B71C1C',
borderRadius: 2,
borderWidth: 1,
padding: '5 10'
},
gui: {
behaviors: [
{
id: 'ViewDataTable',
enabled: 'none'
},
{
id: 'ViewSource',
enabled: 'none'
},
{
id: 'CrosshairHide',
enabled: 'all'
}
]
},
"series": [
{
"values": frequency_array_ff,
'line-color': "#3366ff",
'background-color': "#3366ff",
text: "Centeral Frequency"
},
{
"values": frequency_array_fh,
'line-color': "#00cc99",
'background-color': "#00cc99",
text: "Frequency Hopping"
}
]
};
zingchart.render({
id: 'lineChart_cob_f',
data: configTimeAndAngle,
height: "100%",
width: "100%",
output: "canvas"
});
}
Updated
I have tried to plot like this but still issue. Above chart is also updated and we i need to change how I pass time ? my time format is like 2022-10-10 23:24:03 an array of time like this so in the 'scale-x': { labels: timeArray} i add time like this
"series": [
{
values: [],
'line-color': "#3366ff",
'background-color': "#3366ff",
text: "Centeral Frequency"
},
{
values: [],
'line-color': "#00cc99",
'background-color': "#00cc99",
text: "Frequency Hopping"
}
]
configTimeAndAngle.series[0].values.push([frequency_array_ff]);
configTimeAndAngle.series[1].values.push([frequency_array_fh]);
configTimeAndAngle.series[0]=values.[frequency_array_ff];
configTimeAndAngle.series[1]=values[frequency_array_fh];
configTimeAndAngle.series[0]=frequency_array_ff;
configTimeAndAngle.series[1]=frequency_array_fh];
I've managed to create you a demo in a studio application with over 200k nodes, maybe you can use this setup with canvas in the render method for reference and see how it goes. I hope this helps.
To be honest I also had the same issue all day, then I saw this and I have found the issue on your question. you have use canvas instead use div and while rendering use
// this is your code
$('#canvas_div_f').append(
'<canvas id="lineChart_f" style="min-height: 400px; height: 550px; max-height: 500px; max-width: 100%;"></canvas>'
);
instead just use
$('#canvas_div_f').append(
'<div id="lineChart_f" style="min-height: 400px; height: 550px; max-height: 500px; max-width: 100%;"></div>'
);
and while render set output to canvas.
zingchart.render({
id: 'lineChart_cob_f',
data: configTimeAndAngle,
height: "100%",
width: "100%",
output: "canvas"
});

How does vis-network get the real-time position of the center of the node and the top left corner under any circumstances?

jsfiddle demo
.wrap{
position: relative;
margin: 100px auto 0;
width: 400px;
height: 400px;
border: 1px solid black;
}
#mynetwork {
width: 100%;
height: 100%;
}
.origin {
position: absolute;
left: -5px;
top: -5px;
width: 10px;
height: 10px;
background: red;
z-index: 1;
border-radius: 50%;
}
<div class="wrap">
<div id="mynetwork"></div>
<div class="origin"></div>
</div>
// create an array with nodes
var nodes = [{
id: 1,
"shape": "dot",
label: 'node1',
x: 5,
y: 5
}, {
id: 2,
"shape": "dot",
label: 'node2',
x: 50,
y: 75
}];
// create an array with edges
var edges = [
{from: 1, to: 2},
{from: 1, to: 3}
];
// create a network
var container = document.getElementById('mynetwork');
var data = {
nodes: nodes,
edges: edges
};
var options = {
interaction: {
hover: true,
},
physics: {
enabled: false
}
};
var network = new vis.Network(container, data, options);
network.on("hoverNode", function (t) {
var nodeID = t.node;
// I want to get the real-time position of the center of the node and the upper left corner under any circumstances (zoom in, zoom out, move)
// ... ?
});
I hope that the coordinates of the node and the upper left corner can be obtained in real time. In any case, for example, the position of 'node1' is obtained when zooming in. After the node is moved, it is a new position. It should be changed. The distance from the red dot should be calculated.
const node = network.getPositions([nodeID])[nodeID]
const corner = visGraph.canvasToDOM({
x: node.x,
y: node.y
})
reference GitHub vis-networkissues/128

C3.js tootltip font

I was trying this example in `c3js.
Demo
Does anyone know how to increase the font size of the Tooltip contents (both title and value)?
Any help would be appreciated.
You can set a custom font-size to the tspan element:
tspan {
font-size: 20px;
}
Override the font value for the following selectors:
.c3-tooltip th {
/* for the header */
}
.c3-tooltip td.name {
/* for the title cells */
}
.c3-tooltip td.value {
/* for the value cells */
}
EXAMPLE
var chart = c3.generate({
data: {
columns: [
['data1', 30000, 20000, 10000, 40000, 15000, 250000],
['data2', 100, 200, 100, 40, 150, 250]
],
axes: {
data2: 'y2'
}
},
axis : {
y : {
tick: {
format: d3.format("s")
}
},
y2: {
show: true,
tick: {
format: d3.format("$")
}
}
},
tooltip: {
format: {
title: function (d) { return 'Data ' + d; },
value: function (value, ratio, id) {
var format = id === 'data1' ? d3.format(',') : d3.format('$');
return format(value);
}
// value: d3.format(',') // apply this format to both y and y2
}
}
});
.c3-tooltip th {
/* for the header */
color: red !important;
}
.c3-tooltip td.name {
/* for the title cells */
color: green;
}
.c3-tooltip td.value {
/* for the value cells */
color: blue;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.12/c3.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.12/c3.min.js"></script>
<div id='chart'></div>

Highcharts.js: scrollable innterText?

Based on the code of this Highchart example, I would like to display some text in the center of the donut circle, when a certain tile is clicked. Is it now possible, to make the displayed text scrollable when it doesn't fit nicely into the circle's inner area?
What I have so far
$(function () {
var colors = ['#8d62a0', '#ceb3d8', '#d5dddd'];
var chart = new Highcharts.Chart({
chart: {
renderTo: 'vacation-time-chart',
type: 'pie',
height: 300,
width: 300,
borderRadius: 0
},
credits: {
enabled: false
},
title: false,
tooltip: {
formatter: function() {
return '<b>'+this.y+'</b>';
}
},
plotOptions: {
pie: {
borderWidth: 6,
startAngle: 90,
innerSize: '75%',
size: '100%',
shadow: true,
// {
// color: '#000000',
// offsetX: 0,
// offsetY: 2,
// opacity: 0.7,
// width: 3
// },
dataLabels: false,
stickyTracking: false,
states: {
hover: {
enabled: false
}
},
point: {
events: {
click: function(){
this.series.chart.innerText.attr({text: this.txt});
}
}
}
}
},
series: [{
data: [
{y:40, color: colors[0], txt: 'yoyo'},
{y:10, color: colors[1], txt: 'dada'},
{y:60, color: colors[2], txt: 'this is a longer text that I would like to be scrollable. this is a longer text that I would like to be scrollable. this is a longer text that I would like to be scrollable. this is a longer text that I would like to be scrollable. this is a longer text that I would like to be scrollable.this is a longer text that I would like to be scrollable. this is a longer text that I would like to be scrollable.'}
]
// data: [
// ['Firefox', 44.2],
// ['IE7', 26.6],
// ['IE6', 20],
// ['Chrome', 3.1],
// ['Other', 5.4]
// ]
}]
},
function(chart) { // on complete
var xpos = '50%';
var ypos = '53%';
var circleradius = 102;
var boundingBox;
var series = chart.series[0];
var zones;
// Render the text
chart.innerText = chart.renderer.label('Articles mentioning XY', 135, 125).add();
boundingBox = chart.innerText.getBBox();
chart.innerText.css({
display:"inline-block",
position:"absolute",
top:"1px",
width: "150px",
height:"30px",
color: '#4572A7',
fontSize: '12px',
overflow: 'auto',
textAlign: 'block'
}).attr({
// why doesn't zIndex get the text in front of the chart?
x: series.center[0] - boundingBox.width / 2 + chart.plotLeft / 2,
y: series.center[1] + boundingBox.height / 2 + chart.plotTop,
zIndex: 999
}).add();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
<script src="http://code.highcharts.com/highcharts.js"></script>
</head>
<body>
<div id="vacation-time-chart" style="min-width: 300px; height: 300px; margin: 0 auto"></div>
<script src="testfile.js"></script>
</body>
</html>
Could anyone please help me with this? Adding overflow: auto (or scroll) to the innterText.css properties doesn't seem to be the solution here.
You can do this,
function defineInnerData(name, y, obj) { // on complete
var chart=$("#container").highcharts();
$( "#pieChartInfoText" ).remove();
var textX = chart.plotLeft + (chart.plotWidth * 0.5);
var textY = chart.plotTop + (chart.plotHeight * 0.5);
var span = '<span id="pieChartInfoText" style="position:absolute; text-align:center;left: 235px;top:210px;width: 130px;">';
span += '<span style="font-size: 15px">'+ y +'</span><br>';
span += '<span style="font-size: 12px">'+ name +'</span>';
span += '</span>';
$("#addText").append(span);
span = $('#pieChartInfoText');
span.css('left', textX + (span.width() * -0.5));
span.css('top', textY + (span.height() * -0.5));
}
defineInnerData("", "Tap the slices of this chart to see more");
And call it inside,
series: {
cursor: 'pointer',
point: {
events: {
mouseOver: function() {
console.log(this)
defineInnerData(this.name, this.y, this);
}
}
},
DEMO

Categories