本作业的目标是创建交互式可视化。我正在制作一个条形图,其中包含用于切换某些条形的图例。
我的图表的 y 轴显示欧洲的电动汽车销量,x 轴显示国家/地区。图表的颜色代表该国家/地区的人口范围。我想创建一个图例,当单击特定颜色时,会用相应的颜色切换条形。
这是我第一次使用 D3,所以我的代码可能会也可能不会达到标准。如有任何帮助,我们将不胜感激!
var salesData;
var runningData;
var runningColors;
var customLegendData = [{
label: "Population: >10 Million",
color: "#008B8B"
},
{
label: "Population: 10 Million - 50 Million",
color: "#2F4F4F"
},
{
label: "Population: 50 Million - 100 Million",
color: "#48308B"
}
];
$(document).ready(function() {
Plot();
addCustomLegend(customLegendData);
attachLegendToggle();
});
function Plot() {
TransformChartData(chartData, chartOptions);
BuildBar("chart", chartData, chartOptions);
}
function BuildBar(id, chartData, options, level) {
var chart = d3.select("#" + id);
var margin = {
top: 10,
right: 10,
bottom: 70,
left: 70
},
barWidth = 70,
barPadding = 5;
var width = (barWidth + barPadding) * runningData.length; // Adjusted the width
var height = $(chart[0]).outerHeight() - margin.top - margin.bottom;
var xVarName;
var yVarName = options[0].yaxis;
xVarName = options[0].xaxis;
var xAry = runningData.map(function(el) {
return el[xVarName];
});
var yAry = runningData.map(function(el) {
return el[yVarName];
});
var capAry = runningData.map(function(el) {
return el.caption;
});
var x = d3.scale.ordinal().domain(xAry).rangeRoundBands([0, width], 0.1);
var yMax = d3.max(runningData, function(d) {
return +d[yVarName];
});
var y = d3.scale.linear().domain([0, yMax]).range([height, 0]);
var rcolor = d3.scale.ordinal().range(runningColors);
chart = chart
.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 + ")");
var bars = chart.selectAll("g")
.data(runningData)
.enter()
.append("g")
.attr("class", function(d) {
return "bar " + d[xVarName]; // Add a class based on the country
})
.attr("data-country", function(d) {
return d[xVarName]; // Add data attribute with the country name
})
.attr("transform", function(d) {
return "translate(" + (x(d[xVarName]) + barPadding / 2) + ", 0)";
});
var ctrtxt = 0;
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(xAry.length)
.tickFormat(function(d) {
var mapper = options[0].captions[0];
return mapper[d];
});
var yAxisTicks = [0, 10000, 50000, 100000, 200000, 300000, 400000]
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickValues(yAxisTicks);
bars.append("rect")
.attr("class", function(d) {
return "bar-" + d[xVarName]; // Add a class based on the country
})
.attr("y", function(d) {
return y(parseInt(d[yVarName]));
})
.attr("height", function(d) {
return height - y(parseInt(d[yVarName]));
})
.attr("width", x.rangeBand() - barPadding)
.style("fill", function(d) {
return rcolor(d[xVarName]);
});
bars.append("text")
.attr("x", x.rangeBand() / 2)
.attr("y", function(d) {
return y(parseInt(d[yVarName])) - 5;
})
.attr("dy", ".35em")
.text(function(d) {
return d[yVarName];
})
.attr("class", "bar-text");
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll(".tick text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-45)");
chart.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -40)
.attr("dy", ".71em")
.style("text-anchor", "end");
return chart;
}
function attachLegendToggle() {
d3.selectAll("#legend .legend-item").on("click", function(event, d) {
var country = d.label;
var barsToToggle = d3.selectAll("#chart [data-country='" + country + "']");
barsToToggle.classed("hidden", function() {
var isHidden = d3.select(this).classed("hidden");
return !isHidden;
});
});
}
function TransformChartData(chartData, opts, level, filter) {
var result = [];
var resultColors = [];
var counter = 0;
var hasMatch;
var xVarName;
var yVarName = opts[0].yaxis;
xVarName = opts[0].xaxis;
for (var i in chartData) {
hasMatch = false;
for (var index = 0; index < result.length; ++index) {
var data = result[index];
if (data[xVarName] == chartData[i][xVarName]) {
result[index][yVarName] = result[index][yVarName] + chartData[i][yVarName];
hasMatch = true;
break;
}
}
if (hasMatch == false) {
ditem = {};
ditem[xVarName] = chartData[i][xVarName];
ditem[yVarName] = chartData[i][yVarName];
ditem["caption"] = opts[0].captions != undefined ? opts[0].captions[0]
[chartData[i][xVarName]] : "";
ditem["title"] = opts[0].captions != undefined ? opts[0].captions[0]
[chartData[i][xVarName]] : "";
ditem["op"] = 1;
result.push(ditem);
resultColors[counter] = opts[0].color != undefined ? opts[0].color[0]
[chartData[i][xVarName]] : "";
counter += 1;
}
}
runningData = result;
runningColors = resultColors;
return;
}
var chartData = [{
"Country": "UK",
"Sales": "370000"
},
{
"Country": "France",
"Sales": "340000"
},
{
"Country": "Norway",
"Sales": "166000"
},
{
"Country": "Sweden",
"Sales": "163000"
},
{
"Country": "Italy",
"Sales": "114000"
},
{
"Country": "Netherlands",
"Sales": "107000"
},
{
"Country": "Belgium",
"Sales": "97000"
},
{
"Country": "Spain",
"Sales": "82000"
},
{
"Country": "Switzerland",
"Sales": "59000"
},
{
"Country": "Denmark",
"Sales": "57000"
},
{
"Country": "Austria",
"Sales": "38900"
},
{
"Country": "Portugal",
"Sales": "34000"
},
{
"Country": "Finland",
"Sales": "31000"
},
{
"Country": "Poland",
"Sales": "25000"
},
{
"Country": "Iceland",
"Sales": "11900"
},
{
"Country": "Greece",
"Sales": "8300"
},
{
"Country": "Turkey",
"Sales": "7540"
},
];
chartOptions = [{
"captions": [{
"Germany": "Germany",
"UK": "UK",
"France": "France",
"Norway": "Norway",
"Sweden": "Sweden",
"Italy": "Italy",
"Netherlands": "Netherlands",
"Belgium": "Belgium",
"Spain": "Spain",
"Switzerland": "Switzerland",
"Denmark": "Denmark",
"Austria": "Austria",
"Portugal": "Portugal",
"Finland": "Finland",
"Poland": "Poland",
"Iceland": "Iceland",
"Greece": "Greece",
"Turkey": "Turkey",
}],
"color": [{
"Germany": "#483D8B",
"UK": "#483D8B",
"France": "#483D8B",
"Norway": "#008B8B",
"Sweden": "#2F4F4F",
"Italy": "#483D8B",
"Netherlands": "#2F4F4F",
"Belgium": "#2F4F4F",
"Spain": "#2F4F4F",
"Switzerland": "#008B8B",
"Denmark": "#008B8B",
"Austria": "#008B8B",
"Portugal": "#2F4F4F",
"Finland": "#483D8B",
"Poland": "#2F4F4F",
"Iceland": "#008B8B",
"Greece": "#2F4F4F",
"Turkey": "#483D8B",
}],
"xaxis": "Country",
"yaxis": "Sales"
}];
function addCustomLegend(legendData) {
var legend = d3.select("#legend")
.selectAll(".legend-item") // Add class selector
.data(legendData)
.enter()
.append("g")
.attr("class", "legend-item") // Add class
.attr("transform", function(d, i) {
return "translate(0," + i * 20 + ")";
});
legend.append("rect")
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d) {
return d.color;
});
legend.append("text")
.attr("x", 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d) {
return d.label;
});
}
#chart text {
fill: black;
font: 10px sans-serif;
text-anchor: end;
}
.axis text {
font: bold 10px sans-serif;
}
.axis text.x-axis-label {
font: bold 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
shape-rendering: crispEdges;
}
html,
body {
height: 100%;
margin: 0;
}
body {
color: #eaeaea;
padding: 0px;
background: linear-gradient(to bottom, #3498db, #ffffff);
background-attachment: fixed;
}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
#chart-title {
text-align: center;
margin-top: 20px;
}
#chart-title h2 {
font-size: 50px;
color: #333;
}
#chart {
width: 100%;
}
#legend {
position: absolute;
padding-top: 1%;
padding-right: 1%;
padding-left: 1%;
top: 100px;
right: 20px;
background-color: #FFFFFF;
}
<script src="https://code.jquery.com/jquery-1.12.4.min.js" charset="utf-8"></script>
<script src="https://d3js.org/d3.v3.min.js"></script>
<div id="chart-title">
<h2>2022 Electric Vehicle Sales in Europe</h2>
</div>
<div id="chart" style="height:100%; width:100%;"></div>
<svg id="legend" width="200" height="200"></svg>
var salesData;
var runningData;
var runningColors;
var customLegendData = [{
label: "Population: >10 Million",
color: "#008B8B",
lowerRange : 0,
upperRange : 10000000
},
{
label: "Population: 10 Million - 50 Million",
color: "#2F4F4F",
lowerRange : 10000000,
upperRange : 50000000
},
{
label: "Population: 50 Million - 100 Million",
color: "#48308B",
lowerRange : 50000000,
upperRange : 100000000
}
];
$(document).ready(function() {
Plot();
addCustomLegend(customLegendData);
attachLegendToggle();
});
function Plot() {
TransformChartData(chartData, chartOptions);
BuildBar("chart", chartData, chartOptions);
}
function BuildBar(id, chartData, options, level) {
d3.select("#" + id).select("svg").remove();
var chart = d3.select("#" + id);
var margin = {
top: 10,
right: 10,
bottom: 70,
left: 70
},
barWidth = 70,
barPadding = 5;
var width = (barWidth + barPadding) * runningData.length; // Adjusted the width
var height = $(chart[0]).outerHeight() - margin.top - margin.bottom;
var xVarName;
var yVarName = options[0].yaxis;
xVarName = options[0].xaxis;
var xAry = runningData.map(function(el) {
return el[xVarName];
});
var yAry = runningData.map(function(el) {
return el[yVarName];
});
var capAry = runningData.map(function(el) {
return el.caption;
});
var x = d3.scale.ordinal().domain(xAry).rangeRoundBands([0, width], 0.1);
var yMax = d3.max(runningData, function(d) {
return +d[yVarName];
});
var y = d3.scale.linear().domain([0, yMax]).range([height, 0]);
var rcolor = d3.scale.ordinal().range(runningColors);
chart = chart
.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 + ")");
var bars = chart.selectAll("g")
.data(runningData)
.enter()
.append("g")
.attr("class", function(d) {
return "bar " + d[xVarName]; // Add a class based on the country
})
.attr("data-country", function(d) {
return d[xVarName]; // Add data attribute with the country name
})
.attr("transform", function(d) {
return "translate(" + (x(d[xVarName]) + barPadding / 2) + ", 0)";
});
var ctrtxt = 0;
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(xAry.length)
.tickFormat(function(d) {
var mapper = options[0].captions[0];
return mapper[d];
});
var yAxisTicks = [0, 10000, 50000, 100000, 200000, 300000, 400000]
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickValues(yAxisTicks);
bars.append("rect")
.attr("class", function(d) {
return "bar-" + d[xVarName]; // Add a class based on the country
})
.attr("y", function(d) {
return y(parseInt(d[yVarName]));
})
.attr("height", function(d) {
return height - y(parseInt(d[yVarName]));
})
.attr("width", x.rangeBand() - barPadding)
.style("fill", function(d) {
return rcolor(d[xVarName]);
});
bars.append("text")
.attr("x", x.rangeBand() / 2)
.attr("y", function(d) {
return y(parseInt(d[yVarName])) - 5;
})
.attr("dy", ".35em")
.text(function(d) {
return d[yVarName];
})
.attr("class", "bar-text");
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll(".tick text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-45)");
chart.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -40)
.attr("dy", ".71em")
.style("text-anchor", "end");
return chart;
}
function attachLegendToggle() {
d3.selectAll("#legend .legend-item").on("click", function(event, d) {
var country = d.label;
var barsToToggle = d3.selectAll("#chart [data-country='" + country + "']");
barsToToggle.classed("hidden", function() {
var isHidden = d3.select(this).classed("hidden");
return !isHidden;
});
});
}
function TransformChartData(chartData, opts, level, filter) {
var result = [];
var resultColors = [];
var counter = 0;
var hasMatch;
var xVarName;
var yVarName = opts[0].yaxis;
xVarName = opts[0].xaxis;
for (var i in chartData) {
hasMatch = false;
for (var index = 0; index < result.length; ++index) {
var data = result[index];
if (data[xVarName] == chartData[i][xVarName]) {
result[index][yVarName] = result[index][yVarName] + chartData[i][yVarName];
hasMatch = true;
break;
}
}
if (hasMatch == false) {
ditem = {};
ditem[xVarName] = chartData[i][xVarName];
ditem[yVarName] = chartData[i][yVarName];
ditem["caption"] = opts[0].captions != undefined ? opts[0].captions[0]
[chartData[i][xVarName]] : "";
ditem["title"] = opts[0].captions != undefined ? opts[0].captions[0]
[chartData[i][xVarName]] : "";
ditem["op"] = 1;
result.push(ditem);
resultColors[counter] = opts[0].color != undefined ? opts[0].color[0]
[chartData[i][xVarName]] : "";
counter += 1;
}
}
runningData = result;
runningColors = resultColors;
return;
}
var chartData = [{
"Country": "UK",
"Sales": "370000",
"Population":60000000
},
{
"Country": "France",
"Sales": "340000",
"Population":70000000
},
{
"Country": "Norway",
"Sales": "166000",
"Population":6000000
},
{
"Country": "Sweden",
"Sales": "163000",
"Population":11000000
},
{
"Country": "Italy",
"Sales": "114000",
"Population":80000000
},
{
"Country": "Netherlands",
"Sales": "107000",
"Population":12000000
},
{
"Country": "Belgium",
"Sales": "97000",
"Population":13000000
},
{
"Country": "Spain",
"Sales": "82000",
"Population":14000000
},
{
"Country": "Switzerland",
"Sales": "59000",
"Population":7000000
},
{
"Country": "Denmark",
"Sales": "57000",
"Population":8000000
},
{
"Country": "Austria",
"Sales": "38900",
"Population":9000000
},
{
"Country": "Portugal",
"Sales": "34000",
"Population":15000000
},
{
"Country": "Finland",
"Sales": "31000",
"Population":90000000
},
{
"Country": "Poland",
"Sales": "25000",
"Population":16000000
},
{
"Country": "Iceland",
"Sales": "11900",
"Population":6500000
},
{
"Country": "Greece",
"Sales": "8300",
"Population":95000000
},
{
"Country": "Turkey",
"Sales": "7540",
"Population":96000000
},
];
chartOptions = [{
"captions": [{
"Germany": "Germany",
"UK": "UK",
"France": "France",
"Norway": "Norway",
"Sweden": "Sweden",
"Italy": "Italy",
"Netherlands": "Netherlands",
"Belgium": "Belgium",
"Spain": "Spain",
"Switzerland": "Switzerland",
"Denmark": "Denmark",
"Austria": "Austria",
"Portugal": "Portugal",
"Finland": "Finland",
"Poland": "Poland",
"Iceland": "Iceland",
"Greece": "Greece",
"Turkey": "Turkey",
}],
"color": [{
"Germany": "#483D8B",
"UK": "#483D8B",
"France": "#483D8B",
"Norway": "#008B8B",
"Sweden": "#2F4F4F",
"Italy": "#483D8B",
"Netherlands": "#2F4F4F",
"Belgium": "#2F4F4F",
"Spain": "#2F4F4F",
"Switzerland": "#008B8B",
"Denmark": "#008B8B",
"Austria": "#008B8B",
"Portugal": "#2F4F4F",
"Finland": "#483D8B",
"Poland": "#2F4F4F",
"Iceland": "#008B8B",
"Greece": "#2F4F4F",
"Turkey": "#483D8B",
}],
"xaxis": "Country",
"yaxis": "Sales"
}];
function addCustomLegend(legendData) {
var legend = d3.select("#legend")
.selectAll(".legend-item") // Add class selector
.data(legendData)
.enter()
.append("g")
.attr("class", "legend-item") // Add class
.attr("transform", function(d, i) {
return "translate(0," + i * 20 + ")";
});
legend.append("rect")
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d) {
return d.color;
})
.on("click",function(data){
console.log(data)
let updattedChartData = chartData.filter((el => el.Population >= data.lowerRange && el.Population <=data.upperRange))
TransformChartData(updattedChartData, chartOptions);
BuildBar("chart", updattedChartData, chartOptions);
});
legend.append("text")
.attr("x", 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d) {
return d.label;
})
.on("click",function(data){
console.log(data)
let updattedChartData = chartData.filter((el => el.Population >= data.lowerRange && el.Population <=data.upperRange))
TransformChartData(updattedChartData, chartOptions);
BuildBar("chart", updattedChartData, chartOptions);
});
}
#chart text {
fill: black;
font: 10px sans-serif;
text-anchor: end;
}
.axis text {
font: bold 10px sans-serif;
}
.axis text.x-axis-label {
font: bold 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
shape-rendering: crispEdges;
}
html,
body {
height: 100%;
margin: 0;
}
body {
color: #eaeaea;
padding: 0px;
background: linear-gradient(to bottom, #3498db, #ffffff);
background-attachment: fixed;
}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
#chart-title {
text-align: center;
margin-top: 20px;
}
#chart-title h2 {
font-size: 50px;
color: #333;
}
#chart {
width: 100%;
}
#legend {
position: absolute;
padding-top: 1%;
padding-right: 1%;
padding-left: 1%;
top: 100px;
right: 20px;
background-color: #FFFFFF;
}
<script src="https://code.jquery.com/jquery-1.12.4.min.js" charset="utf-8"></script>
<script src="https://d3js.org/d3.v3.min.js"></script>
<div id="chart-title">
<h2>2022 Electric Vehicle Sales in Europe</h2>
</div>
<div id="chart" style="height:100%; width:100%;"></div>
<svg id="legend" width="280" height="80"></svg>
注意:您使用的是过时版本的 D3.js,并且您的代码似乎杂乱无章。为简单的条形图创建更有组织、更高效的代码还有改进的空间。