Public
Edited
Apr 25
1 fork
4 stars
Insert cell
Insert cell
scale = d3.scaleLinear().domain([20, 80]).range([0, 120])
Insert cell
viewof controls = Inputs.form({
v: Inputs.range(scale.domain(), {label: "v", step: 1}),
ratio: Inputs.range([0, 1], {label: "ratio", step: 0.01})
})
Insert cell
scaleViz(scale, controls.v, controls.ratio)
Insert cell
function scaleViz(scale, v, domainBandRatio = .8) {
const width = 400;
const height = 180;
const domain = scale.domain();
const range = scale.range();

const svg = d3.create("svg")
.attr("width", width)
.attr("height", height);

const margin = 40;
const lineY1 = 50;
const lineY2 = 130;

const xDomain = d3.scaleLinear().domain(domain).range([margin, width - margin]);
const xRange = d3.scaleLinear().domain(range).range([margin, width - margin]);

let domainValue = (domain[0] + domain[1]) / 2;
let rangeValue = scale(domainValue);

const rangeX0 = xRange(range[0]);
const rangeX1 = xRange(range[1]);
const rangeWidth = rangeX1 - rangeX0;
const domainWidth = rangeWidth * domainBandRatio;

const domainCenter = (xDomain(domain[0]) + xDomain(domain[1])) / 2;
const domainX0 = domainCenter - domainWidth / 2;
const domainX1 = domainCenter + domainWidth / 2;

svg.append("path")
.attr("d", `
M${domainX0},${lineY1 + 10}
L${domainX1},${lineY1 + 10}
L${rangeX1},${lineY2}
L${rangeX0},${lineY2}
Z`)
.attr("fill", "peachpuff")
.attr("stroke", "orange")
.attr("stroke-width", 1);

svg.append("rect")
.attr("x", domainX0)
.attr("y", lineY1 - 20)
.attr("width", domainWidth)
.attr("height", 30)
.attr("fill", "lightblue")
.attr("stroke", "#2563eb");

svg.append("rect")
.attr("x", rangeX0)
.attr("y", lineY2 - 15)
.attr("width", rangeWidth)
.attr("height", 30)
.attr("fill", "lightblue")
.attr("stroke", "#2563eb");

svg.append("text").text("Domain")
.attr("x", domainCenter)
.attr("y", lineY1)
.attr("dominant-baseline", "middle")
.attr("text-anchor", "middle")
.style("fill", "#ffffff")
.style("font-weight", "bold");

svg.append("text").text("Range")
.attr("x", (rangeX0 + rangeX1) / 2)
.attr("y", lineY2)
.attr("dominant-baseline", "middle")
.attr("text-anchor", "middle")
.style("fill", "#ffffff")
.style("font-weight", "bold");

const domainLabel = svg.append("text")
.attr("y", lineY1 - 10)
.attr("text-anchor", "middle")
.style("font-size", "12px");

const rangeLabel = svg.append("text")
.attr("y", lineY2 + 15)
.attr("text-anchor", "middle")
.style("font-size", "12px");

const connector = svg.append("line")
.attr("stroke", "black")
.attr("stroke-width", 1)
.attr("marker-end", "url(#arrow)");

const arrowLabel = svg.append("text")
.attr("text-anchor", "middle")
.attr("font-size", "10px")
.style("fill", "black");

svg.append("defs").append("marker")
.attr("id", "arrow")
.attr("viewBox", "0 0 10 10")
.attr("refX", 25) // 5
.attr("refY", 5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto-start-reverse")
.append("path")
.attr("d", "M 0 0 L 10 5 L 0 10 z")
.attr("fill", "black");

svg.append("text")
.text(domain[0])
.attr("x", xDomain(domain[0]))
.attr("y", lineY1 - 25)
.attr("text-anchor", "start");

svg.append("text")
.text(domain[1])
.attr("x", xDomain(domain[1]))
.attr("y", lineY1 - 25)
.attr("text-anchor", "end");

svg.append("text")
.text(range[0])
.attr("x", rangeX0)
.attr("y", lineY2 + 30)
.attr("text-anchor", "start");

svg.append("text")
.text(range[1])
.attr("x", rangeX1)
.attr("y", lineY2 + 30)
.attr("text-anchor", "end");

function update(v) {
const mapped = scale(v);
const t = (v - domain[0]) / (domain[1] - domain[0]);
const x1 = domainX0 + t * domainWidth;

const tMapped = (mapped - range[0]) / (range[1] - range[0]);
const x2 = rangeX0 + tMapped * rangeWidth;

connector
.attr("x1", x1)
.attr("y1", lineY1 + 10)
.attr("x2", x2)
.attr("y2", lineY2 - 5);

domainLabel.attr("x", x1).text(v);
rangeLabel.attr("x", x2).text(mapped.toFixed(0));

arrowLabel
.attr("x", (x1 + x2) / 2)
.attr("y", (lineY1 + lineY2) / 2)
.text(`${v} → ${mapped.toFixed(0)}`);
}

update(v);

return svg.node();
}

Insert cell
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