【问题标题】:How to add transition effect to d3.js Chart如何为 d3.js 图表添加过渡效果
【发布时间】:2021-07-01 13:40:00
【问题描述】:

我试图向图表添加过渡效果,但它不起作用。 在这种情况下不可能吗?因为我尝试了 d3.js 网站中提到的所有步骤。 我正在尝试在条形和路径、矩形和文本上添加过渡。

下面是我的尝试:

.transition()
  .duration(2000)

任何帮助都将不胜感激。

代码下方:

const barData = [{
"Time": "2019",
"Value": 5388
  },
  {
"Time": "2020",
"Value": 6453
  },
  {
"Time": "2021",
"Value": -4345
  },
  {
"Time": "2022",
"Value": 7345
  },
  {
"Time": "2023",
"Value": 8345
  },
  {
"Time": "2024",
"Value": 5345
  },
  {
"Time": "2025",
"Value": 6345
  },
  {
"Time": "2026",
"Value": 3345
  }
];

const container = d3.select('#graph');
const divWidth = parseInt(container.style('width'));
const divHeight = parseInt(container.style('height'));

// Consider this width and Height are dynamic for div "graphID" because I am trying to responsive design
const margin = {
  top: 50,
  right: 50,
  bottom: 50,
  left: 50
};
const width = divWidth - margin.left - margin.right;
const height = divHeight - margin.top - margin.bottom;

//To add svg in the visualization node i.e Dome node                    
const svg = container.append("svg")
  .attr("width", divWidth)
  .attr("height", divHeight)
  .append("g")
  .attr("transform", `translate(${margin.left},${margin.top})`);

//To add tooltip for bar
const tooltip = d3.select("body").append("div").attr("class", "toolTip");

const defs = svg.append("defs");

const marker = defs.append("marker")
  .attr("id", "arrowhead")
  .attr("markerWidth", "10")
  .attr("markerHeight", "7")
  .attr("refX", "0")
  .attr("refY", "3.5")
  .attr("orient", "auto")

const polygon = marker.append("polygon")
  .attr("fill", "gray")
  .attr("points", "0 0, 10 3.5, 0 7")

const xScale = d3.scaleBand()
  .domain(barData.map(d => d.Time))
  .range([0, width + margin.right]);

const xAxis = d3.axisBottom(xScale)

//Adding g attribute to svg for x axis
const yAxisMax = barData.reduce((max, item) => Math.max(max, item.Value), 0) * 1.5;

const yAxisMin = barData.reduce((min, item) => Math.min(min, item.Value), 0) * 1.5;

const yAxisRange = Math.max(yAxisMax, Math.abs(yAxisMin));

const yScale = d3.scaleLinear()
  .domain([-yAxisRange, yAxisRange])
  .range([height, 0]);

const yAxis = d3.axisLeft(yScale).ticks(4);

svg.append('g')
  .call(yAxis);

const bars = svg.selectAll('g.bar')
  .data(barData)
  .enter()
  .append('g')
  .classed('bar', true)
  .attr('transform', d => `translate(${xScale(d.Time) + xScale.bandwidth() / 2}, 0)`);

bars.append('rect')
  .attr('x', -20)
  .attr('width', 40)
  .attr('y', d => Math.min(yScale(d.Value), height/2))
  .attr('height', d => d.Value > 0 ?
(height/2 - yScale(d.Value)) : 
(yScale(d.Value) - height/2)
  )
  .attr('fill', 'blue')
  .on("mousemove", onMouseOver)
  .on("mouseout", onMouseOut);

function onMouseOver(d, i) {
  tooltip
.style("left", d3.event.pageX - 50 + "px")
.style("top", d3.event.pageY - 70 + "px")
.style("display", "inline-block")
.html("Year: " + (d.Time) + "<br>" + "Value: " + (d.Value));
  d3.select(this).attr('fill', "#eec42d");
}

function onMouseOut(d, i) {
  tooltip.style("display", "none");
  d3.select(this).attr('fill', "blue");
}

bars.append('text')
  .text(d => d.Value)
  .attr('text-anchor', 'middle')
  .attr('alignment-baseline', d => d.Value > 0 ? 'baseline' : 'hanging')
  .attr('y', d => yScale(d.Value))
  .attr('dy', d => d.Value > 0 ? -5 : 5);

