Skip to content

Instantly share code, notes, and snippets.

@ngminhtrung
Last active April 27, 2018 04:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ngminhtrung/6c354a3d5075f41fa7db51b896d1595a to your computer and use it in GitHub Desktop.
Save ngminhtrung/6c354a3d5075f41fa7db51b896d1595a to your computer and use it in GitHub Desktop.
Illustration of zooming in D3 v4
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
.red {
color: red;
}
#txty {
margin-left: 50px;
}
.section {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
padding-bottom: 20px;
border-bottom: 1px solid antiquewhite;
}
p {
text-align: center;
width: 100%;
color: blue;
}
body {
margin: 0 auto;
}
</style>
<title>Document</title>
</head>
<body>
<div class="section section1">
<p>
<strong>Step 1:</strong> Draw 4 points without rescaling</p>
<svg class="figure1"></svg>
</div>
<div class="section section2">
<p><strong>Step 2:</strong> Rescaling to separate overlapped points 1010 & 1020</p>
<svg class="figure2"></svg>
</div>
<div class="section section3">
<p><strong>Step 3:</strong> Rescaling x top axis</p>
<svg class="figure3"></svg>
</div>
<div class="section section4">
<p><strong>Step 4:</strong> Adding zoom behavior. Touch to see it.</p>
<p id="txty">
Current {tx, ty}: {
<span class="red" id="tx"></span>,
<span class="red" id="ty"></span>}</p>
<svg class="figure4"></svg>
</div>
<div class="section section5">
<p><strong>Step 5:</strong> Selecting random point for zooming</p>
<svg class="figure5"></svg>
</div>
<script src="script.js"></script>
</body>
</html>
figure01();
figure02();
figure03();
figure04();
figure05();
function figure05() {
const margin = { 'left': -50, 'top': 80, 'bottom': 20, 'right': 20 };
const width = 500, height = 120;
const svg = d3.selectAll(".figure5")
.attr("height", height + margin.top + margin.bottom)
.attr("width", width + margin.left + margin.right);
const gMain = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + 100 + ")");
gMain.append("rect")
.attr("x", 50)
.attr("y", -25)
.attr("width", width)
.attr("height", height)
.style("fill", "transparent");
const xScale = d3.scaleLinear()
.domain([0, 5000])
.range([100, 500])
const dataPoints = [1, 1010, 1020, 5000];
let targetPoint = 1015;
const k = 10 / (xScale(1020) - xScale(1010));
const tx = 200 - k * xScale(1010);
const t = d3.zoomIdentity.translate(tx, 0).scale(k);
// d3.zoomIdentity là 1 transform có k = 1, tx = ty = 0;
// t = d3.zoomIdentity.translate(tx, 0).scale(k);
// t trở thành 1 transform, có k = k, tx=tx, ty = 0;
const xNewScale = t.rescaleX(xScale);
const circles = gMain.selectAll("circle")
.data(dataPoints)
.enter()
.append("circle")
.attr("r", 10)
.attr("fill", "pink")
.attr("stroke", "red")
.attr("stroke-width", "0.5px")
gMain.append("text")
.attr("x", 300)
.attr("y", -60)
.attr("text-anchor", "middle")
.text("point value");
gMain.append("text")
.attr("x", 300)
.attr("y", 55)
.attr("text-anchor", "middle")
.text("screen position");
const xTopAxis = d3.axisTop()
.scale(xNewScale)
.ticks(3)
const gTopAxis = gMain.append("g")
.classed("x axis", true)
.attr("transform", "translate(0, -15)");
const xBottomAxis = d3.axisBottom()
.scale(d3.scaleLinear().domain([100, 500]).range([100, 500]))
.ticks(3);
const gBottomAxis = gMain.append("g")
.classed("x axis bottom", true)
.attr("transform", "translate(0, 15)");
const zoom = d3.zoom().on("zoom", zoomed);
gMain.call(zoom.transform, t);
function zoomed() {
const transform = d3.event.transform;
const xNewScale = transform.rescaleX(xScale);
const {x, y} = transform;
document.getElementById("tx").textContent = x.toFixed(1);
document.getElementById("ty").textContent = y.toFixed(1);
xTopAxis.scale(xNewScale);
gTopAxis.call(xTopAxis);
circles.attr("cx", d => xNewScale(d));
}
gMain.call(zoom);
gBottomAxis.call(xBottomAxis);
gTopAxis.call(xTopAxis);
function transform() {
// put points that are 10 values apart 20 pixels apart
var k = 20 / (xScale(10) - xScale(0))
// center in the middle of the visible area
var tx = (xScale.range()[1] + xScale.range()[0]) / 2 - k * xScale(targetPoint)
var t = d3.zoomIdentity.translate(tx, 0).scale(k)
return t;
}
function transition(selection) {
let n = dataPoints.length;
let prevTargetPoint = targetPoint;
// pick a new point to zoom to
while (targetPoint == prevTargetPoint) {
let i = Math.random() * n | 0
targetPoint = dataPoints[i];
}
selection.transition()
.delay(300)
.duration(2000)
.call(zoom.transform, transform)
.on('end', function () { circles.call(transition); });
}
circles.call(transition);
}
function figure04() {
const margin = { 'left': -50, 'top': 20, 'bottom': 20, 'right': 20 };
const width = 500, height = 100;
const svg = d3.selectAll(".figure4")
.attr("height", height + margin.top + margin.bottom)
.attr("width", width + margin.left + margin.right);
const gMain = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + 80 + ")");
gMain.append("rect")
.attr("x", 50)
.attr("y", -25)
.attr("width", width)
.attr("height", height)
.style("fill", "transparent");
const xScale = d3.scaleLinear()
.domain([0, 5000])
.range([100, 500])
const dataPoints = [1, 1010, 1020, 5000];
const k = 10 / (xScale(1020) - xScale(1010));
const tx = 200 - k * xScale(1010);
const t = d3.zoomIdentity.translate(tx, 0).scale(k);
// d3.zoomIdentity là 1 transform có k = 1, tx = ty = 0;
// t = d3.zoomIdentity.translate(tx, 0).scale(k);
// t trở thành 1 transform, có k = k, tx=tx, ty = 0;
const xNewScale = t.rescaleX(xScale);
const circles = gMain.selectAll("circle")
.data(dataPoints)
.enter()
.append("circle")
.attr("r", 10)
.attr("fill", "pink")
.attr("stroke", "red")
.attr("stroke-width", "0.5px")
gMain.append("text")
.attr("x", 300)
.attr("y", -60)
.attr("text-anchor", "middle")
.text("point value");
gMain.append("text")
.attr("x", 300)
.attr("y", 55)
.attr("text-anchor", "middle")
.text("screen position");
const xTopAxis = d3.axisTop()
.scale(xNewScale)
.ticks(3)
const gTopAxis = gMain.append("g")
.classed("x axis", true)
.attr("transform", "translate(0, -15)");
const xBottomAxis = d3.axisBottom()
.scale(d3.scaleLinear().domain([100, 500]).range([100, 500]))
.ticks(3);
const gBottomAxis = gMain.append("g")
.classed("x axis bottom", true)
.attr("transform", "translate(0, 15)");
const zoom = d3.zoom().on("zoom", zoomed);
gMain.call(zoom.transform, t);
function zoomed() {
const transform = d3.event.transform;
const xNewScale = transform.rescaleX(xScale);
const {x, y} = transform;
document.getElementById("tx").textContent = x.toFixed(1);
document.getElementById("ty").textContent = y.toFixed(1);
xTopAxis.scale(xNewScale);
gTopAxis.call(xTopAxis);
circles.attr("cx", d => xNewScale(d));
}
gMain.call(zoom);
gBottomAxis.call(xBottomAxis);
gTopAxis.call(xTopAxis);
}
function figure03() {
const margin = { 'left': -50, 'top': 40, 'bottom': 20, 'right': 20 };
const width = 500, height = 120;
const svg = d3.selectAll(".figure3")
.attr("height", height + margin.top + margin.bottom)
.attr("width", width + margin.left + margin.right);
const gMain = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + 100 + ")");
const xScale = d3.scaleLinear()
.domain([0, 5000])
.range([100, 500])
const dataPoints = [1, 1010, 1020, 5000];
const k = 10 / (xScale(1020) - xScale(1010));
const tx = 200 - k * xScale(1010);
const t = d3.zoomIdentity.translate(tx, 0).scale(k);
const xNewScale = t.rescaleX(xScale);
const circles =
gMain.selectAll("circle")
.data(dataPoints)
.enter()
.append("circle")
.attr("r", 10)
.attr("fill", "pink")
.attr("stroke", "red")
.attr("stroke-width", "0.5px")
.attr("cx", d => t.applyX(xScale(d)));
gMain.append("text")
.attr("x", 300)
.attr("y", -60)
.attr("text-anchor", "middle")
.text("point value");
gMain.append("text")
.attr("x", 300)
.attr("y", 55)
.attr("text-anchor", "middle")
.text("screen position");
const xTopAxis = d3.axisTop()
.scale(xNewScale)
.ticks(3)
const gTopAxis = gMain.append("g")
.classed("x axis", true)
.attr("transform", "translate(0, -15)");
const xBottomAxis = d3.axisBottom()
.scale(d3.scaleLinear().domain([100, 500]).range([100, 500]))
.ticks(3);
const gBottomAxis = gMain.append("g")
.classed("x axis bottom", true)
.attr("transform", "translate(0, 15)");
gBottomAxis.call(xBottomAxis);
gTopAxis.call(xTopAxis);
}
function figure02() {
const margin = { 'left': -50, 'top': 20, 'bottom': 20, 'right': 20 };
const width = 500, height = 120;;
const svg = d3.selectAll(".figure2")
.attr("height", height + margin.top + margin.bottom)
.attr("width", width + margin.left + margin.right);
const gMain = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + 100 + ")");
const xScale = d3.scaleLinear()
.domain([0, 5000])
.range([100, 500])
const dataPoints = [1, 1010, 1020, 5000];
const k = 10 / (xScale(1020) - xScale(1010));
const tx = 200 - k * xScale(1010);
const t = d3.zoomIdentity.translate(tx, 0).scale(k);
gMain.selectAll("circle")
.data(dataPoints)
.enter()
.append("circle")
.attr("r", 10)
.attr("fill", "pink")
.attr("stroke", "red")
.attr("stroke-width", "0.5px")
.attr("cx", d => t.applyX(xScale(d)));
gMain.append("text")
.attr("x", 300)
.attr("y", -60)
.attr("text-anchor", "middle")
.text("point value");
gMain.append("text")
.attr("x", 300)
.attr("y", 55)
.attr("text-anchor", "middle")
.text("screen position");
const xTopAxis = d3.axisTop()
.scale(xScale)
.ticks(3)
const gTopAxis = gMain.append("g")
.classed("x axis", true)
.attr("transform", "translate(0, -15)");
const xBottomAxis = d3.axisBottom()
.scale(d3.scaleLinear().domain([100, 500]).range([100, 500]))
.ticks(3);
const gBottomAxis = gMain.append("g")
.classed("x axis bottom", true)
.attr("transform", "translate(0, 15)");
gBottomAxis.call(xBottomAxis);
gTopAxis.call(xTopAxis);
}
function figure01() {
const margin = { 'left': -50, 'top': 20, 'bottom': 20, 'right': 20 };
const width = 500, height = 120;
const svg = d3.selectAll(".figure1")
.attr("height", height + margin.top + margin.bottom)
.attr("width", width + margin.left + margin.right);
const gMain = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + 100 + ")");
const xScale = d3.scaleLinear()
.domain([0, 5000])
.range([100, 500])
const dataPoints = [1, 1010, 1020, 5000];
gMain.selectAll("circle")
.data(dataPoints)
.enter()
.append("circle")
.attr("r", 10)
.attr("fill", "pink")
.attr("stroke", "red")
.attr("stroke-width", "0.5px")
.attr("cx", d => xScale(d));
gMain.append("text")
.attr("x", 300)
.attr("y", -60)
.attr("text-anchor", "middle")
.text("point value");
gMain.append("text")
.attr("x", 300)
.attr("y", 55)
.attr("text-anchor", "middle")
.text("screen position");
const xTopAxis = d3.axisTop()
.scale(xScale)
.ticks(3)
const gTopAxis = gMain.append("g")
.classed("x axis", true)
.attr("transform", "translate(0, -15)");
const xBottomAxis = d3.axisBottom()
.scale(d3.scaleLinear().domain([100, 500]).range([100, 500]))
.ticks(3);
const gBottomAxis = gMain.append("g")
.classed("x axis bottom", true)
.attr("transform", "translate(0, 15)");
gBottomAxis.call(xBottomAxis);
gTopAxis.call(xTopAxis);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment