Skip to content

Instantly share code, notes, and snippets.

@kalpanavaidya
Last active July 12, 2017 20:01
Show Gist options
  • Save kalpanavaidya/dbf4fad564b4b4b317ac02d716db0f9a to your computer and use it in GitHub Desktop.
Save kalpanavaidya/dbf4fad564b4b4b317ac02d716db0f9a to your computer and use it in GitHub Desktop.
addElements
license: mit
<!DOCTYPE html>
<!-- Allows creating new values outside the normal scale
Then updates axis, scales, and circle attributes -->
<html>
<head>
<!-- Load D3 from site -->
<!-- CSS (Styling) -->
<style type="text/css">
body {
margin: 0;
font-family: sans-serif;
font-size: 11px;
}
.axis path, .axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges; /* Round any decimal pixels so it'll render nicely */
}
/*
//Can use CSS3 Transitions, but not for everything (e.g. change radius size)
circle:hover{
fill: green;
}
*/
</style>
</head>
<body>
<!-- Begin Javascript -->
<script src="http://d3js.org/d3.v4.min.js"></script>
<script type="text/javascript">
var w = window.innerWidth,
h = window.innerHeight,
margin = { top: 40, right: 20, bottom: 20, left: 40 },
radius = 6;
var svg = d3.select("body").append("svg").attr({
width: w,
height: h
});
var dataset = [
{ x: 100, y: 110 },
{ x: 83, y: 43 },
{ x: 92, y: 28 },
{ x: 49, y: 74 },
{ x: 51, y: 10 },
{ x: 25, y: 98 },
{ x: 77, y: 30 },
{ x: 20, y: 83 },
{ x: 11, y: 63 },
{ x: 4, y: 55 },
{ x: 0, y: 0 },
{ x: 85, y: 100 },
{ x: 60, y: 40 },
{ x: 70, y: 80 },
{ x: 10, y: 20 },
{ x: 40, y: 50 },
{ x: 25, y: 31 }
];
// We're passing in a function in d3.max to tell it what we're maxing (x value)
var xScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function (d) { return d.x + 10; })])
.range([margin.left, w - margin.right]); // Set margins for x specific
// We're passing in a function in d3.max to tell it what we're maxing (y value)
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function (d) { return d.y + 10; })])
.range([margin.top, h - margin.bottom]); // Set margins for y specific
// Add a X and Y Axis (Note: orient means the direction that ticks go, not position)
var xAxis = d3.axisBottom(xScale).tickFormat(function (d) { return d.x; });
var yAxis = d3.axisLeft(yScale);
// Sets circles attributes
var circleAttrs = {
cx: function(d) { return xScale(d.x); },
cy: function(d) { return yScale(d.y); },
r: radius
};
// Adds X-Axis as a 'g' element
var xAxisGroup = svg.append("g").attr({
"class": "axis", // Give class so we can style it
transform: "translate(" + [0, margin.top] + ")" // Translate just moves it down into position (or will be on top)
}).call(xAxis); // Call the xAxis function on the group
// Adds Y-Axis as a 'g' element
var yAxisGroup = svg.append("g").attr({
"class": "axis",
transform: "translate(" + [margin.left, 0] + ")"
}).call(yAxis); // Call the yAxis function on the group
var drag = d3.drag()
.on('dragstart', function(d, i) {
d3.event.sourceEvent.stopPropagation();
d3.select("#t" + d.x + "-" + d.y + "-" + i).remove();
})
.on('drag', dragged)
.on('dragend', dragended);
var circles = svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr(circleAttrs) // Get attributes from circleInitialAttrs var
.on("mouseover", handleMouseOver)
.on("mouseout", handleMouseOut)
.call(drag);
// On Click, we want to add data to the array and chart
svg.on("click", function() {
if(d3.event.defaultPrevented) return;
var coords = d3.mouse(this);
// Normally we go from data to pixels, but here we're doing pixels to data
var newData= {
x: Math.round( xScale.invert(coords[0])), // Takes the pixel number to convert to number
y: Math.round( yScale.invert(coords[1]))
};
dataset.push(newData); // Push data to our array
xScale.domain([0, d3.max(dataset, function (d) { return d.x + 10; })])
yScale.domain([0, d3.max(dataset, function (d) { return d.y + 10; })])
// Update Axis (e.g. might increase if new higher value added)
xAxisGroup.transition().call(xAxis); // Update X-Axis
yAxisGroup.transition().call(yAxis); // Update Y-Axis
// When adding new items, goes from 0,0 and transition to place
var c = svg.selectAll("circle") // For new circle, go through the update process
.data(dataset);
// Updates existing circles to new positions by just adding attr
c.transition()
.ease("elastic")
.attr(circleAttrs)
c.enter()
.append("circle")
.attr(circleAttrs)
.on("mouseover", handleMouseOver)
.on("mouseout", handleMouseOut)
.call(drag);
})
// Create Event Handlers for mouse
function handleMouseOver(d, i) { // Add interactivity
// Use D3 to select element, change color and size
d3.select(this).attr({
fill: "orange",
r: radius * 2
});
// Specify where to put label of text
svg.append("text").attr({
id: "t" + d.x + "-" + d.y + "-" + i, // Create an id for text so we can select it later for removing on mouseout
x: function() { return xScale(d.x) - 30; },
y: function() { return yScale(d.y) - 15; }
})
.text(function() {
return [d.x, d.y]; // Value of the text
});
}
function handleMouseOut(d, i) {
// Use D3 to select element, change color back to normal
d3.select(this).attr({
fill: "black",
r: radius
});
// Select text by id and then remove
d3.select("#t" + d.x + "-" + d.y + "-" + i).remove(); // Remove text location
}
function dragged(d,i) {
if(d3.event.x >= margin.left && d3.event.y >= margin.top
&& d3.event.x <= w
&& d3.event.y <= h) {
d.x = d3.event.x;
d.y = d3.event.y;
console.log(d3.event.x + " " + d3.event.y);
d3.select(this)
.attr("cx", d.x )
.attr("cy", d.y );
}
}
function dragended(d, i) {
console.log("end");
var newX = Math.round(xScale.invert(d.x)),
newY = Math.round(yScale.invert(d.y));
dataset[i].x = newX;
dataset[i].y = newY;
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment