{
const xVal = [...new Set(data.map((d) => d.x))];
const countX = xVal.length;
const midIndex = Math.floor(countX / 2);
const median =
countX % 2 === 1
? xVal[midIndex]
: (xVal[midIndex] + xVal[midIndex - 1]) / 2;
const width = 1280;
const height = 600;
const main = d3.select("div #out");
const div = main
.html(null)
.append("div")
.attr("class", "tooltip")
.style("opacity", "0")
.style("position", "absolute")
.style("background", "yellow");
const svgns = "http://www.w3.org/2000/svg";
main
.append("svg")
.attr("xmlns", svgns)
.attr("viewBox", `0 0 ${width} ${height}`)
.attr("id", "svg");
const svg = d3.select("#out svg");
svg
.append("rect")
.attr("class", "vBoxRect")
.attr("width", `${width}`)
.attr("height", `${height}`)
.attr("fill", "none")
.attr("stroke", "red");
//------------------------2. CREATE BOUND------------------------//
const padding = {
top: 70,
bottom: 50,
left: 70,
right: 50
};
const boundHeight = height - padding.top - padding.bottom;
const boundWidth = width - padding.right - padding.left;
//create BOUND rect -- to be deleted later
svg
.append("rect")
.attr("class", "boundRect")
.attr("x", `${padding.left}`)
.attr("y", `${padding.top}`)
.attr("width", `${boundWidth}`)
.attr("height", `${boundHeight}`)
.attr("fill", "none")
.attr("stroke", "green");
//create bound element
const bound = svg
.append("g")
.attr("class", "bound")
.style("transform", `translate(${padding.left}px,${padding.top}px)`);
//------------------------3. CREATE SCALE------------------------//
const scaleX = d3
.scaleLinear()
.range([0, boundWidth])
.domain(d3.extent(data, (d) => d.x));
const scaleY = d3
.scaleLinear()
.range([boundHeight, 0])
.domain(d3.extent(data, (d) => d.y));
//------------------------4. CREATE AXIS------------------------//
//create Y Axis
bound
.append("g")
.attr("class", "yAxis")
.call(d3.axisLeft(scaleY))
.attr("class", "yAxis");
//create X Axis Bottom
bound
.append("g")
.attr("class", "xAxis")
.append("g")
.attr("class", "xAxisBottom")
.call(d3.axisBottom(scaleX))
.style("transform", `translateY(${boundHeight}px)`);
//------------------------5. CREATE PATH------------------------//
// Add the line
bound
.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.attr(
"d",
d3
.line()
.x((d) => scaleX(d.x))
.y((d) => scaleY(d.y))
);
//------------------------6. CREATE INTERACTION------------------------//
// Create the text that travels along the curve of chart
const focusText = bound
.append("g")
.attr("class", "travText")
.append("text")
.attr("opacity", 0);
// listening Rect
const listener = bound
.append("rect")
.style("fill", "none")
.attr("class", "listeningRect")
.style("pointer-events", "all")
.attr("width", boundWidth)
.attr("height", boundHeight);
//------------------------B. PROGRAM INTERACTION--------------//
listener
.on("mouseover", function () {
focusText.attr("opacity", 1);
div.style("opacity", "1");
})
.on("mouseout", function () {
focusText.attr("opacity", 0);
div.style("opacity", "0");
})
.on("mousemove", function (event) {
const mouseCoord = d3.pointer(event);
const mouseCoordX = mouseCoord[0];
const invertX = scaleX.invert(mouseCoordX);
const rnd = Math.round(invertX);
const src = data.filter((a) => a.x === rnd)[0];
focusText
.html("SVG Element-x:" + src.x + " - " + "y:" + src.y)
.attr("x", () => (src.x < median ? scaleX(src.x) : scaleX(src.x)))
.attr("y", scaleY(src.y));
div
.html("HTML Element-x:" + src.x + " - " + "y:" + src.y)
.style("transform", () => {
const point = svg.node().createSVGPoint();
point.x = src.x < median ? scaleX(src.x) : scaleX(src.x);
point.y = scaleY(src.y);
const screen = point.matrixTransform(svg.node().getScreenCTM());
return `translate(${screen.x}px,${screen.y}px)`;
});
});
}