I have a line chart which shows multiple lines. X-axis represents date and Y-axis represents numeric reading. The lines represent the category PZ-1, PZ-2 & PZ-3. I have managed to remove the horizontal line that connect between the dots but now I want to connect the dots vertically that aligns based on the date on X-axis. I do not want to rotate the line or swap X-axis position with Y-axis and vice versa. Does anyone know how I can achieve the vertical line? Thank you
Below is my current code:
const data = {
datasets: [
{label: 'PZ-1',data:[{x:'2022-02-25', y:40.551},{x:'2022-03-01', y:35.889},{x:'2022-03-02', y:34.68},{x:'2022-03-03', y:33.182},{x:'2022-03-04', y:30.82},{x:'2022-03-05', y:29.864},{x:'2022-03-08', y:28.413},{x:'2022-03-10', y:28.413},{x:'2022-03-12', y:28.424},{x:'2022-03-15', y:25.578},{x:'2022-03-17', y:27.07},{x:'2022-03-19', y:27.42},{x:'2022-03-22', y:27.478},{x:'2022-03-24', y:22.817},{x:'2022-03-26', y:22.576},{x:'2022-03-29', y:22.326},{x:'2022-03-31', y:22.011},{x:'2022-04-02', y:21.672},{x:'2022-04-05', y:21.561},{x:'2022-04-07', y:21.307},{x:'2022-04-09', y:34.988},{x:'2022-04-12', y:28.89},{x:'2022-04-14', y:28.618},{x:'2022-04-17', y:28.862},{x:'2022-04-19', y:27.727},{x:'2022-04-21', y:27.493},{x:'2022-04-23', y:27.149},{x:'2022-04-26', y:25.862},{x:'2022-04-28', y:25.59},{x:'2022-04-30', y:25.37},{x:'2022-05-04', y:24.79},{x:'2022-05-06', y:24.927}],backgroundColor: '#778899',borderColor: '#778899',borderWidth: 1,showLine: false},{label: 'PZ-2',data:[{x:'2022-02-22', y:40.994},{x:'2022-03-01', y:55.537},{x:'2022-03-02', y:62.907},{x:'2022-03-03', y:59.462},{x:'2022-03-04', y:55.175},{x:'2022-03-05', y:53.294},{x:'2022-03-08', y:50.284},{x:'2022-03-10', y:49.89},{x:'2022-03-12', y:50.334},{x:'2022-03-15', y:47.137},{x:'2022-03-17', y:48.726},{x:'2022-03-19', y:48.294},{x:'2022-03-22', y:48.002},{x:'2022-03-24', y:40.156},{x:'2022-03-26', y:39.857},{x:'2022-03-29', y:39.678},{x:'2022-03-31', y:39.331},{x:'2022-04-02', y:36.719},{x:'2022-04-05', y:36.438},{x:'2022-04-07', y:36.258},{x:'2022-04-09', y:72.891},{x:'2022-04-12', y:59.97},{x:'2022-04-14', y:59.578},{x:'2022-04-17', y:59.781},{x:'2022-04-19', y:60.408},{x:'2022-04-21', y:60.309},{x:'2022-04-23', y:59.82},{x:'2022-04-26', y:61.679},{x:'2022-04-28', y:61.539},{x:'2022-04-30', y:61.187},{x:'2022-05-04', y:59.871},{x:'2022-05-06', y:59.63}],backgroundColor: '#DB7093',borderColor: '#DB7093',borderWidth: 1,showLine: false},{label: 'PZ-3',data:[{x:'2022-02-22', y:51.455},{x:'2022-03-01', y:44.882},{x:'2022-03-02', y:58.791},{x:'2022-03-03', y:55.118},{x:'2022-03-04', y:48.364},{x:'2022-03-05', y:47.498},{x:'2022-03-08', y:45.477},{x:'2022-03-10', y:44.859},{x:'2022-03-12', y:45.468},{x:'2022-03-15', y:39.599},{x:'2022-03-17', y:40.561},{x:'2022-03-19', y:39.993},{x:'2022-03-22', y:40.232},{x:'2022-03-24', y:33.061},{x:'2022-03-26', y:33.169},{x:'2022-03-29', y:32.99},{x:'2022-03-31', y:32.849},{x:'2022-04-02', y:31.811},{x:'2022-04-05', y:31.412},{x:'2022-04-07', y:31.223},{x:'2022-04-09', y:84.506},{x:'2022-04-12', y:74.415},{x:'2022-04-14', y:74.079},{x:'2022-04-17', y:73.876},{x:'2022-04-19', y:87.873},{x:'2022-04-21', y:87.748},{x:'2022-04-23', y:87.45},{x:'2022-04-26', y:76.555},{x:'2022-04-28', y:76.401},{x:'2022-04-30', y:76.649},{x:'2022-05-04', y:75.585},{x:'2022-05-06', y:75.748}],backgroundColor: '#8B008B',borderColor: '#8B008B',borderWidth: 1,showLine: false}
]
};
// config
const config = {
type: 'line',
data,
options: {
layout: {
padding: {
left: 5
}
},
indexAxis: 'x',
scales: {
y: {
beginAtZero: true
},
x:{
reverse: false,
type: 'time',
time: {
tooltipFormat: 'dd-MMM-yy',
displayFormats: {
day: 'dd-MMM-yy'
}
},
ticks: {
source: 'date',
autoSkip: false
}
}
}
}
};
// render init block
const myChart = new Chart(
document.getElementById('myChart'),
config
);
* {
margin: 0;
padding: 0;
font-family: sans-serif;
}
.chartCard {
overflow:auto;
background: rgba(255, 26, 104, 0.2);
display: flex;
align-items: center;
justify-content: center;
}
.chartBox {
padding: 20px;
border-radius: 20px;
border: solid 3px rgba(255, 26, 104, 1);
background: white;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Line Chart</title>
</head>
<body>
<div class="chartCard">
<div class="chartBox">
<canvas id="myChart" style="position: relative;height:1200px;width:1400px"></canvas>
</div>
</div>
<script src="https://rawgit.com/moment/moment/2.2.1/min/moment.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
</body>
</html>
You can use the Plugin Core API and define a beforeDraw hook that draws the lines directly on the canvas through the CanvasRenderingContext2D.
Please take a look at your amended code and see how it works.
const data = {
datasets: [
{label: 'PZ-1',data:[{x:'2022-02-25', y:40.551},{x:'2022-03-01', y:35.889},{x:'2022-03-02', y:34.68},{x:'2022-03-03', y:33.182},{x:'2022-03-04', y:30.82},{x:'2022-03-05', y:29.864},{x:'2022-03-08', y:28.413},{x:'2022-03-10', y:28.413},{x:'2022-03-12', y:28.424},{x:'2022-03-15', y:25.578},{x:'2022-03-17', y:27.07},{x:'2022-03-19', y:27.42},{x:'2022-03-22', y:27.478},{x:'2022-03-24', y:22.817},{x:'2022-03-26', y:22.576},{x:'2022-03-29', y:22.326},{x:'2022-03-31', y:22.011},{x:'2022-04-02', y:21.672},{x:'2022-04-05', y:21.561},{x:'2022-04-07', y:21.307},{x:'2022-04-09', y:34.988},{x:'2022-04-12', y:28.89},{x:'2022-04-14', y:28.618},{x:'2022-04-17', y:28.862},{x:'2022-04-19', y:27.727},{x:'2022-04-21', y:27.493},{x:'2022-04-23', y:27.149},{x:'2022-04-26', y:25.862},{x:'2022-04-28', y:25.59},{x:'2022-04-30', y:25.37},{x:'2022-05-04', y:24.79},{x:'2022-05-06', y:24.927}],backgroundColor: '#778899',borderColor: '#778899',borderWidth: 1,showLine: false},{label: 'PZ-2',data:[{x:'2022-02-22', y:40.994},{x:'2022-03-01', y:55.537},{x:'2022-03-02', y:62.907},{x:'2022-03-03', y:59.462},{x:'2022-03-04', y:55.175},{x:'2022-03-05', y:53.294},{x:'2022-03-08', y:50.284},{x:'2022-03-10', y:49.89},{x:'2022-03-12', y:50.334},{x:'2022-03-15', y:47.137},{x:'2022-03-17', y:48.726},{x:'2022-03-19', y:48.294},{x:'2022-03-22', y:48.002},{x:'2022-03-24', y:40.156},{x:'2022-03-26', y:39.857},{x:'2022-03-29', y:39.678},{x:'2022-03-31', y:39.331},{x:'2022-04-02', y:36.719},{x:'2022-04-05', y:36.438},{x:'2022-04-07', y:36.258},{x:'2022-04-09', y:72.891},{x:'2022-04-12', y:59.97},{x:'2022-04-14', y:59.578},{x:'2022-04-17', y:59.781},{x:'2022-04-19', y:60.408},{x:'2022-04-21', y:60.309},{x:'2022-04-23', y:59.82},{x:'2022-04-26', y:61.679},{x:'2022-04-28', y:61.539},{x:'2022-04-30', y:61.187},{x:'2022-05-04', y:59.871},{x:'2022-05-06', y:59.63}],backgroundColor: '#DB7093',borderColor: '#DB7093',borderWidth: 1,showLine: false},{label: 'PZ-3',data:[{x:'2022-02-22', y:51.455},{x:'2022-03-01', y:44.882},{x:'2022-03-02', y:58.791},{x:'2022-03-03', y:55.118},{x:'2022-03-04', y:48.364},{x:'2022-03-05', y:47.498},{x:'2022-03-08', y:45.477},{x:'2022-03-10', y:44.859},{x:'2022-03-12', y:45.468},{x:'2022-03-15', y:39.599},{x:'2022-03-17', y:40.561},{x:'2022-03-19', y:39.993},{x:'2022-03-22', y:40.232},{x:'2022-03-24', y:33.061},{x:'2022-03-26', y:33.169},{x:'2022-03-29', y:32.99},{x:'2022-03-31', y:32.849},{x:'2022-04-02', y:31.811},{x:'2022-04-05', y:31.412},{x:'2022-04-07', y:31.223},{x:'2022-04-09', y:84.506},{x:'2022-04-12', y:74.415},{x:'2022-04-14', y:74.079},{x:'2022-04-17', y:73.876},{x:'2022-04-19', y:87.873},{x:'2022-04-21', y:87.748},{x:'2022-04-23', y:87.45},{x:'2022-04-26', y:76.555},{x:'2022-04-28', y:76.401},{x:'2022-04-30', y:76.649},{x:'2022-05-04', y:75.585},{x:'2022-05-06', y:75.748}],backgroundColor: '#8B008B',borderColor: '#8B008B',borderWidth: 1,showLine: false}]};
const config = {
type: 'line',
plugins: [{
beforeDraw: chart => {
var ctx = chart.ctx;
ctx.save();
ctx.strokeStyle = '#aaaaaa';
var xAxis = chart.scales.x;
var yAxis = chart.scales.y;
xAxis.ticks.forEach((t, i) => {
const dateString = moment(t.value).format('YYYY-MM-DD');
const values = chart.data.datasets
.filter((ds, i) => !chart.getDatasetMeta(i).hidden)
.map(ds => ds.data.find(v => v.x == dateString))
.filter(v => v != undefined)
.map(o => o.y);
if (values.length > 1) {
var x = xAxis.getPixelForTick(i);
var yTop = yAxis.getPixelForValue(Math.max(...values));
var yBottom = yAxis.getPixelForValue(Math.min(...values));
ctx.beginPath();
ctx.moveTo(x, yBottom);
ctx.lineTo(x, yTop);
ctx.stroke();
}
});
ctx.restore();
}
}],
data,
options: {
layout: {
padding: {
left: 5
}
},
scales: {
y: {
beginAtZero: true
},
x: {
offset: true,
type: 'time',
time: {
tooltipFormat: 'dd-MMM-yy',
displayFormats: {
day: 'dd-MMM-yy'
}
},
grid: {
display: false
},
ticks: {
source: 'date',
autoSkip: false
}
}
}
}
};
new Chart('myChart', config );
<script src="https://rawgit.com/moment/moment/2.2.1/min/moment.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
<canvas id="myChart" height="140"></canvas>
I learned cytoscape.js and related extension some days and tried its many amazing features.
I find that when the number of children is greater than 5 and expand a node, the whole graph fly out of screen.
I constructed more complex data which own 3 parent nodes and 5 child nodes per parent node. There is a connection between any two child nodes.
So there are 3 parent nodes, 15 children nodes and 14+13+12..1 links.
To sum up, when there are more links, the layout behavior looks abnormal.
See my demo below.
You can modify parameters of my function getInitData() to see the effect.
document.addEventListener('DOMContentLoaded', function(){
var cy = window.cy = cytoscape({
container: document.getElementById('cy'),
ready: function(){
var api = this.expandCollapse({
layoutBy: {
name: "cose-bilkent",
animate: 'end',
randomize: false,
fit: false,
idealEdgeLength : 150
},
fisheye: false,
animate: true,
undoable: false
});
api.collapseAll();
},
style: [
{
selector: 'node',
style: {
'background-color': '#ad1a66'
}
},
{
selector: ':parent',
style: {
'background-opacity': 0.333
}
},
{
selector: "node.cy-expand-collapse-collapsed-node",
style: {
"background-color": "darkblue",
"shape": "rectangle"
}
},
{
selector: 'edge',
style: {
'width': 3,
'line-color': '#ad1a66'
}
},
{
selector: 'edge.meta',
style: {
'width': 2,
'line-color': 'red'
}
},
{
selector: ':selected',
style: {
"border-width": 3,
"border-color": '#DAA520'
}
}
],
elements : getInitData(3, 5)
});
var api = cy.expandCollapse('get');
var elements = null;
});
function getInitData(parentNum, childrenNum){
var data = [], children = [], i, j, n;
for(i = 0; i < parentNum; i++){
n = "parent"+i;
data.push({"group":'nodes',data:{"id":n}});
for(j = 0; j < childrenNum; j++){
children.push({"group":'nodes',data:{"id":n+"_child_"+j, parent:n}});
}
}
var s,t;
for(i = 0; i < children.length - 1; i++){
s = children[i].data.id;
for(j = i+1; j < children.length; j++){
t = children[j].data.id;
data.push({"group":'edges',data:{"id":s+"_"+t, source:s, target:t}});
}
}
return data.concat(children);
}
body {
font-family: helvetica neue, helvetica, liberation sans, arial, sans-serif;
font-size: 14px;
}
#cy {
z-index: 999;
width: 100%;
height: 100%;
}
h1 {
opacity: 0.5;
font-size: 1em;
font-weight: bold;
}
<script src="https://code.jquery.com/jquery-2.0.3.min.js"></script>
<script src="https://unpkg.com/cytoscape#3.1.0/dist/cytoscape.min.js"></script>
<!-- for testing with local version of cytoscape.js -->
<!--<script src="../cytoscape.js/build/cytoscape.js"></script>-->
<script src="https://unpkg.com/cytoscape-cose-bilkent#4.0.0/cytoscape-cose-bilkent.js"></script>
<script src="https://unpkg.com/cytoscape-expand-collapse#3.1.1/cytoscape-expand-collapse.js"></script>
<div id="cy"></div>
Solution one:
Your graph doesn't have any fitting logic. You can implement that yourself with the two methods cy.center(), which centers the graph to the current viewport and cy.fit(), which zooms the graph to the right position. You would have to call these mehtods everytime you change your graph, e.g. when you add a node, remove a node or, like in your case, expand and collapse. You can do that by binding these events and calling the said methods there.
Binding, as you know from the last question works like this:
cy.unbind('event');
cy.bind('event', 'target', function (event) {...});
Solution two:
You can alternatively, if possible (not all layouts can do this), set the method to fit: true,, which fits the graph with cy.fit(); and cy.center(); internally.
Additional problem and solution for that:
You said, that your graph looks bad when you only have one node in it, so to circumvent that, you can set the padding property of 'cose-bilkent' to a higher number. You can do that at the initialization in the options.
document.addEventListener('DOMContentLoaded', function() {
var padding = 10;
var cy = window.cy = cytoscape({
container: document.getElementById('cy'),
layout: {
name: 'cose-bilkent',
animate: false,
randomize: true
},
style: [{
selector: 'node',
style: {
'background-color': '#ad1a66'
}
},
{
selector: 'edge',
style: {
'width': 3,
'line-color': '#ad1a66'
}
}
],
elements: [{
"data": {
"id": "glyph9"
}
}]
});
document.getElementById("add").addEventListener("click", function() {
padding += 10;
var layout = cy.layout({
name: 'cose-bilkent',
animate: false,
padding: padding
});
layout.run();
});
});
body {
font: 14px helvetica neue, helvetica, arial, sans-serif;
}
#cy {
height: 90%;
width: 100%;
position: absolute;
float: left;
}
button {
margin-right: 10px;
}
<!DOCTYPE>
<html>
<head>
<title>cytoscape-cose-bilkent.js demo</title>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<script src="https://code.jquery.com/jquery-2.0.3.min.js"></script>
<script src="https://unpkg.com/cytoscape#3.1.0/dist/cytoscape.min.js"></script>
<!-- for testing with local version of cytoscape.js -->
<!--<script src="../cytoscape.js/build/cytoscape.js"></script>-->
<script src="https://unpkg.com/cytoscape-cose-bilkent#4.0.0/cytoscape-cose-bilkent.js"></script>
<script src="https://unpkg.com/cytoscape-expand-collapse#3.1.1/cytoscape-expand-collapse.js"></script>
</head>
<body>
<button id="add" type="button">Add padding</button>
<div id="cy"></div>
</body>
</html>
I am trying to make a Mapbox map that would show data chronologically for a given year, and I am trying to make the year be specified automatically in sequence.
However, I am having a problem using setInterval to move the time slider in the following script automatically, and it gives me a [object HTMLLabelElement] without the slider moving.
Am I setting the setInterval to the wrong function?
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title></title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.29.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.29.0/mapbox-gl.css' rel='stylesheet' />
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<style>
.map-overlay {
font: 12px/20px 'Helvetica Neue', Arial, Helvetica, sans-serif;
position: absolute;
width: 25%;
top: 0;
left: 0;
padding: 10px;
}
.map-overlay .map-overlay-inner {
background-color: #fff;
box-shadow:0 1px 2px rgba(0, 0, 0, 0.20);
border-radius: 3px;
padding: 10px;
margin-bottom: 10px;
}
.map-overlay h2 {
line-height: 24px;
display: block;
margin: 0 0 10px;
}
.map-overlay input {
background-color: transparent;
display: inline-block;
width: 100%;
position: relative;
margin: 0;
cursor: ew-resize;
}
</style>
<div id='map'></div>
<div class='map-overlay top'>
<div class='map-overlay-inner'>
<h2>Tel-Aviv Cinemas 1914-2016</h2>
<label id='Year'></label>
<input id='slider' type='range' min='0' max='102' step='1' value='0' />
</div>
<div class='map-overlay-inner'></div>
</div>
<script src="https://d3js.org/d3.v4.js"></script>
<script>
mapboxgl.accessToken = 'pk.eyJ1Ijoia3Z5YiIsImEiOiJjaXUwMHEwcmgwMDAxMnlvM3NzMm0xbGozIn0.JL_eeNZL_lDoJxijNqFPoA';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/dark-v9',
center: [34.775981, 32.081287],
zoom: 11
});
var Years = ['1914', '1915', '1916', '1917', '1918', '1919', '1920', '1921', '1922', '1923', '1924', '1925', '1926', '1927', '1928', '1929', '1930', '1931', '1932', '1933', '1934', '1935', '1936', '1937', '1938', '1939', '1940', '1941', '1942', '1943', '1944', '1945', '1946', '1947', '1948', '1949', '1950', '1951', '1952', '1953', '1954', '1955', '1956', '1957', '1958', '1959', '1960', '1961', '1962', '1963', '1964', '1965', '1966', '1967', '1968', '1969', '1970', '1971', '1972', '1973', '1974', '1975', '1976', '1977', '1978', '1979', '1980', '1981', '1982', '1983', '1984', '1985', '1986', '1987', '1988', '1989', '1990', '1991', '1992', '1993', '1994', '1995', '1996', '1997', '1998', '1999', '2000', '2001', '2002', '2003', '2004', '2005', '2006', '2007', '2008', '2009', '2010', '2011', '2012', '2013', '2014', '2015', '2016'];
function filterBy(Year) {
var filters = ['==', 'Year', Year];
map.setFilter('cinema-circles', filters);
map.setFilter('cinema-labels', filters);
// Set the label to the Year
document.getElementById('Year').textContent = Year;
}
map.on('load', function() {
// Data courtesy of https://earthquake.usgs.gov/
// Query for significant earthquakes in 2015 URL request looked like this:
// https://earthquake.usgs.gov/fdsnws/event/1/query
// ?format=geojson
// &starttime=2015-01-01
// &endtime=2015-12-31
// &minmagnitude=6'
//
// Here we're using d3 to help us make the ajax request but you can use
// Any request method (library or otherwise) you wish.
d3.json('https://cldex.net/visual/cinema_telaviv.geojson', function(err, data) {
if (err) throw err;
// Create a Year property value based on time
// used to filter against.
data.features = data.features.map(function(d) {
return d;
});
map.addSource('cinemas', {
'type': 'geojson',
'data': data
});
map.addLayer({
'id': 'cinema-circles',
'type': 'circle',
'source': 'cinemas',
'paint': {
'circle-color': {
property: 'sqrt',
stops: [
[0, '#f1f075'],
[1500, '#e55e5e']
]
},
'circle-opacity': 0.75,
'circle-radius': 20
}
});
map.addLayer({
'id': 'cinema-labels',
'type': 'symbol',
'source': 'cinemas',
'layout': {
'text-field': '{Cinema}',
'text-font': ['Open Sans Bold', 'Arial Unicode MS Bold'],
'text-size': 12
},
'paint': {
'text-color': 'rgba(0,0,0,0.5)'
}
});
// Set filter to first Year of the Year
// 0 = 1914
filterBy(1914);
document.getElementById('slider').addEventListener('input', function(e) {
var Year = window.setInterval(function() { parseInt(Years[e.target.value]) }, 1000);
filterBy(Year);
});
// Create a popup, but don't add it to the map yet.
var popup = new mapboxgl.Popup({
closeButton: false,
closeOnClick: false
});
map.on('mousemove', function(e) {
var features = map.queryRenderedFeatures(e.point, { layers: ['cinema-circles'] });
// Change the cursor style as a UI indicator.
map.getCanvas().style.cursor = (features.length) ? 'pointer' : '';
if (!features.length) {
popup.remove();
return;
}
var feature = features[0];
// Populate the popup and set its coordinates
// based on the feature found.
popup.setLngLat(feature.geometry.coordinates)
.setHTML(feature.properties.Cinema+'<b> Cinema Information</b>'+'<br><b>Number: </b>'+feature.properties.Number+'<br><b>Number of Screens: </b>'+feature.properties.Screens+'<br><b>Number of Seats: </b>'+feature.properties.Seatss)
.addTo(map);
});
});
});
</script>
</body>
</html>
My Data can be found in a geojson: https://cldex.net/visual/cinema_telaviv.geojson
Your usage of setInterval is incorrect currently, and you have a few syntax errors.
Where you currently have this:
var Year = set.Interval(function() { parseInt(Years[e.target.value] }, 1000);
Try this instead:
var Year = window.setInterval(function() { parseInt(Years[e.target.value]) }, 1000);
You may still have some issues elsewhere, but I was having difficulty getting your data endpoint parsing correctly in d3. It may help to post up a codepen or something of this to get more help if you need it.
Edit:
I've created a codepen from your current setup, which may help you to tinker around a little easier: http://codepen.io/anon/pen/EZjKqM
As you can see I've made a few more changes, such as adding something to increment through the array of years which uses your filterBy function. It doesn't work perfectly, but you can see how it's parsing the data correctly and changing the year by the second.
This is achieved by using the following JS:
// Automatically cycle through years.
var yearSlider = document.getElementById('slider');
var curYearIndex = -1;
function advanceYear() {
++curYearIndex;
if (curYearIndex >= years.length) {
curYearIndex = 0;
}
return years[curYearIndex];
}
var cycleYears = window.setInterval(function() {
var currentYear = advanceYear();
filterBy(parseInt(currentYear));
}, 1000);
Unfortunately this is probably as far as I'll get with this, as I've other things to work on, but it perhaps gives you a bit of a starting point.
#Wakeuphate, this is an awesome solution (up-voted accordingly). I was able to implement quickly for my project.
It seems the missing item was moving the slider as well. I was able to achieve this by simply adding the following line:
document.getElementById('slider').value = currentYear;
So that the full function looks like:
var cycleYears = window.setInterval(function() {
var currentYear = advanceYear();
filterBy(parseInt(currentYear));
document.getElementById('slider').value = currentYear;
}, 1000);
I just need to align the Chart Legend so it don't look too messy as the default shows, here is an example what I'm trying to achieve:
Please give some code suggestions: https://jsfiddle.net/holp/68wf75r8/
new Chart(document.getElementById("field-0"), {
type: 'pie',
data: {
labels: ["Chat", "Prospeção", "Whatsapp", "Trial", "Site", "Telefone", "E-mail", "Evento"],
datasets: [{
data: [700, 400, 200, 150, 80, 50, 20, 10],
borderWidth: 2,
hoverBorderWidth: 10,
backgroundColor: pieColors,
hoverBackgroundColor: pieColors,
hoverBorderColor: pieColors,
borderColor: pieColors
}]
},
options: {
legend: {
labels: {
padding: 20
}
}
}
});
There is legend.labels.generateLabels hook you generally can use to customise your legend labels.
I found out, that you can put something like below to adjust Chart.js calculations.
generateLabels: function (chart) {
chart.legend.afterFit = function () {
var width = this.width; // guess you can play with this value to achieve needed layout
this.lineWidths = this.lineWidths.map(function(){return width;});
};
// here goes original or customized code of your generateLabels callback
}
Weird thing that there is no actual configuration option to achieve this.
Chartjs v2 creates an overhead to handle the legends. Basically what you are looking for is to leverage the usage of generateLabels.
The key point to bare in mind is that you need to return an valid array of legend objects.
This plunker describes the solution.
Main focus on this part:
generateLabels: (chart) => {
chart.legend.afterFit = function () {
var width = this.width;
console.log(this);
this.lineWidths = this.lineWidths.map( () => this.width-12 );
this.options.labels.padding = 30;
this.options.labels.boxWidth = 15;
};
var data = chart.data;
//https://github.com/chartjs/Chart.js/blob/1ef9fbf7a65763c13fa4bdf42bf4c68da852b1db/src/controllers/controller.doughnut.js
if (data.labels.length && data.datasets.length) {
return data.labels.map((label, i) => {
var meta = chart.getDatasetMeta(0);
var ds = data.datasets[0];
var arc = meta.data[i];
var custom = arc && arc.custom || {};
var getValueAtIndexOrDefault = this.getValueAtIndexOrDefault;
var arcOpts = chart.options.elements.arc;
var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);
return {
text: label,
fillStyle: fill,
strokeStyle: stroke,
lineWidth: bw,
hidden: isNaN(ds.data[i]) || meta.data[i].hidden,
// Extra data used for toggling the correct item
index: i
};
});
}
return [];
}
I tried to do as advised by the comments above. But to see that it is really difficult. It’s better and easier for me to set:
legend: {display: FALSE, ..} `, and then render the legend using html (angular, react, view .. another render template):
// part of angualr model class
public dataSets = [{
label: "New Deals",
backgroundColor: "#88B2FF",
data: [26, 15, 5],
},
{
label: "Active Deals",
backgroundColor: "#397FFF",
data: [7, 13, 22],
},
....
this.chart = new Chart(ctx, {
type: "roundedBar",
data: {
labels: this.xLabels,
datasets: this.dataSets,
},
<div style="width: 380px;height: 200px; display: inline-block;">
<canvas id="chart" aria-label="Hello ARIA World" role="img"></canvas>
</div>
<!-- This is angular template -->
<ul class="legend">
<li *ngFor="let set of dataSets">
<i [style.backgroundColor]="set.backgroundColor" class="icon"></i>
<label>
{{ set.label }}
</label>
</li>
</ul>
<style>
.legend {
display: flex;
text-align: center;
justify-content: space-between;
font-size: 10px;
line-height: 12px;
}
.icon {
width: 10px;
height: 10px;
border-radius: 50%;
display: inline-block;
}
</style>