我正在使用以下代码,但似乎无法弄清楚为什么 d3.exit 在这里无法按预期工作。
例如,2000 到 2046 之间的前 3 个排名如下。
2000, A-B-C 2046, C-E-B
当我在 2000 年之后选择 2046 时,我希望旧数据 (A) 按照下面的exit代码移出。但它没有发生。
exit
exit.style('fill', 'red') .transition().duration(1000) .attr('transform', (d, i) => `translate(${ 100 },${ d.Rank * 50 })`) .remove()
//desired permutation length const length = 5; //build array from the above length const perm = Array.from(Array(length).keys()).map((d) => d + 1); //generate corresponding alphabets for name const name = perm.map((x) => String.fromCharCode(x - 1 + 65)); //permutation function - https://stackoverflow.com/questions/9960908/permutations-in-javascript/24622772#24622772 function permute(permutation) { var length = permutation.length, result = [permutation.slice()], c = new Array(length).fill(0), i = 1, k, p; while (i < length) { if (c[i] < i) { k = i % 2 && c[i]; p = permutation[i]; permutation[i] = permutation[k]; permutation[k] = p; ++c[i]; i = 1; result.push(permutation.slice()); } else { c[i] = 0; ++i; } } return result; }; //generate permutations const permut = permute(perm); //generate year based on permutation const year = permut.map((x, i) => i + 2000); //generate a yearly constant based on year to generate final value as per the rank {year-name} const constant = year.map(d => Math.round(d * Math.random())); const src = year.map((y, i) => { return name.map((d, j) => { return { Name: d, Year: y, Rank: permut[i][j], Const: constant[i], Value: Math.round(constant[i] / permut[i][j]) }; }); }).flat(); //console.log(src); //////////////////////////////////////////////////////////// //////////////////////// 0 BUILD HTML DROPDOWN ///////////// //////////////////////////////////////////////////////////// const dropDown = d3.select('body') .append('div', 'dropdown') .style('position', 'absolute') .style('top', '400px') .append('select') .attr('name', 'input') .classed('Year', true) dropDown .selectAll('option') .data(year) .enter() .append('option') //.join('option') .text((d) => d) // text showed in the menu .attr("value", (d) => { d }) // corresponding value returned by the button //get the dropdown value const filterYr = parseFloat(d3.select('.Year').node().value); dropDown.on('change', () => { const yr = parseFloat(d3.select('.Year').node().value); draw(yr); }) //////////////////////////////////////////////////////////// //////////////////////// 1 DATA WRANGLING ////////////////// //////////////////////////////////////////////////////////// const xAccessor = (d) => d.Year; const yAccessor = (d) => d.Value; //const zAccessor = (d) => d.Category; //////////////////////////////////////////////////////////// //////////////////////// 2 CREATE SVG ////////////////////// //////////////////////////////////////////////////////////// //namespace //define dimension const width = 1536; const height = 720; const svgns = "http://www.w3.org/2000/svg"; const svg = d3.select("svg"); svg.attr("xmlns", svgns).attr("viewBox", `0 0 ${width} ${height}`); svg .append("rect") .attr("class", "vBoxRect") //.style("overflow", "visible") .attr("width", `${width}`) .attr("height", `${height}`) .attr("stroke", "black") .attr("fill", "white"); //////////////////////////////////////////////////////////// //////////////////////// 3 CREATE BOUND //////////////////// //////////////////////////////////////////////////////////// const padding = { top: 70, bottom: 100, left: 120, right: 120 }; const multiplierH = 1; //controls the height of the visual container const multiplierW = 1; //controls the width of the visual container const boundHeight = height * multiplierH - padding.top - padding.bottom; const boundWidth = width * multiplierW - 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", "white") .attr('stroke', 'black') //create bound element const bound = svg .append("g") .attr("class", "bound") //specify transform, must be .style and not .attr, px needs to be mentioned .style("transform", `translate(${padding.left}px,${padding.top}px)`); const textContainer = bound.append('g') .classed('textContainer', true) function draw(filterYr) { const data = src.filter(a => a.Year == filterYr) //console.log(data.filter(a => a.Rank >= 1 && a.Rank <= 3).sort((a, b) => a.Rank - b.Rank)); //////////////////////////////////////////////////////////// //////////////////////// 4 CREATE SCALE //////////////////// //////////////////////////////////////////////////////////// textContainer .selectAll('text') .data(data.filter(a => a.Rank >= 1 && a.Rank <= 3)) .join( enter => enter.append('text') .attr('transform', (d, i) => `translate(${ 0 },${ d.Rank * 50 })`) .attr('x', 30) .attr('dy', '1.25em') .style('font-family', 'sans-serif') .style('font-size', 'xx-large') .style('fill', 'blue') .style('opacity', '0') .text(d => d.Name) .transition().duration(1000) .style('opacity', 1) .selection(), update => update .transition().duration(1000) .attr('transform', (d, i) => `translate(${ 0 },${ d.Rank * 50 })`) .style('fill', 'black') .text(d => d.Name) .selection(), exit => exit .style('fill', 'red') .transition().duration(1000) .attr('transform', (d, i) => `translate(${ 100 },${ d.Rank * 50 })`) .remove() ) } draw(filterYr); <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script> <body> <svg> </svg> <!--d3 script--> <script type="text/javascript" src="prod5.js"> </script> </body> </html>
如果您希望数据受其值而不是其索引绑定,则必须提供一个键功能:
.data(data.filter(a => a.Rank >= 1 && a.Rank <= 3), d => d.Name) //this is the key function ---------------------------^
这是您进行更改的代码:
Hide code snippet
//desired permutation length const length = 5; //build array from the above length const perm = Array.from(Array(length).keys()).map((d) => d + 1); //generate corresponding alphabets for name const name = perm.map((x) => String.fromCharCode(x - 1 + 65)); //permutation function - https://stackoverflow.com/questions/9960908/permutations-in-javascript/24622772#24622772 function permute(permutation) { var length = permutation.length, result = [permutation.slice()], c = new Array(length).fill(0), i = 1, k, p; while (i < length) { if (c[i] < i) { k = i % 2 && c[i]; p = permutation[i]; permutation[i] = permutation[k]; permutation[k] = p; ++c[i]; i = 1; result.push(permutation.slice()); } else { c[i] = 0; ++i; } } return result; }; //generate permutations const permut = permute(perm); //generate year based on permutation const year = permut.map((x, i) => i + 2000); //generate a yearly constant based on year to generate final value as per the rank {year-name} const constant = year.map(d => Math.round(d * Math.random())); const src = year.map((y, i) => { return name.map((d, j) => { return { Name: d, Year: y, Rank: permut[i][j], Const: constant[i], Value: Math.round(constant[i] / permut[i][j]) }; }); }).flat(); //console.log(src); //////////////////////////////////////////////////////////// //////////////////////// 0 BUILD HTML DROPDOWN ///////////// //////////////////////////////////////////////////////////// const dropDown = d3.select('body') .append('div', 'dropdown') .style('position', 'absolute') .style('top', '400px') .append('select') .attr('name', 'input') .classed('Year', true) dropDown .selectAll('option') .data(year) .enter() .append('option') //.join('option') .text((d) => d) // text showed in the menu .attr("value", (d) => { d }) // corresponding value returned by the button //get the dropdown value const filterYr = parseFloat(d3.select('.Year').node().value); dropDown.on('change', () => { const yr = parseFloat(d3.select('.Year').node().value); draw(yr); }) //////////////////////////////////////////////////////////// //////////////////////// 1 DATA WRANGLING ////////////////// //////////////////////////////////////////////////////////// const xAccessor = (d) => d.Year; const yAccessor = (d) => d.Value; //const zAccessor = (d) => d.Category; //////////////////////////////////////////////////////////// //////////////////////// 2 CREATE SVG ////////////////////// //////////////////////////////////////////////////////////// //namespace //define dimension const width = 1536; const height = 720; const svgns = "http://www.w3.org/2000/svg"; const svg = d3.select("svg"); svg.attr("xmlns", svgns).attr("viewBox", `0 0 ${width} ${height}`); svg .append("rect") .attr("class", "vBoxRect") //.style("overflow", "visible") .attr("width", `${width}`) .attr("height", `${height}`) .attr("stroke", "black") .attr("fill", "white"); //////////////////////////////////////////////////////////// //////////////////////// 3 CREATE BOUND //////////////////// //////////////////////////////////////////////////////////// const padding = { top: 70, bottom: 100, left: 120, right: 120 }; const multiplierH = 1; //controls the height of the visual container const multiplierW = 1; //controls the width of the visual container const boundHeight = height * multiplierH - padding.top - padding.bottom; const boundWidth = width * multiplierW - 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", "white") .attr('stroke', 'black') //create bound element const bound = svg .append("g") .attr("class", "bound") //specify transform, must be .style and not .attr, px needs to be mentioned .style("transform", `translate(${padding.left}px,${padding.top}px)`); const textContainer = bound.append('g') .classed('textContainer', true) function draw(filterYr) { const data = src.filter(a => a.Year == filterYr) //console.log(data.filter(a => a.Rank >= 1 && a.Rank <= 3).sort((a, b) => a.Rank - b.Rank)); //////////////////////////////////////////////////////////// //////////////////////// 4 CREATE SCALE //////////////////// //////////////////////////////////////////////////////////// textContainer .selectAll('text') .data(data.filter(a => a.Rank >= 1 && a.Rank <= 3), d => d.Name) .join( enter => enter.append('text') .attr('transform', (d, i) => `translate(${ 0 },${ d.Rank * 50 })`) .attr('x', 30) .attr('dy', '1.25em') .style('font-family', 'sans-serif') .style('font-size', 'xx-large') .style('fill', 'blue') .style('opacity', '0') .text(d => d.Name) .transition().duration(1000) .style('opacity', 1) .selection(), update => update .transition().duration(1000) .attr('transform', (d, i) => `translate(${ 0 },${ d.Rank * 50 })`) .style('fill', 'black') .text(d => d.Name) .selection(), exit => exit .style('fill', 'red') .transition().duration(1000) .attr('transform', (d, i) => `translate(${ 100 },${ d.Rank * 50 })`) .remove() ) } draw(filterYr); <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script> <body> <svg> </svg> <!--d3 script--> <script type="text/javascript" src="prod5.js"> </script> </body> </html>