Responsive CHART svg - javascript

I'm trying to do responsive chart... i used this but its not responsive just when i refresh the page it scaled to right width... Any other ideas then i had ?
And i try to set a div with viewBox and preserveAspectRatio.. But the same result..
var graph = new Rickshaw.Graph( {
element: document.querySelector("#chart"),
height: 100,
series: [{
color: '#1abc9c',
data: [
{ x: 0, y: 50 },
{ x: 1, y: 50 },
{ x: 2, y: 40 },
{ x: 3, y: 40 },
{ x: 4, y: 30 },
{ x: 5, y: 20 },
{ x: 6, y: 0 },
{ x: 7, y: 0 } ]
}]
});
graph.render();
var svg = d3.select('.chart-container').append("svg")
.attr("width", '100%')
.attr("height", '100%')
.attr('viewBox','0 0 '+Math.min(width,height)+' '+Math.min(width,height))
.attr('preserveAspectRatio','xMinYMin')
.append("g")
.attr("transform", "translate(" + Math.min(width,height) / 2 + "," + Math.min(width,height) / 2 + ")");
<div class="chart-container" id="chart"></div>

Ok thanks to Mark Sizer.. Solution is here :
var seriesData = [ [
{ x: 0, y: 50 },
{ x: 1, y: 50 },
{ x: 2, y: 50 },
{ x: 3, y: 40 },
{ x: 4, y: 40 },
{ x: 5, y: 20 },
{ x: 6, y: 0 },
{ x: 7, y: 0 } ] ];
var graph = new Rickshaw.Graph( {
element: document.getElementById("chart"),
renderer: 'area',
series: [
{
data: seriesData[0],
color: '#1abc9c'
}
]
} );
var resize = function() {
graph.configure({
width: window.innerWidth * 0,
height: window.innerHeight * 0.20
});
graph.render();
}
window.addEventListener('resize', resize);
resize();
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rickshaw/1.5.1/rickshaw.js"></script>
<div id="chart"></div>

Related

Projecting a Point onto a Line

I'm trying to find the red point coordinates in this image where the points are projected on to a line formed by the jagged line's start and end point.
I'm using the code from this article, What are the coordinates of the projected point on a line segment using the perp dot product ?, but it appears that I'm doing something incorrectly. Could someone please help me achieve the result in the above image. Thank you!
const points = [
{ x: 2, y: 2 },
{ x: 3, y: 1 },
{ x: 4, y: 4 },
{ x: 5, y: 3 },
{ x: 6, y: 6 },
{ x: 7, y: 5 },
{ x: 8, y: 6 }
];
const first = points[0];
const last = points.slice(-1)[0];
const d = {
x: last.x - first.x,
y: last.y - first.y
};
function dot_product(pt1,pt2)
{
return pt1.x*pt2.x + pt1.y*pt2.y;
}
function drawChart() {
let projected = [];
for (var i=0; i<points.length; i++)
{
p = points[i];
let e2 = {x:p.x-first.x,y:p.y-first.y};
let dot = dot_product(d,e2);
let len2 = d.x * d.x + d.y * d.y;
let v = {
x: first.x + (dot*d.x)/ len2,
y: first.y + (dot*d.y)/ len2,
}
projected.push(v);
}
chart = new CanvasJS.Chart("chartContainer", {
animationEnabled: false,
theme: "light2",
data: [
{
type: "line",
color: "blue",
lineDashType: "dash",
indexLabelFontSize: 16,
dataPoints: [points[0],points.slice(-1)[0]]
},
{
type: "line",
color: "blue",
indexLabelFontSize: 16,
dataPoints: points
},
{
type: "line",
color: "red",
indexLabelFontSize: 16,
dataPoints: projected
}
]
});
chart.render();
}
drawChart();
html
{
font-family: sans-serif;
}
body
{
width: 80%;
position: relative;
}
#chartContainer { height: 400px; width: 320px; }
<script src="https://canvasjs.com/assets/script/canvasjs.min.js"></script>
<div id="chartContainer"></div>

D3 v4 re-rendering nested objects, having problems with `enter()` and `exit()`