bars.filter((d, i) => i < barData.length - 1)
  .append('path')
  .attr('d', (d, i) => `M 5,${Math.min(yScale(d.Value) - 20, height/2)} V ${Math.min(yScale(d.Value), yScale(barData[i + 1].Value)) - 60} H ${xScale.bandwidth() - 5} V ${Math.min(yScale(barData[i + 1].Value) - 25, height/2 - 10)}`)
  .style('stroke', 'gray')
  .style('fill', 'none')
  .attr('marker-end', 'url(#arrowhead)')

bars.filter((d, i) => i < barData.length - 1)
  .append('rect')
  .attr('x', 15)
  .attr('y', (d, i) => Math.min(yScale(d.Value), yScale(barData[i + 1].Value)) - 70)
  .attr('width', xScale.bandwidth() - 30)
  .attr('height', 20)
  .attr('rx', 10)
  .style('fill', 'white')
  .style('stroke', 'gray');

bars.filter((d, i) => i < barData.length - 1)
  .append('text')
  .text((d, i) => `${barData[i + 1].Value > d.Value ? '+' : ''}${Math.round((barData[i + 1].Value / d.Value * 100) - 100)}%`)
  .attr('x', xScale.bandwidth() / 2)
  .attr('y', (d, i) => Math.min(yScale(d.Value), yScale(barData[i + 1].Value)) - 56)
  .attr('text-anchor', 'middle')
  .style('fill', 'black');

const xAxisG = svg.append('g')
  .attr("transform", `translate(0,${height/2})`)
  .call(xAxis);
  
xAxisG.selectAll('.tick').each(function() {
  const tick = d3.select(this);
  const text = tick.select('text').text();
  const data = barData.find(d => d.Time === text);
  if (data.Value < 0) {
tick.select('text').style('fill', 'white');
tick.select('line').style('stroke', 'white');
  }
})  
#graph {
  width: 700px;
  height: 500px;
}

text {
  font-size: 12px;
  font-family: "Ubuntu";
}

