chart = {
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);
const radius = 3.5;
let overviewMarker = {
x: 0,
y: 0,
w: width,
h: height
};
let transform = d3.zoomIdentity;
let zoom = d3
.zoom()
.extent([[0, 0], [width, height]])
.translateExtent([[0, 0], [width, height]])
.scaleExtent([1, 8])
.on("zoom", zoomed);
const detailview = svg.append("g");
detailview
.append("rect")
.attr("transform", transform)
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height)
.attr("fill", "#ECEFF1");
detailview
.selectAll("circle")
.data(data)
.join("circle")
.attr("fill", "#263238")
.attr("cx", ([x]) => x)
.attr("cy", ([, y]) => y)
.attr("r", radius)
.attr("fill", (d, i) => d3.interpolateCool(i / data.length))
.attr("stroke-width", 1)
.attr("stroke", "#263238");
//put the object at the right position and make it smaller
const overviewTransform = d3.zoomIdentity.scale(0.3).translate(width/0.3-1000,50);
const overview = svg
.append("g")
.attr("transform", overviewTransform)
.attr("viewBox", [0, 0, width, height]);
overview
.append("rect")
.attr("x", 0)
.attr("y", 0)
//set width, heigth, Fill, StrokeWidth, Stroke Color
.attr("width", width)
.attr("height", height)
.attr("fill", "#ECEFF1")
.attr("stroke-width", 1)
.attr("stroke", "black")
overview
.selectAll("circle")
//append the data points in the overview
.data(data)
.join("circle")
.attr("fill", "#263238")
.attr("cx", ([x]) => x)
.attr("cy", ([, y]) => y)
.attr("r", radius)
.attr("fill", (d, i) => d3.interpolateCool(i / data.length))
.attr("stroke-width", 1)
.attr("stroke", "#263238");
function zoomed() {
transform = d3.event.transform;
}
const markerRect = overview
.append("rect")
.datum(overviewMarker)
.attr("x", 0)
.attr("y", 0)
//set x, y, width, height and opacity, stroke width, stroke color
.attr("width", width)
.attr("height", height)
.attr("fill-opacity", 0.2)
.attr("stroke-width", 1)
.attr("stroke", "red");
function updateMarkerRect() {
overviewMarker.x = transform.invertX(0);
overviewMarker.y = transform.invertY(0);
overviewMarker.w = transform.invertX(width) - overviewMarker.x;
overviewMarker.h = transform.invertY(height) - overviewMarker.y;
markerRect
.attr("x", d => d.x)
.attr("y", d => d.y)
.attr("width", d => d.w)
.attr("height", d => d.h);
}
function updateMarkerDrag(d) {
//check if marker is moved outside the black rectangle
if (d3.event.x < 0) {
d3.event.x = 0;
}
//the width of the rectangle depends on the zoom level
//width of the red rectangle = width/zoomSlider.value
if (d3.event.x > width-width/zoomSlider.value) {
d3.event.x = width-width/zoomSlider.value;
}
if (d3.event.y < 0) {
d3.event.y = 0;
}
if (d3.event.y > height-height/zoomSlider.value) {
d3.event.y = height-height/zoomSlider.value;
}
//event is the moving of the marker
d3.select(this)
.attr("x", d3.event.x)
.attr("y", d3.event.y);
//scale to zoom level and translate according to movement
let t = d3.zoomIdentity.scale(zoomSlider.value).translate(-d3.event.x, -d3.event.y);
detailview.call(zoom.transform, t);
detailview.attr("transform", transform);
}
//graphical feedback when square is selected
function startMarkerDrag(d) {
markerRect.attr("stroke-width", 4)
.attr("fill-opacity", 0.1)
}
function endMarkerDrag(d) {
markerRect.attr("stroke-width", 1)
.attr("fill-opacity", 0.2)
}
let markerDrag = d3
.drag()
.on("start", startMarkerDrag)
.on("drag", updateMarkerDrag)
.on("end", endMarkerDrag);
markerRect.call(markerDrag);
//set the slider to the beginning when pressing play
zoomSlider.value=1;
//set the slider step, min and max parameters
zoomSlider.min=1;
zoomSlider.max=8;
zoomSlider.step=0.3;
function sliderZoom() {
//move to center, zoom, move back
let t = d3.zoomIdentity.translate(width/2,height/2).scale(zoomSlider.value).translate(-width/2,-height/2);
detailview.call(zoom.transform, t);
detailview.attr("transform", transform);
updateMarkerRect();
}
d3.select(zoomSlider)
.on("input", sliderZoom);
return svg.node();
}