I'm trying to display a number of circles wrapped in <g> elements. This wrapping thing is important for me as in real world I want other elements to be in my group as well. The problem is, circles in the below example wont get displayed by first render(myObjects) call, and if you hit render button they are shown.
Second problem is when I press update the previously drawn circles are not removed and the new ones will render on top of them.
var svg = d3.select('body').append('svg')
.attr('width', 250)
.attr('height', 250);
//render the data
function render(datum) {
//Bind
var groups = svg.selectAll("g").data(datum);
groups.enter().append('g');
var circles = groups.append("circle").attr('r', 10)
.attr('cx', function(d) {
return d.x;
})
.attr('cy', function(d) {
return d.y;
});
groups.exit().remove();
}
var myObjects = [{ x: 100, y: 100 }, { x: 130, y: 120 }, { x: 80, y: 180 }, { x: 180, y: 80 }, { x: 180, y: 40 }];
var newObjects = [{ x: 200, y: 120 }, { x: 150, y: 160 }, { x: 20, y: 80 }, { x: 80, y: 60 }, { x: 100, y: 20 }];
function removeEls() {
svg.selectAll('circle').remove();
}
render(myObjects);
svg {
width: 500px;
height: 500px
}
button {
float: left
}
text {
text-anchor: middle;
}
circle {
fill: orange;
stroke: orange;
fill-opacity: 0.5;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<button onclick="render(newObjects)">Update</button>
<button onclick="render(myObjects);">Render</button>
<button onclick="removeEls();">removeEls</button>
You have 2 little issues in your code :
You have added the circles in the groups instead of groups.enter(), hence the issue of not displaying the circles at the first draw
You forget to add an identifier when you add the data, so the script is unable to detect which data is new and which data is not. I added a d.x/d.y identifier, which works in your case, but I advice you to add a true unique identifier. Like add a field id in your list of objects.
Here is the snippet with the corrections :
var svg = d3.select('body').append('svg')
.attr('width', 250)
.attr('height', 250)
.append('g');
//render the data
function render(datum) {
//Bind
var groups = svg.selectAll("g").data(datum, d => (d.x)/(d.y));
var groupEnter = groups.enter().append('g');
var circles = groupEnter.append("circle").attr('r', 10)
.attr('cx', function(d) {
return d.x;
})
.attr('cy', function(d) {
return d.y;
});
groups.exit().remove();
}
var myObjects = [{ x: 100, y: 100 }, { x: 130, y: 120 }, { x: 80, y: 180},{ x: 180, y: 80 }, { x: 180, y: 40 }];
var newObjects = [{ x: 200, y: 120 }, { x: 150, y: 160 }, { x: 20, y: 80}, { x: 80, y: 60 }, { x: 100, y: 20 }];
function removeEls() {
svg.selectAll('circle').remove();
}
render(myObjects);
svg {
width: 500px;
height: 500px
}
button {
float: left
}
text {
text-anchor: middle;
}
circle {
fill: orange;
stroke: orange;
fill-opacity: 0.5;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<button onclick="render(newObjects)">Update</button>
<button onclick="render(myObjects);">Render</button>
<button onclick="removeEls();">removeEls</button>

d3 multiline update path and transition path does not work with nested data

I want to update the circles and the paths in this graph with a transition. However it does not work.
I am not very experienced with D3. How can I make my code better? Changing data structure is no problem. I want bind the data to graph with exit() remove() and enter() without deleting whole graph and add the data every time I update again. I do not know how to use enter() exit() and remove() for nested data. One time for the whole group and the other side updating the circle and paths. The ID should be fixed.
Here I have a little single line example from d3noob.
here is a JS Fiddle
var data = [{
id: 'p1',
points: [{
point: {
x: 10,
y: 10
}
}, {
point: {
x: 100,
y: 30
}
}]
},
{
id: 'p2',
points: [{
point: {
x: 30,
y: 100
}
}, {
point: {
x: 230,
y: 30
}
},
{
point: {
x: 50,
y: 200
}
},
{
point: {
x: 50,
y: 300
}
},
]
}
];
var svg = d3.select("svg");
var line = d3.line()
.x((d) => d.point.x)
.y((d) => d.point.y);
function updateGraph() {
console.log('dataset contains', data.length, 'item(s)')
var allGroup = svg.selectAll(".pathGroup").data(data, function(d) {
return d.id
});
var g = allGroup.enter()
.append("g")
.attr("class", "pathGroup")
allGroup.exit().remove()
g.append("path")
.attr("class", "line")
.attr("stroke", "red")
.attr("stroke-width", "1px")
.transition()
.duration(500)
.attr("d", function(d) {
return line(d.points)
});
g.selectAll("path")
.transition()
.duration(500)
.attr("d", function(d) {
return line(d.points)
});
g.selectAll("circle")
.data(d => d.points)
.enter()
.append("circle")
.attr("r", 4)
.attr("fill", "teal")
.attr("cx", function(d) {
return d.point.x
})
.attr("cy", function(d) {
return d.point.y
})
.exit().remove()
}
updateGraph()
document.getElementById('update').onclick = function(e) {
data = [{
id: 'p1',
points: [{
point: {
x: 10,
y: 10
}
}, {
point: {
x: 100,
y: 30
}
}]
},
{
id: 'p2',
points: [{
point: {
x: 30,
y: 100
}
}, {
point: {
x: 230,
y: 30
}
},
{
point: {
x: 50,
y: 300
}
},
]
}
];
updateGraph()
}
$('#cb1').click(function() {
if ($(this).is(':checked')) {
data = [{
id: 'p1',
points: [{
point: {
x: 10,
y: 10
}
}, {
point: {
x: 100,
y: 30
}
}]
},
{
id: 'p2',
points: [{
point: {
x: 30,
y: 100
}
}, {
point: {
x: 230,
y: 30
}
},
{
point: {
x: 50,
y: 200
}
},
{
point: {
x: 50,
y: 300
}
},
]
}
];
} else {
data = [{
id: 'p1',
points: [{
point: {
x: 10,
y: 10
}
}, {
point: {
x: 100,
y: 30
}
}]
}];
}
updateGraph()
});
Edited to change the data join to be be appropriate.
I think the issue has to do with variable data. So, I added data as an argument to the function and specified separate names for different data sets. When I specify different data names, I'm able to update the chart:
var first_data = [{
id: 'p1',
points: [{
point: {
x: 100,
y: 10
}
}, {
point: {
x: 100,
y: 30
}
}]
},
{
id: 'p2',
points: [{
point: {
x: 30,
y: 100
}
}, {
point: {
x: 230,
y: 30
}
},
{
point: {
x: 50,
y: 200
}
},
{
point: {
x: 50,
y: 300
}
},
]
}
];
var svg = d3.select("svg");
var line = d3.line()
.x((d) => d.point.x)
.y((d) => d.point.y);
function updateGraph(data) {
console.log('dataset contains', data.length, 'item(s)')
var allGroup = svg.selectAll(".pathGroup")
.data(data, function(d) { return d; });
var g = allGroup.enter()
.append("g")
.attr("class", "pathGroup")
allGroup.exit().remove()
g.append("path")
.attr("class", "line")
.attr("stroke", "red")
.attr("stroke-width", "1px")
.transition()
.duration(500)
.attr("d", function(d) {
console.log("update path");
return line(d.points)
});
g.selectAll("path")
.transition()
.duration(500)
.attr("d", function(d) {
return line(d.points)
});
g.selectAll("circle")
.data(d => d.points)
.enter()
.append("circle")
.attr("r", 4)
.attr("fill", "teal")
.attr("cx", function(d) {
return d.point.x
})
.attr("cy", function(d) {
return d.point.y
})
.exit().remove()
}
updateGraph(first_data)
document.getElementById('update').onclick = function(e) {
var update_data = [{
id: 'p1',
points: [{
point: {
x: 20,
y: 10
}
}, {
point: {
x: 100,
y: 30
}
}]
},
{
id: 'p2',
points: [{
point: {
x: 30,
y: 100
}
}, {
point: {
x: 230,
y: 30
}
},
{
point: {
x: 50,
y: 300
}
},
]
}
];
updateGraph(update_data)
}
$('#cb1').click(function() {
if ($(this).is(':checked')) {
var click_data = [{
id: 'p1',
points: [{
point: {
x: 10,
y: 10
}
}, {
point: {
x: 100,
y: 30
}
}]
},
{
id: 'p2',
points: [{
point: {
x: 30,
y: 100
}
}, {
point: {
x: 230,
y: 30
}
},
{
point: {
x: 50,
y: 200
}
},
{
point: {
x: 50,
y: 300
}
},
]
}
];
} else {
var click_data = [{
id: 'p1',
points: [{
point: {
x: 10,
y: 10
}
}, {
point: {
x: 100,
y: 30
}
}]
}];
}
updateGraph(click_data)
});
Fiddle: https://jsfiddle.net/wj266kmx/32/

Changing the x-label in HTML Canvas charts

Hi I am using HTML Canvas charts. In one of the charts can I change the label of x-axis?
The source code for the chart is :
<!DOCTYPE HTML>
<html>
<head>
</head>
<body>
<div id="chartContainer" style="height: 300px; width: 100%;"></div>
<script type="text/javascript" src="https://canvasjs.com/assets/script/canvasjs.min.js"></script>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded",function () {
var chart = new CanvasJS.Chart("chartContainer",
{
animationEnabled: true,
theme: "theme2",
title:{
text: "Multi Series Spline Chart - Hide / Unhide via Legend"
},
axisY:[{
lineColor: "#4F81BC",
tickColor: "#4F81BC",
labelFontColor: "#4F81BC",
titleFontColor: "#4F81BC",
lineThickness: 2,
},
{
lineColor: "#C0504E",
tickColor: "#C0504E",
labelFontColor: "#C0504E",
titleFontColor: "#C0504E",
lineThickness: 2,
}],
data: [
{
type: "spline", //change type to bar, line, area, pie, etc
showInLegend: true,
dataPoints: [
{ x: 10, y: 51 },
{ x: 20, y: 45},
{ x: 30, y: 50 },
{ x: 40, y: 62 },
{ x: 50, y: 95 },
{ x: 60, y: 66 },
{ x: 70, y: 24 },
{ x: 80, y: 32 },
{ x: 90, y: 16}
]
},
{
type: "spline",
axisYIndex: 1,
showInLegend: true,
dataPoints: [
{ x: 10, y: 201 },
{ x: 20, y: 404},
{ x: 30, y: 305 },
{ x: 40, y: 405 },
{ x: 50, y: 905 },
{ x: 60, y: 508 },
{ x: 70, y: 108 },
{ x: 80, y: 300 },
{ x: 90, y: 101}
]
}
],
legend: {
cursor: "pointer",
itemclick: function (e) {
if (typeof(e.dataSeries.visible) === "undefined" || e.dataSeries.visible) {
e.dataSeries.visible = false;
} else {
e.dataSeries.visible = true;
}
chart.render();
}
}
});
chart.render();
});
</script>
</body>
</html>
Now here we can see that x-values is 10,20,30... My question is can we change them to other format like 10am or 10pm?
You can add a suffix (or a prefix) to the x-axis with chart.options.axisX
chart.options.axisX = { suffix: "AM" };
Or you can add both like this:
chart.options.axisX = { prefix: "/", suffix: "AM" };

