Robert Andersson
Robert Andersson

Reputation: 1521

How to set an initially determined zoom level based on data d3js

In my code below I've set an intial zoom level after the page have loaded like this:

svg.transition().delay(500).duration(750)
    .call(zooming.transform, transform)

function transform() {
    return d3.zoomIdentity
        .translate(-210, 0)
        .scale(2);
}

The problem is that the x value in the d3.zoomIdentity.translate(x, y) is hardcoded.

What I want to know is if there's a way to set it so that it always zoom in to, let's say, the last six bars based on the data that's provided? (within reason)

Is using d3.zoomIdentity the right way to go about it or is there some other method that would make it easier?

Here's my code:

const data = [
	{name: "A", value: 67},
	{name: "B", value: 92},
	{name: "C", value: 82},
	{name: "D", value: 53},
	{name: "E", value: 20},
	{name: "F", value: 88},
	{name: "G", value: 15},
	{name: "H", value: 94},
	{name: "I", value: 66},
	{name: "J", value: 53}
];

var svg = d3.select("#chart"),
	margin = {top: 35, left: 35, bottom: 35, right: 35},
	width = +svg.attr("width") - margin.left - margin.right,
	height = +svg.attr("height") - margin.top - margin.bottom;

var x = d3.scaleBand()
	.domain(data.map(d => d.name))
	.range([margin.left, width - margin.right])
	.padding(0.1)

var y = d3.scaleLinear()
	.domain([0, d3.max(data, d => d.value)]).nice()
	.range([height - margin.bottom, margin.top])

var xAxis = g => g
	.attr("transform", `translate(0,${height - margin.bottom})`)
	.call(d3.axisBottom(x).tickSizeOuter(0))

svg.append("g")
	.attr("class", "bars")
	.attr("fill", "steelblue")
.selectAll("rect").data(data).enter().append("rect")
	.attr("x", d => x(d.name))
	.attr("y", d => y(d.value))
	.attr("height", d => y(0) - y(d.value))
	.attr("width", x.bandwidth());

svg.append("g")
	.attr("class", "x-axis")
	.call(xAxis);

svg.call(zoom);

function zoom(svg) {

	const extent = [
		[margin.left, margin.top], 
		[width - margin.right, height - margin.top]];

	var zooming = d3.zoom()
		.scaleExtent([1, 3])
		.translateExtent(extent)
		.extent(extent)
		.on("zoom", zoomed)
	
	svg.call(zooming);

	svg.transition().delay(500).duration(750)
		.call(zooming.transform, transform)

	function transform() {
		return d3.zoomIdentity
			.translate(-210, 0)
			.scale(2);
	}
	
	function zoomed() {

		x.range([
			margin.left, width - margin.right]
			.map(d => d3.event.transform.applyX(d)));

		svg.selectAll(".bars rect")
			.attr("x", d => x(d.name))
			.attr("width", x.bandwidth());

		svg.selectAll(".x-axis").call(xAxis);
	}
}
body {
	margin: auto;
	width: 850px;
}
<meta charset ="utf-8">
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg id="chart" width="400" height="420"></svg>

Upvotes: 2

Views: 145

Answers (1)

Gerardo Furtado
Gerardo Furtado

Reputation: 102218

If I understand your question correctly, you can get the last six bars by simply getting the x position of the sixth last:

-x(data[data.length - 6].name)

Since you're scaling by two, we have to multiply it accordingly:

-x(data[data.length - 6].name) * 2

Here is the code with that change:

const data = [
	{name: "A", value: 67},
	{name: "B", value: 92},
	{name: "C", value: 82},
	{name: "D", value: 53},
	{name: "E", value: 20},
	{name: "F", value: 88},
	{name: "G", value: 15},
	{name: "H", value: 94},
	{name: "I", value: 66},
	{name: "J", value: 53}
];

var svg = d3.select("#chart"),
	margin = {top: 35, left: 35, bottom: 35, right: 35},
	width = +svg.attr("width") - margin.left - margin.right,
	height = +svg.attr("height") - margin.top - margin.bottom;

var x = d3.scaleBand()
	.domain(data.map(d => d.name))
	.range([margin.left, width - margin.right])
	.padding(0.1)

var y = d3.scaleLinear()
	.domain([0, d3.max(data, d => d.value)]).nice()
	.range([height - margin.bottom, margin.top])

var xAxis = g => g
	.attr("transform", `translate(0,${height - margin.bottom})`)
	.call(d3.axisBottom(x).tickSizeOuter(0))

svg.append("g")
	.attr("class", "bars")
	.attr("fill", "steelblue")
.selectAll("rect").data(data).enter().append("rect")
	.attr("x", d => x(d.name))
	.attr("y", d => y(d.value))
	.attr("height", d => y(0) - y(d.value))
	.attr("width", x.bandwidth());

svg.append("g")
	.attr("class", "x-axis")
	.call(xAxis);

svg.call(zoom);

function zoom(svg) {

	const extent = [
		[margin.left, margin.top], 
		[width - margin.right, height - margin.top]];

	var zooming = d3.zoom()
		.scaleExtent([1, 3])
		.translateExtent(extent)
		.extent(extent)
		.on("zoom", zoomed)
	
	svg.call(zooming);

	svg.transition().delay(500).duration(750)
		.call(zooming.transform, transform);
    
 var xValue = -x(data[data.length - 6].name)*2;

	function transform() {
		return d3.zoomIdentity
			.translate(xValue, 0)
			.scale(2);
	}
	
	function zoomed() {

		x.range([
			margin.left, width - margin.right]
			.map(d => d3.event.transform.applyX(d)));

		svg.selectAll(".bars rect")
			.attr("x", d => x(d.name))
			.attr("width", x.bandwidth());

		svg.selectAll(".x-axis").call(xAxis);
	}
}
body {
	margin: auto;
	width: 850px;
}
<meta charset ="utf-8">
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg id="chart" width="400" height="250"></svg>

Upvotes: 4

Related Questions