.toolTip {
  position: absolute;
  display: none;
  min-width: 80px;
  height: auto;
  background: none repeat scroll 0 0 #ffffff;
  border: 1px solid #6F257F;
  padding: 5px;
  text-align: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

<div id="graph">
</div>

【问题讨论】:

  • 我不清楚您要过渡什么以及在什么条件下;你能澄清一下吗?

标签: javascript svg d3.js


【解决方案1】:

这是图表转换和延迟的示例:

const barData = [{
"Time": "2019",
"Value": 5388
  },
  {
"Time": "2020",
"Value": 6453
  },
  {
"Time": "2021",
"Value": -4345
  },
  {
"Time": "2022",
"Value": 7345
  },
  {
"Time": "2023",
"Value": 8345
  },
  {
"Time": "2024",
"Value": 5345
  },
  {
"Time": "2025",
"Value": 6345
  },
  {
"Time": "2026",
"Value": 3345
  }
];

const container = d3.select('#graph');
const divWidth = parseInt(container.style('width'));
const divHeight = parseInt(container.style('height'));

// Consider this width and Height are dynamic for div "graphID" because I am trying to responsive design
const margin = {
  top: 50,
  right: 50,
  bottom: 50,
  left: 50
};
const width = divWidth - margin.left - margin.right;
const height = divHeight - margin.top - margin.bottom;

//To add svg in the visualization node i.e Dome node                    
const svg = container.append("svg")
  .attr("width", divWidth)
  .attr("height", divHeight)
  .append("g")
  .attr("transform", `translate(${margin.left},${margin.top})`);

//To add tooltip for bar
const tooltip = d3.select("body").append("div").attr("class", "toolTip");

const defs = svg.append("defs");

const marker = defs.append("marker")
  .attr("id", "arrowhead")
  .attr("markerWidth", "10")
  .attr("markerHeight", "7")
  .attr("refX", "0")
  .attr("refY", "3.5")
  .attr("orient", "auto")

const polygon = marker.append("polygon")
  .attr("fill", "gray")
  .attr("points", "0 0, 10 3.5, 0 7")

const xScale = d3.scaleBand()
  .domain(barData.map(d => d.Time))
  .range([0, width + margin.right]);

const xAxis = d3.axisBottom(xScale)

//Adding g attribute to svg for x axis
const yAxisMax = barData.reduce((max, item) => Math.max(max, item.Value), 0) * 1.5;

const yAxisMin = barData.reduce((min, item) => Math.min(min, item.Value), 0) * 1.5;

const yAxisRange = Math.max(yAxisMax, Math.abs(yAxisMin));

const yScale = d3.scaleLinear()
  .domain([-yAxisRange, yAxisRange])
  .range([height, 0]);

const yAxis = d3.axisLeft(yScale).ticks(4);

svg.append('g')
  .call(yAxis);

const bars = svg.selectAll('g.bar')
  .data(barData)
  .enter()
  .append('g')
  .classed('bar', true)
  .attr('transform', d => `translate(${xScale(d.Time) + xScale.bandwidth() / 2}, 0)`);

bars.append('rect')
  .attr('x', -20)
  .attr('width', 40)
  .attr('y', height/2)
  .attr('height', 0)
  .attr('fill', 'blue')
  .on("mousemove", onMouseOver)
  .on("mouseout", onMouseOut)
  .transition()
  .delay((d, i) => i * 200)
  .duration(500)
  .attr('y', d => Math.min(yScale(d.Value), height/2))
  .attr('height', d => d.Value > 0 ?
(height/2 - yScale(d.Value)) : 
(yScale(d.Value) - height/2)
  )

function onMouseOver(d, i) {
  tooltip
.style("left", d3.event.pageX - 50 + "px")
.style("top", d3.event.pageY - 70 + "px")
.style("display", "inline-block")
.html("Year: " + (d.Time) + "<br>" + "Value: " + (d.Value));
  d3.select(this).attr('fill', "#eec42d");
}

function onMouseOut(d, i) {
  tooltip.style("display", "none");
  d3.select(this).attr('fill', "blue");
}

bars.append('text')
  .text(d => d.Value)
  .attr('text-anchor', 'middle')
  .attr('alignment-baseline', d => d.Value > 0 ? 'baseline' : 'hanging')
  .attr('y', d => yScale(d.Value))
  .attr('dy', d => d.Value > 0 ? -5 : 5)
  .style('visibility', 'hidden')
  .transition()
  .delay((d, i) => 1000 + i * 200)
  .style('visibility', 'visible')

bars.filter((d, i) => i < barData.length - 1)
  .append('path')
  .attr('d', (d, i) => `M 5,${Math.min(yScale(d.Value) - 20, height/2)} V ${Math.min(yScale(d.Value), yScale(barData[i + 1].Value)) - 60} H ${xScale.bandwidth() - 5} V ${Math.min(yScale(barData[i + 1].Value) - 25, height/2 - 10)}`)
  .style('stroke', 'gray')
  .style('fill', 'none')
  .attr('marker-end', 'url(#arrowhead)')
  .style('visibility', 'hidden')
  .transition()
  .delay((d, i) => 1000 + i * 200)
  .style('visibility', 'visible')

bars.filter((d, i) => i < barData.length - 1)
  .append('rect')
  .attr('x', 15)
  .attr('y', (d, i) => Math.min(yScale(d.Value), yScale(barData[i + 1].Value)) - 70)
  .attr('width', xScale.bandwidth() - 30)
  .attr('height', 20)
  .attr('rx', 10)
  .style('fill', 'white')
  .style('stroke', 'gray')
  .style('visibility', 'hidden')
  .transition()
  .delay((d, i) => 1000 + i * 200)
  .style('visibility', 'visible')

bars.filter((d, i) => i < barData.length - 1)
  .append('text')
  .text((d, i) => `${barData[i + 1].Value > d.Value ? '+' : ''}${Math.round((barData[i + 1].Value / d.Value * 100) - 100)}%`)
  .attr('x', xScale.bandwidth() / 2)
  .attr('y', (d, i) => Math.min(yScale(d.Value), yScale(barData[i + 1].Value)) - 56)
  .attr('text-anchor', 'middle')
  .style('fill', 'black')
  .style('visibility', 'hidden')
  .transition()
  .delay((d, i) => 1000 + i * 200)
  .style('visibility', 'visible')
  

const xAxisG = svg.append('g')
  .attr("transform", `translate(0,${height/2})`)
  .call(xAxis);
  
xAxisG.selectAll('.tick').each(function() {
  const tick = d3.select(this);
  const text = tick.select('text').text();
  const data = barData.find(d => d.Time === text);
  if (data.Value < 0) {
tick.select('text').style('fill', 'white');
tick.select('line').style('stroke', 'white');
  }
}) 
#graph {
  width: 700px;
  height: 500px;
}

text {
  font-size: 12px;
  font-family: "Ubuntu";
}

.toolTip {
  position: absolute;
  display: none;
  min-width: 80px;
  height: auto;
  background: none repeat scroll 0 0 #ffffff;
  border: 1px solid #6F257F;
  padding: 5px;
  text-align: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

<div id="graph">
</div>

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-07
  • 1970-01-01
  • 2014-03-05
  • 2013-02-11
  • 2018-04-20
相关资源
最近更新 更多