Closing path for transition animation d3.js

I've made a line path for creating a shape of a bottle. I'm trying to figure out if I can close this path in order to transition it. Ideally I would like to have the bottle animate across the canvas, the same way a circle or rectangle would. Is this possible? I'm still learning d3.
var width = 900,
height = 800;
var canvas = d3.select('body')
.append('svg')
.attr('width', width)
.attr('height', height);
var dataArray = [
{x:51,y:44},
{x:51,y:49},
{x:53,y:50},
{x:53,y:53},
{x:52,y:53},
{x:52,y:60},
{x:70,y:85},
{x:71,y:160},
{x:64,y:181},
{x:54,y:181},
{x:47,y:170},
{x:43,y:170},
{x:36,y:181},
{x:26,y:181},
{x:19,y:160},
{x:19,y:85},
{x:39,y:60},
{x:39,y:53},
{x:38,y:53},
{x:38,y:50},
{x:40,y:49},
{x:40,y:44},
{x:51,y:44}
];
var interpolate = d3.curveCardinal.tension(0.35);
var line = d3.line()
.x(function(d,i){ return d.x/1.05 })
.y(function(d,i){ return d.y })
.curve(interpolate);
var group = canvas.append('g')
.attr('transform','translate(0,0)');
var bottle = group.selectAll('path')
.data([dataArray])
.enter()
.append('path')
.attr('fill','#ED5545')
.attr('stroke','#AA2731')
.attr('stroke-width','2')
.attr('id','bottleImage')
.attr('d',line);
bottle.transition()
.attr('x',300);
A SVG path has no x attribute. The simplest solution, therefore, is applying the transition to the group:
group.transition()
.delay(500)
.duration(2000)
.attr('transform', 'translate(300,0)');
Here is the demo:
var width = 400,
height = 300;
var canvas = d3.select('body')
.append('svg')
.attr('width', width)
.attr('height', height);
var dataArray = [{
x: 51,
y: 44
}, {
x: 51,
y: 49
}, {
x: 53,
y: 50
}, {
x: 53,
y: 53
}, {
x: 52,
y: 53
}, {
x: 52,
y: 60
}, {
x: 70,
y: 85
}, {
x: 71,
y: 160
}, {
x: 64,
y: 181
}, {
x: 54,
y: 181
}, {
x: 47,
y: 170
}, {
x: 43,
y: 170
}, {
x: 36,
y: 181
}, {
x: 26,
y: 181
}, {
x: 19,
y: 160
}, {
x: 19,
y: 85
}, {
x: 39,
y: 60
}, {
x: 39,
y: 53
}, {
x: 38,
y: 53
}, {
x: 38,
y: 50
}, {
x: 40,
y: 49
}, {
x: 40,
y: 44
}, {
x: 51,
y: 44
}];
var interpolate = d3.curveCardinal.tension(0.35);
var line = d3.line()
.x(function(d, i) {
return d.x / 1.05
})
.y(function(d, i) {
return d.y
})
.curve(interpolate);
var group = canvas.append('g')
.attr('transform', 'translate(0,0)');
var bottle = group.selectAll('path')
.data([dataArray])
.enter()
.append('path')
.attr('fill', '#ED5545')
.attr('stroke', '#AA2731')
.attr('stroke-width', '2')
.attr('id', 'bottleImage')
.attr('d', line);
group.transition()
.delay(500)
.duration(2000)
.attr('transform', 'translate(300,0)');
<script src="https://d3js.org/d3.v4.min.js"></script>

Categories

Resources