Published
Edited
Feb 12, 2021
1 fork
Insert cell
md`# D3 Autoscaling Axis
Port of http://bl.ocks.org/WilliamQLiu/59c87d2bcc00800ec3f9 to D3 v6`
Insert cell
chart = {
var w = width, //window.innerWidth,
h = height, // window.innerHeight,
//margin = { top: 0, right: 0, bottom: 0, left: 0 },
margin = { top: 40, right: 20, bottom: 20, left: 40 },
radius = 6;
/*var svg = d3.select("body").append("svg").attr({
width: w,
height: h
});*/

const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);

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.axisTop(xScale);
var yAxis = d3.axisLeft(yScale);
//var xAxis = d3.svg.axis().scale(xScale).orient("top");
//var yAxis = d3.svg.axis().scale(yScale).orient("left");

// New circles will start at 0,0
var circleInitialAttrs = {
cx: xScale(0),
cy: yScale(0),
r: 1
};

// 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
*/
// Adds X-Axis as a 'g' element
var xAxisGroup = svg.append("g")
.attr("class", "axis") // Give class so we can style it
.attr("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") // Give class so we can style it
.attr("transform", "translate(" + [margin.left, 0] + ")") // Translate just moves it down into position (or will be on top)
.call(yAxis); // Call the yAxis function on the group

var circles = svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
//.attr(circleInitialAttrs) // Get attributes from circleInitialAttrs var
.attr("cx", xScale(0))
.attr("cy", yScale(0))
.attr("r", 1)
.on("mouseover", handleMouseOver)
.on("mouseout", handleMouseOut);

circles.transition() // Gives the fly out from the center effect
.delay(function (d, i){
return i * 100; // Gives a slight delay with 100 ms spacing
})
.duration(1000)
.ease(d3.easeElastic.period(0.4))
.attr("cx", (d) => xScale(d.x))
.attr("cy", (d) => yScale(d.y))
.attr("r", radius);

// On Click, we want to add data to the array and chart
svg.on("click", function(event) {
let coords = d3.pointer(event);
// 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
let c = svg.selectAll("circle"); // For new circle, go through the update process

// Updates existing circles to new positions by just adding attr
c.transition()
.ease(d3.easeElastic.period(0.4))
.attr("cx", (d) => xScale(d.x))
.attr("cy", (d) => yScale(d.y))
.attr("r", radius);
//.attr(circleAttrs);

/*c.enter()
.append("circle")
//.attr(circleInitialAttrs)
.attr("cx", xScale(0))
.attr("cy", yScale(0))
.attr("r", 1)
.on("mouseover", handleMouseOver)
.on("mouseout", handleMouseOut); */
c = svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", xScale(0))
.attr("cy", yScale(0))
.attr("r", 1)
.on("mouseover", handleMouseOver)
.on("mouseout", handleMouseOut);

c.transition()
.duration(1000) // Set how long it takes
.ease(d3.easeElastic.period(0.4))
.attr("cx", (d) => xScale(d.x))
.attr("cy", (d) => yScale(d.y))
.attr("r", radius);
})

// Create Event Handlers for mouse
function handleMouseOver(event, d, i, index) { // Add interactivity
// Use D3 to select element, change color and size
d3.select(this)
.attr("fill", "orange")
.attr("r", radius * 2);

// Specify where to put label of text
svg.append("text")
// Create an id for text so we can select it later for removing on mouseout
.attr("id", function() { return "t" + d.x + "-" + d.y + "-" + index})
.attr("x", function() { return xScale(d.x) - 30; })
.attr("y", function() { return yScale(d.y) - 15; })
.text(function() {
return `${d.x}, ${d.y}`; // Value of the text
});
}

function handleMouseOut(event, d, i, index) {
// Use D3 to select element, change color back to normal
d3.select(this)
.attr("fill", "black")
.attr("r", radius);

// Select text by id and then remove
d3.select("#t" + d.x + "-" + d.y + "-" + index).remove(); // Remove text location
}
return svg.node();
}
Insert cell
d3 = require('d3@6')
Insert cell
width = 800
Insert cell
height = 600
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more