高亮最近点d3js折线图和点图
Highlight closest point d3js line and dot chart
我目前有一个 d3js 折线图和点图。截至目前,当我将鼠标悬停在点上时,它会突出显示该点并显示显示的值。我想看到的是,当我将鼠标悬停在该线上时,它会将鼠标光标悬停在灯光上最接近的点上,并执行与悬停在某个点上时相同的操作。我确实检查了平分线但不确定它是否仅对带日期的数据有用。下面是 link 对应 fiddle 的图表。
https://jsfiddle.net/snt1/56qvqaz9/2/
var margin = {top: 30,right: 20,bottom: 100,left: 80 },
width = 400 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var svg = d3.select("#charts").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", "0 0 600 400")
//class to make it responsive
.classed("svg-content-responsive", true); ;
// Set the ranges
var x = d3.scaleBand().rangeRound([0, width]).padding(0.1);
var y = d3.scaleLinear().range([height, 0]);
var xAxis ;
function tickLabels(dataLength, d) {
if (dataLength > 9) return "";
return d.replace(/^.+_/, '')
}
/* var xAxis = d3.axisBottom().scale(x)
.ticks()
.tickFormat(function(d,i) { return tickLabels(toCSV.length, d) })*/
/*
var ticks = d3.selectAll(".tick text");
ticks.attr("class", function(d,i){
if(i%3 != 0) d3.select(this).remove();
});
*/
if (all.length < 16 ){
xAxis = d3.axisBottom().scale(x).ticks(10);
}
else
{
xAxis = d3.axisBottom().scale(x)
.tickFormat(function(d, i) {
return i % 3 === 0 ? d : null;
});
}
// var xAxis = d3.axisBottom().scale(x).ticks(10);
var yAxis = d3.axisLeft().scale(y).ticks(5).tickSizeInner(-width).tickSizeOuter(0).tickPadding(10);
// Define the line
var valueline = d3.line().curve(d3.curveCatmullRom)
.x(function (d) {
return x(d.label) + x.bandwidth() / 2;
})
.y(function (d) {
return y(d.value);
});
var data = all;
// Get the data
data.forEach(function (d) {
d.value = +d.value;
});
// Scale the range of the data
x.domain(data.map(function (d) {return d.label;}));
y.domain([0, d3.max(data, function (d) {return d.value;})]);
// Add the valueline path.
svg.append("path") // Add the valueline path.
.attr("d", valueline(data))
.attr("class", "line");
// Add the scatterplot
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 4)
.attr("cx", function (d) {return x(d.label) + x.bandwidth() / 2;})
.attr("cy", function(d) { return y(d.value); }).on("mouseover", function() {
tooltip.style("display", null);
})
/* .on("mouseout", function() {
tooltip.transition()
.duration(500)
.style("opacity", 0);
d3.select(this)
.attr("fill", "black")
.attr("r", 4);
})
*/
.on("mouseover", function() { tooltip.style("display", null); })
.on("mouseout", function() { tooltip.style("display", "none");
d3.select(this)
.attr("fill", "black")
.attr("r", 4);})
.on("mousemove", function(d) {
d3.select(this)
.attr("fill", "red")
.attr("r", 8);
tooltip.transition().duration(200)
.style("opacity", 0.9);
tooltip.select("div").html( d.name +":" +" <br><strong>" + d.value + "</strong>")
.style("position", "fixed")
.style("text-align", "center")
.style("width", "120px")
.style("height", "45px")
.style("padding", "2px")
.style("font", "12px sans-serif")
.style("background", "lightsteelblue")
.style("border", "0px")
.style("border-radius", "8px")
.style("left", (d3.event.pageX + 15) + "px")
.style("top", (d3.event.pageY - 28) + "px");
});
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0.5);
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "#ffffff")
.style("opacity", 0.5);
tooltip.append("div")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "1.5em")
.attr("font-weight", "bold");
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "0.1em")
.attr("dy", "-1em")
.attr("y", 30)
.attr("transform", "rotate(-30)");
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y",-30)
.attr("x", 0 - (height / 2))
.attr("dy", "-2em")
.attr("text-anchor", "middle")
.style("font-size", "13px")
.text("Count");
有很多方法可以做你想做的事。本解法暴力搜索直线上距离mouseover位置最近的点:
.attr("class", "line")
.on("mouseover", function(d) {
var mx = d3.mouse(svg.node())[0], // x position of cursor
c = 1e10,
idx = -1;
xs.forEach(function(x, i) { //xs is an array of points x location
var dis = Math.abs(x - mx); //distance
if (dis < c) {
c = dis;
idx = i; // find closest
}
});
var dot = dots.nodes()[idx], //get dot
d3Dot = d3.select(dot);
d3Dot.on("mouseover").call(dot, d3Dot.datum()); //call it's mouseover event
})
.on("mouseout", function(d){
tooltip.style("display", "none");
dots
.attr("fill", "black")
.attr("r", 4);
});
运行代码:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3@4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
<style>
.chart {
background: #eee;
padding: 3px;
}
.chart div {
width: 0;
transition: all 1s ease-out;
-moz-transition: all 1s ease-out;
-webkit-transition: all 1s ease-out;
}
.chart div {
font: 10px sans-serif;
background-color: steelblue;
text-align: right;
padding: 3px;
margin: 5px;
color: white;
box-shadow: 2px 2px 2px #666;
}
.bar {
fill: orange;
}
.bar:hover {
fill: red;
}
rect.background {
fill: white;
}
.axis {
shape-rendering: crispEdges;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
}
tooltip {
position: absolute;
text-align: center;
width: 120px;
height: 45px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
bar {
fill: #8CD3DD;
}
bar:hover {
fill: #F56C4E;
}
/* .tick:nth-child(3n) text {
visibility: hidden;
} */
.d3-tip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
content: "BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
Creates a small triangle extender for the tooltip .d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
content: "BC";
position: absolute;
text-align: center;
}
Style northward tooltips differently .d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
svg text.label {
fill: #ff0000;
font: 25px;
text-anchor: middle;
font-weight: 400;
text-anchor: middle;
font-size: 125%;
text-anchor: start;
}
path {
stroke: steelblue;
stroke-width: 2;
/* fill: none; */
/* commenting out to show multiple ring donut chart. */
}
pathline {
fill: none;
stroke-width: 2;
stroke: #000;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
.graph {
width: auto;
}
.tooltip {
color: black;
}
.axis {
font: 12px sans-serif, Georgia, Arial;
}
.axis path,
.axis line {
fill: none;
stroke: #dadada;
shape-rendering: crispEdges;
}
/* ANGULAR MODAL */
.dialogdemoBasicUsage #popupContainer {
position: relative;
}
.dialogdemoBasicUsage .footer {
width: 100%;
text-align: center;
margin-left: 20px;
}
.dialogdemoBasicUsage .footer,
.dialogdemoBasicUsage .footer > code {
font-size: 0.8em;
margin-top: 50px;
}
.dialogdemoBasicUsage button {
width: 200px;
}
.dialogdemoBasicUsage div#status {
color: #c60008;
}
.dialogdemoBasicUsage .dialog-demo-prerendered md-checkbox {
margin-bottom: 0;
}
/* Angular grids */
.gridListdemoBasicUsage md-grid-list {
margin: 8px;
}
gridListdemoBasicUsage .gray {
background: #f5f5f5;
}
.gridListdemoBasicUsage .green {
background: #b9f6ca;
}
.gridListdemoBasicUsage .yellow {
background: #ffff8d;
}
.gridListdemoBasicUsage .blue {
background: #84ffff;
}
.gridListdemoBasicUsage .purple {
background: #b388ff;
}
.gridListdemoBasicUsage .red {
background: #ff8a80;
}
.gridListdemoBasicUsage md-grid-tile {
transition: all 400ms ease-out 50ms;
}
md-grid-list md-grid-tile md-grid-tile-footer,
md-grid-list md-grid-tile md-grid-tile-header {
height: 25px;
}
.svg-container {
display: inline-block;
position: relative;
width: 100%;
padding-bottom: 12%;
vertical-align: top;
overflow: hidden;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 3px;
}
.svg-content-responsive {
display: inline-block;
position: absolute;
top: 10px;
left: 0;
}
.svg-content {
display: inline-block;
position: absolute;
top: 0;
left: 0;
}
#linechart {
width: 100%;
height: 100%;
position: absolute;
}
/* TESTING GRIDS */
.chart-tile {
display: block;
height: 100%;
width: 100%;
}
.gridListdemoDynamicTiles .s64 {
font-size: 64px;
}
.gridListdemoDynamicTiles .s32 {
font-size: 48px;
}
.gridListdemoDynamicTiles md-grid-list {
margin: 8px;
}
.gridListdemoDynamicTiles md-grid-tile {
box-shadow: 2px 2px 3px 3px #888888;
transition: all 300ms ease-out 50ms;
}
.gridListdemoDynamicTiles md-grid-tile md-grid-tile-footer {
background: rgba(0, 0, 0, 0.68);
height: 36px;
}
.gridListdemoDynamicTiles md-grid-tile-footer figcaption {
width: 100%;
}
.gridListdemoDynamicTiles md-grid-tile-footer figcaption h3 {
margin: 0;
font-weight: 700;
width: 100%;
text-align: center;
}
/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that can be in foundin the LICENSE file at https://material.angularjs.org/license.
*/
@media (min-width: 1200px) {
.container {
width: 1400px;
margin-right: -50px;
}
}
@media only screen and (max-width: 260px) and (min-width: 1600px) {
.container {
width: 1700px;
margin-right: -50px;
}
}
#wrapper {
border-style: solid;
height: 100px;
width: 200px;
}
#dropdown {
vertical-align: middle;
width: 80px;
margin-left: 300px;
margin-top: -30px;
}
/* Styles go here */
.gridster-item {
background-color: darkgrey;
}
.my-class {
border: 1px solid red;
height: 50px;
}
body {
background-color: #fcfcfc;
}
.container {
text-align: center;
padding: 15px;
}
.left-div {
display: inline-block;
max-width: 300px;
text-align: left;
padding: 30px;
background-color: #ddd;
border-radius: 3px;
margin: 15px;
vertical-align: top;
}
.right-div {
display: inline-block;
max-width: 150px;
text-align: left;
padding: 30px;
background-color: #ddd;
border-radius: 3px;
margin: 15px;
}
.left-text,
.right-text {
font: 300 16px/1.6 'Helvetica Neue' sans-serif;
color: #333;
}
@media screen and (max-width: 600px) {
.left-div, .right-div {
max-width: 100%;
}
</style>
</head>
<body>
<div id="charts">
</div>
<script>
var all = [{
"name": "SEASONAL_LYQ1",
"code": "SEASONAL_LYQ1",
"parent": "SEASONAL_POP",
"value": "0",
"label": "LYQ1",
"children": []
}, {
"name": "SEASONAL_LYQ2",
"code": "SEASONAL_LYQ2",
"parent": "SEASONAL_POP",
"value": "10",
"label": "LYQ2",
"children": []
}, {
"name": "SEASONAL_LYQ3",
"code": "SEASONAL_LYQ3",
"parent": "SEASONAL_POP",
"value": "16",
"label": "LYQ3",
"children": []
}, {
"name": "SEASONAL_LYQ4",
"code": "SEASONAL_LYQ4",
"parent": "SEASONAL_POP",
"value": "10",
"label": "LYQ4",
"children": []
}, {
"name": "SEASONAL_CYQ1",
"code": "SEASONAL_CYQ1",
"parent": "SEASONAL_POP",
"value": "0",
"label": "CYQ1",
"children": []
}, {
"name": "SEASONAL_CYQ2",
"code": "SEASONAL_CYQ2",
"parent": "SEASONAL_POP",
"value": "10",
"label": "CYQ2",
"children": []
}, {
"name": "SEASONAL_CYQ3",
"code": "SEASONAL_CYQ3",
"parent": "SEASONAL_POP",
"value": "16",
"label": "CYQ3",
"children": []
}, {
"name": "SEASONAL_CYQ4",
"code": "SEASONAL_CYQ4",
"parent": "SEASONAL_POP",
"value": "10",
"label": "CYQ4",
"children": []
}];
var margin = {
top: 30,
right: 20,
bottom: 100,
left: 80
},
width = 400 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var svg = d3.select("#charts").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", "0 0 600 400")
//class to make it responsive
.classed("svg-content-responsive", true);;
// Set the ranges
var x = d3.scaleBand().rangeRound([0, width]).padding(0.1);
var y = d3.scaleLinear().range([height, 0]);
var xAxis;
function tickLabels(dataLength, d) {
if (dataLength > 9) return "";
return d.replace(/^.+_/, '')
}
/* var xAxis = d3.axisBottom().scale(x)
.ticks()
.tickFormat(function(d,i) { return tickLabels(toCSV.length, d) })*/
/*
var ticks = d3.selectAll(".tick text");
ticks.attr("class", function(d,i){
if(i%3 != 0) d3.select(this).remove();
});
*/
if (all.length < 16) {
xAxis = d3.axisBottom().scale(x).ticks(10);
} else {
xAxis = d3.axisBottom().scale(x)
.tickFormat(function(d, i) {
return i % 3 === 0 ? d : null;
});
}
// var xAxis = d3.axisBottom().scale(x).ticks(10);
var yAxis = d3.axisLeft().scale(y).ticks(5).tickSizeInner(-width).tickSizeOuter(0).tickPadding(10);
// Define the line
var valueline = d3.line().curve(d3.curveCatmullRom)
.x(function(d) {
return x(d.label) + x.bandwidth() / 2;
})
.y(function(d) {
return y(d.value);
});
var data = all;
// Get the data
data.forEach(function(d) {
d.value = +d.value;
});
// Scale the range of the data
x.domain(data.map(function(d) {
return d.label;
}));
y.domain([0, d3.max(data, function(d) {
return d.value;
})]);
// Add the valueline path.
svg.append("path") // Add the valueline path.
.attr("d", valueline(data))
.attr("class", "line")
.on("mouseover", function(d) {
var mx = d3.mouse(svg.node())[0],
c = 1e10,
idx = -1;
xs.forEach(function(x, i) {
var dis = Math.abs(x - mx);
if (dis < c) {
c = dis;
idx = i;
}
});
var dot = dots.nodes()[idx],
d3Dot = d3.select(dot);
d3Dot.on("mouseover").call(dot, d3Dot.datum());
})
.on("mouseout", function(d){
tooltip.style("display", "none");
dots
.attr("fill", "black")
.attr("r", 4);
})
var xs = [];
// Add the scatterplot
var dots = svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 4)
.attr("cx", function(d) {
var xp = x(d.label) + x.bandwidth() / 2;
xs.push(xp);
return xp;
})
.attr("cy", function(d) {
return y(d.value);
})
.on("mouseout", function() {
tooltip.style("display", "none");
d3.select(this)
.attr("fill", "black")
.attr("r", 4);
})
.on("mouseover", function(d) {
tooltip.style("display", "block");
var self = d3.select(this);
self
.attr("fill", "red")
.attr("r", 8);
tooltip.transition().duration(200)
.style("opacity", 0.9);
tooltip.select("div").html(d.name + ":" + " <br><strong>" + d.value + "</strong>")
.style("position", "fixed")
.style("text-align", "center")
.style("width", "120px")
.style("height", "45px")
.style("padding", "2px")
.style("font", "12px sans-serif")
.style("background", "lightsteelblue")
.style("border", "0px")
.style("border-radius", "8px")
.style("left", (+self.attr('cx') + 100) + "px")
.style("top", (+self.attr('cy')) + "px");
});
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0.5);
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "#ffffff")
.style("opacity", 0.5);
tooltip.append("div")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "1.5em")
.attr("font-weight", "bold");
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "0.1em")
.attr("dy", "-1em")
.attr("y", 30)
.attr("transform", "rotate(-30)");
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -30)
.attr("x", 0 - (height / 2))
.attr("dy", "-2em")
.attr("text-anchor", "middle")
.style("font-size", "13px")
.text("Count");
</script>
</body>
</html>
我目前有一个 d3js 折线图和点图。截至目前,当我将鼠标悬停在点上时,它会突出显示该点并显示显示的值。我想看到的是,当我将鼠标悬停在该线上时,它会将鼠标光标悬停在灯光上最接近的点上,并执行与悬停在某个点上时相同的操作。我确实检查了平分线但不确定它是否仅对带日期的数据有用。下面是 link 对应 fiddle 的图表。
https://jsfiddle.net/snt1/56qvqaz9/2/
var margin = {top: 30,right: 20,bottom: 100,left: 80 },
width = 400 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var svg = d3.select("#charts").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", "0 0 600 400")
//class to make it responsive
.classed("svg-content-responsive", true); ;
// Set the ranges
var x = d3.scaleBand().rangeRound([0, width]).padding(0.1);
var y = d3.scaleLinear().range([height, 0]);
var xAxis ;
function tickLabels(dataLength, d) {
if (dataLength > 9) return "";
return d.replace(/^.+_/, '')
}
/* var xAxis = d3.axisBottom().scale(x)
.ticks()
.tickFormat(function(d,i) { return tickLabels(toCSV.length, d) })*/
/*
var ticks = d3.selectAll(".tick text");
ticks.attr("class", function(d,i){
if(i%3 != 0) d3.select(this).remove();
});
*/
if (all.length < 16 ){
xAxis = d3.axisBottom().scale(x).ticks(10);
}
else
{
xAxis = d3.axisBottom().scale(x)
.tickFormat(function(d, i) {
return i % 3 === 0 ? d : null;
});
}
// var xAxis = d3.axisBottom().scale(x).ticks(10);
var yAxis = d3.axisLeft().scale(y).ticks(5).tickSizeInner(-width).tickSizeOuter(0).tickPadding(10);
// Define the line
var valueline = d3.line().curve(d3.curveCatmullRom)
.x(function (d) {
return x(d.label) + x.bandwidth() / 2;
})
.y(function (d) {
return y(d.value);
});
var data = all;
// Get the data
data.forEach(function (d) {
d.value = +d.value;
});
// Scale the range of the data
x.domain(data.map(function (d) {return d.label;}));
y.domain([0, d3.max(data, function (d) {return d.value;})]);
// Add the valueline path.
svg.append("path") // Add the valueline path.
.attr("d", valueline(data))
.attr("class", "line");
// Add the scatterplot
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 4)
.attr("cx", function (d) {return x(d.label) + x.bandwidth() / 2;})
.attr("cy", function(d) { return y(d.value); }).on("mouseover", function() {
tooltip.style("display", null);
})
/* .on("mouseout", function() {
tooltip.transition()
.duration(500)
.style("opacity", 0);
d3.select(this)
.attr("fill", "black")
.attr("r", 4);
})
*/
.on("mouseover", function() { tooltip.style("display", null); })
.on("mouseout", function() { tooltip.style("display", "none");
d3.select(this)
.attr("fill", "black")
.attr("r", 4);})
.on("mousemove", function(d) {
d3.select(this)
.attr("fill", "red")
.attr("r", 8);
tooltip.transition().duration(200)
.style("opacity", 0.9);
tooltip.select("div").html( d.name +":" +" <br><strong>" + d.value + "</strong>")
.style("position", "fixed")
.style("text-align", "center")
.style("width", "120px")
.style("height", "45px")
.style("padding", "2px")
.style("font", "12px sans-serif")
.style("background", "lightsteelblue")
.style("border", "0px")
.style("border-radius", "8px")
.style("left", (d3.event.pageX + 15) + "px")
.style("top", (d3.event.pageY - 28) + "px");
});
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0.5);
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "#ffffff")
.style("opacity", 0.5);
tooltip.append("div")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "1.5em")
.attr("font-weight", "bold");
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "0.1em")
.attr("dy", "-1em")
.attr("y", 30)
.attr("transform", "rotate(-30)");
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y",-30)
.attr("x", 0 - (height / 2))
.attr("dy", "-2em")
.attr("text-anchor", "middle")
.style("font-size", "13px")
.text("Count");
有很多方法可以做你想做的事。本解法暴力搜索直线上距离mouseover位置最近的点:
.attr("class", "line")
.on("mouseover", function(d) {
var mx = d3.mouse(svg.node())[0], // x position of cursor
c = 1e10,
idx = -1;
xs.forEach(function(x, i) { //xs is an array of points x location
var dis = Math.abs(x - mx); //distance
if (dis < c) {
c = dis;
idx = i; // find closest
}
});
var dot = dots.nodes()[idx], //get dot
d3Dot = d3.select(dot);
d3Dot.on("mouseover").call(dot, d3Dot.datum()); //call it's mouseover event
})
.on("mouseout", function(d){
tooltip.style("display", "none");
dots
.attr("fill", "black")
.attr("r", 4);
});
运行代码:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3@4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
<style>
.chart {
background: #eee;
padding: 3px;
}
.chart div {
width: 0;
transition: all 1s ease-out;
-moz-transition: all 1s ease-out;
-webkit-transition: all 1s ease-out;
}
.chart div {
font: 10px sans-serif;
background-color: steelblue;
text-align: right;
padding: 3px;
margin: 5px;
color: white;
box-shadow: 2px 2px 2px #666;
}
.bar {
fill: orange;
}
.bar:hover {
fill: red;
}
rect.background {
fill: white;
}
.axis {
shape-rendering: crispEdges;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
}
tooltip {
position: absolute;
text-align: center;
width: 120px;
height: 45px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
bar {
fill: #8CD3DD;
}
bar:hover {
fill: #F56C4E;
}
/* .tick:nth-child(3n) text {
visibility: hidden;
} */
.d3-tip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
content: "BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
Creates a small triangle extender for the tooltip .d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
content: "BC";
position: absolute;
text-align: center;
}
Style northward tooltips differently .d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
svg text.label {
fill: #ff0000;
font: 25px;
text-anchor: middle;
font-weight: 400;
text-anchor: middle;
font-size: 125%;
text-anchor: start;
}
path {
stroke: steelblue;
stroke-width: 2;
/* fill: none; */
/* commenting out to show multiple ring donut chart. */
}
pathline {
fill: none;
stroke-width: 2;
stroke: #000;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
.graph {
width: auto;
}
.tooltip {
color: black;
}
.axis {
font: 12px sans-serif, Georgia, Arial;
}
.axis path,
.axis line {
fill: none;
stroke: #dadada;
shape-rendering: crispEdges;
}
/* ANGULAR MODAL */
.dialogdemoBasicUsage #popupContainer {
position: relative;
}
.dialogdemoBasicUsage .footer {
width: 100%;
text-align: center;
margin-left: 20px;
}
.dialogdemoBasicUsage .footer,
.dialogdemoBasicUsage .footer > code {
font-size: 0.8em;
margin-top: 50px;
}
.dialogdemoBasicUsage button {
width: 200px;
}
.dialogdemoBasicUsage div#status {
color: #c60008;
}
.dialogdemoBasicUsage .dialog-demo-prerendered md-checkbox {
margin-bottom: 0;
}
/* Angular grids */
.gridListdemoBasicUsage md-grid-list {
margin: 8px;
}
gridListdemoBasicUsage .gray {
background: #f5f5f5;
}
.gridListdemoBasicUsage .green {
background: #b9f6ca;
}
.gridListdemoBasicUsage .yellow {
background: #ffff8d;
}
.gridListdemoBasicUsage .blue {
background: #84ffff;
}
.gridListdemoBasicUsage .purple {
background: #b388ff;
}
.gridListdemoBasicUsage .red {
background: #ff8a80;
}
.gridListdemoBasicUsage md-grid-tile {
transition: all 400ms ease-out 50ms;
}
md-grid-list md-grid-tile md-grid-tile-footer,
md-grid-list md-grid-tile md-grid-tile-header {
height: 25px;
}
.svg-container {
display: inline-block;
position: relative;
width: 100%;
padding-bottom: 12%;
vertical-align: top;
overflow: hidden;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 3px;
}
.svg-content-responsive {
display: inline-block;
position: absolute;
top: 10px;
left: 0;
}
.svg-content {
display: inline-block;
position: absolute;
top: 0;
left: 0;
}
#linechart {
width: 100%;
height: 100%;
position: absolute;
}
/* TESTING GRIDS */
.chart-tile {
display: block;
height: 100%;
width: 100%;
}
.gridListdemoDynamicTiles .s64 {
font-size: 64px;
}
.gridListdemoDynamicTiles .s32 {
font-size: 48px;
}
.gridListdemoDynamicTiles md-grid-list {
margin: 8px;
}
.gridListdemoDynamicTiles md-grid-tile {
box-shadow: 2px 2px 3px 3px #888888;
transition: all 300ms ease-out 50ms;
}
.gridListdemoDynamicTiles md-grid-tile md-grid-tile-footer {
background: rgba(0, 0, 0, 0.68);
height: 36px;
}
.gridListdemoDynamicTiles md-grid-tile-footer figcaption {
width: 100%;
}
.gridListdemoDynamicTiles md-grid-tile-footer figcaption h3 {
margin: 0;
font-weight: 700;
width: 100%;
text-align: center;
}
/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that can be in foundin the LICENSE file at https://material.angularjs.org/license.
*/
@media (min-width: 1200px) {
.container {
width: 1400px;
margin-right: -50px;
}
}
@media only screen and (max-width: 260px) and (min-width: 1600px) {
.container {
width: 1700px;
margin-right: -50px;
}
}
#wrapper {
border-style: solid;
height: 100px;
width: 200px;
}
#dropdown {
vertical-align: middle;
width: 80px;
margin-left: 300px;
margin-top: -30px;
}
/* Styles go here */
.gridster-item {
background-color: darkgrey;
}
.my-class {
border: 1px solid red;
height: 50px;
}
body {
background-color: #fcfcfc;
}
.container {
text-align: center;
padding: 15px;
}
.left-div {
display: inline-block;
max-width: 300px;
text-align: left;
padding: 30px;
background-color: #ddd;
border-radius: 3px;
margin: 15px;
vertical-align: top;
}
.right-div {
display: inline-block;
max-width: 150px;
text-align: left;
padding: 30px;
background-color: #ddd;
border-radius: 3px;
margin: 15px;
}
.left-text,
.right-text {
font: 300 16px/1.6 'Helvetica Neue' sans-serif;
color: #333;
}
@media screen and (max-width: 600px) {
.left-div, .right-div {
max-width: 100%;
}
</style>
</head>
<body>
<div id="charts">
</div>
<script>
var all = [{
"name": "SEASONAL_LYQ1",
"code": "SEASONAL_LYQ1",
"parent": "SEASONAL_POP",
"value": "0",
"label": "LYQ1",
"children": []
}, {
"name": "SEASONAL_LYQ2",
"code": "SEASONAL_LYQ2",
"parent": "SEASONAL_POP",
"value": "10",
"label": "LYQ2",
"children": []
}, {
"name": "SEASONAL_LYQ3",
"code": "SEASONAL_LYQ3",
"parent": "SEASONAL_POP",
"value": "16",
"label": "LYQ3",
"children": []
}, {
"name": "SEASONAL_LYQ4",
"code": "SEASONAL_LYQ4",
"parent": "SEASONAL_POP",
"value": "10",
"label": "LYQ4",
"children": []
}, {
"name": "SEASONAL_CYQ1",
"code": "SEASONAL_CYQ1",
"parent": "SEASONAL_POP",
"value": "0",
"label": "CYQ1",
"children": []
}, {
"name": "SEASONAL_CYQ2",
"code": "SEASONAL_CYQ2",
"parent": "SEASONAL_POP",
"value": "10",
"label": "CYQ2",
"children": []
}, {
"name": "SEASONAL_CYQ3",
"code": "SEASONAL_CYQ3",
"parent": "SEASONAL_POP",
"value": "16",
"label": "CYQ3",
"children": []
}, {
"name": "SEASONAL_CYQ4",
"code": "SEASONAL_CYQ4",
"parent": "SEASONAL_POP",
"value": "10",
"label": "CYQ4",
"children": []
}];
var margin = {
top: 30,
right: 20,
bottom: 100,
left: 80
},
width = 400 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var svg = d3.select("#charts").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", "0 0 600 400")
//class to make it responsive
.classed("svg-content-responsive", true);;
// Set the ranges
var x = d3.scaleBand().rangeRound([0, width]).padding(0.1);
var y = d3.scaleLinear().range([height, 0]);
var xAxis;
function tickLabels(dataLength, d) {
if (dataLength > 9) return "";
return d.replace(/^.+_/, '')
}
/* var xAxis = d3.axisBottom().scale(x)
.ticks()
.tickFormat(function(d,i) { return tickLabels(toCSV.length, d) })*/
/*
var ticks = d3.selectAll(".tick text");
ticks.attr("class", function(d,i){
if(i%3 != 0) d3.select(this).remove();
});
*/
if (all.length < 16) {
xAxis = d3.axisBottom().scale(x).ticks(10);
} else {
xAxis = d3.axisBottom().scale(x)
.tickFormat(function(d, i) {
return i % 3 === 0 ? d : null;
});
}
// var xAxis = d3.axisBottom().scale(x).ticks(10);
var yAxis = d3.axisLeft().scale(y).ticks(5).tickSizeInner(-width).tickSizeOuter(0).tickPadding(10);
// Define the line
var valueline = d3.line().curve(d3.curveCatmullRom)
.x(function(d) {
return x(d.label) + x.bandwidth() / 2;
})
.y(function(d) {
return y(d.value);
});
var data = all;
// Get the data
data.forEach(function(d) {
d.value = +d.value;
});
// Scale the range of the data
x.domain(data.map(function(d) {
return d.label;
}));
y.domain([0, d3.max(data, function(d) {
return d.value;
})]);
// Add the valueline path.
svg.append("path") // Add the valueline path.
.attr("d", valueline(data))
.attr("class", "line")
.on("mouseover", function(d) {
var mx = d3.mouse(svg.node())[0],
c = 1e10,
idx = -1;
xs.forEach(function(x, i) {
var dis = Math.abs(x - mx);
if (dis < c) {
c = dis;
idx = i;
}
});
var dot = dots.nodes()[idx],
d3Dot = d3.select(dot);
d3Dot.on("mouseover").call(dot, d3Dot.datum());
})
.on("mouseout", function(d){
tooltip.style("display", "none");
dots
.attr("fill", "black")
.attr("r", 4);
})
var xs = [];
// Add the scatterplot
var dots = svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 4)
.attr("cx", function(d) {
var xp = x(d.label) + x.bandwidth() / 2;
xs.push(xp);
return xp;
})
.attr("cy", function(d) {
return y(d.value);
})
.on("mouseout", function() {
tooltip.style("display", "none");
d3.select(this)
.attr("fill", "black")
.attr("r", 4);
})
.on("mouseover", function(d) {
tooltip.style("display", "block");
var self = d3.select(this);
self
.attr("fill", "red")
.attr("r", 8);
tooltip.transition().duration(200)
.style("opacity", 0.9);
tooltip.select("div").html(d.name + ":" + " <br><strong>" + d.value + "</strong>")
.style("position", "fixed")
.style("text-align", "center")
.style("width", "120px")
.style("height", "45px")
.style("padding", "2px")
.style("font", "12px sans-serif")
.style("background", "lightsteelblue")
.style("border", "0px")
.style("border-radius", "8px")
.style("left", (+self.attr('cx') + 100) + "px")
.style("top", (+self.attr('cy')) + "px");
});
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0.5);
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "#ffffff")
.style("opacity", 0.5);
tooltip.append("div")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "1.5em")
.attr("font-weight", "bold");
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "0.1em")
.attr("dy", "-1em")
.attr("y", 30)
.attr("transform", "rotate(-30)");
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -30)
.attr("x", 0 - (height / 2))
.attr("dy", "-2em")
.attr("text-anchor", "middle")
.style("font-size", "13px")
.text("Count");
</script>
</body>
</html>