I am drawing charts with d3 in my Angular 2 application. Now I have a multi series line chart so I am trying to add tool tips at each line when hover its' vertical position.
export class LineGraphDirective {
private host;
private svg;
private margin;
private width;
private height;
private xScale; // D3 scale in X
private yScale; // D3 scale in Y
private zScale; // D3 color scale
private xAxis;
private yAxis;
private line;
private htmlElement:HTMLElement;
private parseDate;
private ds;
constructor(private element:ElementRef) {
this.htmlElement = this.element.nativeElement;
this.host = d3.select(this.element.nativeElement);
this.parseDate = d3.timeParse('%Y-%m-%d');
let data = [];
this.ngOnChanges(data);
}
/**
* Every time the @Input is updated, rebuild the chart
**/
ngOnChanges(data):void {
this.setup(data);
this.initData(data);
this.buildSVG();
this.scaleAxis(data);
this.populate();
this.drawXAxis();
this.drawYAxis();
this.zoomEventHandler();
this.addVerticalLineTooltip();
}
private setup(data):void {}
private initData(data) {}
/**
* build SVG element using the configurations
**/
private buildSVG():void {}
private scaleAxis(data) {}
/**
* Create x axis
**/
private drawXAxis():void {}
/**
*create y axis
**/
private drawYAxis():void {}
/**
* Populate the graphs
**/
private populate():void {}
private addVerticalLineTooltip() {
// append a g for all the mouse over nonsense
let mouseG = this.svg.append("g")
.attr("class", "mouse-over-effects");
// this is the vertical line
mouseG.append("path")
.attr("class", "mouse-line")
.style("stroke", "black")
.style("stroke-width", "1px")
.style("opacity", "0");
// keep a reference to all our lines
let lines = d3.select('.line');
// here's a g for each circle and text on the line
var mousePerLine = mouseG.selectAll('.mouse-per-line')
.data(this.ds)
.enter()
.append("g")
.attr("class", "mouse-per-line");
// the circle
mousePerLine.append("circle")
.attr("r", 7)
.style("stroke", (d) => {
return this.zScale(d.name);
})
.style("fill", "none")
.style("stroke-width", "1px")
.style("opacity", "0");
// the text
mousePerLine.append("text")
.attr("transform", "translate(10,3)");
// rect to capture mouse movements
mouseG.append('svg:rect')
.attr('width', this.width)
.attr('height', this.height)
.attr('fill', 'none')
.attr('pointer-events', 'all')
.on('mouseout', () => { // on mouse out hide line, circles and text
d3.select(".mouse-line")
.style("opacity", "0");
d3.selectAll(".mouse-per-line circle")
.style("opacity", "0");
d3.selectAll(".mouse-per-line text")
.style("opacity", "0");
})
.on('mouseover', () => { // on mouse in show line, circles and text
d3.select(".mouse-line")
.style("opacity", "1");
d3.selectAll(".mouse-per-line circle")
.style("opacity", "1");
d3.selectAll(".mouse-per-line text")
.style("opacity", "1");
})
.on('mousemove', () => { // mouse moving over canvas
let mouse = d3.mouse(this); // this is the line I am getting error
// move the vertical line
d3.select(".mouse-line")
.attr("d", () => {
let d = "M" + mouse[0] + "," + this.height;
d += " " + mouse[0] + "," + 0;
return d;
});
// position the circle and text
d3.selectAll(".mouse-per-line")
.attr("transform", (d, i) => {
let beginning = 0,
end = d3.select(lines[i]).node().getTotalLength(),
target,
pos;
while (true) {
target = Math.floor((beginning + end) / 2);
pos = d3.select(lines[i]).node().getPointAtLength(target);
if ((target === end || target === beginning) && pos.x !== mouse[0]) {
break;
}
if (pos.x > mouse[0]) end = target;
else if (pos.x < mouse[0]) beginning = target;
else break; //position found
}
// update the text with y value
d3.select(this).select('text')
.text(this.yScale.invert(pos.y).toFixed(2));
// return position
return "translate(" + mouse[0] + "," + pos.y + ")";
});
});
}
private zoomEventHandler() {
let zoom = d3.zoom()
.scaleExtent([1, 2])
.translateExtent([[0, -100], this.width + 90, this.height + 100]).on("zoom", () => this.zoomed());
this.svg.call(zoom);
}
private zoomed() {
}
}
I am getting following error message on browser console.
node.getBoundingClientRect is not a function
Line: let mouse = d3.mouse(this);
Any suggestions?
Thank you!
I guess you should use it:
let mouse = d3.mouse(mouseG);
or you can write like:
let mouse = d3.mouse(d3.event.currentTarget);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With