【问题标题】:D3.js radial barplot with bars as linesD3.js 径向条形图,条形为线
【发布时间】:2020-09-14 12:45:17
【问题描述】:

我想构建一个 D3.js 径向条形图。我见过的所有示例代码都显示了这种图表,其中条形图绘制为“楔形”。是否可以告诉 d3.js 将条形图绘制为直线而不是楔形?

这张图片是我希望条形图出现在图表上的示例

作为一个例子,这里是生成条形图的代码。

const data = [{
    "name": "Burj Khalifa",
    "height": "350"
  },
  {
    "name": "Shanghai Tower",
    "height": "263.34"
  },
  {
    "name": "Abraj Al-Bait Clock Tower",
    "height": "254.04"
  },
  {
    "name": "Ping An Finance Centre",
    "height": "253.20"
  },
  {
    "name": "Lotte World Tower",
    "height": "230.16"
  },
  {
    "name": "Burj Khalifa 2",
    "height": "350"
  },
  {
    "name": "Shanghai Tower 2",
    "height": "263.34"
  },
  {
    "name": "Abraj Al-Bait Clock Tower 2",
    "height": "254.04"
  },
  {
    "name": "Ping An Finance Centre 2",
    "height": "253.20"
  }
]

data.forEach((d) => {
  d.height = Number(d.height)
})

const heightExtent = d3.extent(data, d => d.height)

const bars = data.map(d => d.name)

const containerWidth = 400
const containerHeight = 800

const y = d3.scaleLinear()
  .domain([0, heightExtent[1]])
  .range([0, containerHeight])

const x = d3.scaleBand()
  .domain(bars)
  .range([0, containerWidth])
  .paddingInner(0.02)
  .paddingOuter(0)

const colourScale = d3.scaleOrdinal(d3.schemeBlues[data.length])

const svg = d3.select("#chart-area").append("svg")
  .attr("width", containerWidth)
  .attr("height", containerHeight)

const buildings = svg.selectAll('rect').data(data)

buildings.enter().append('rect')
  .attr('x', (d, i) => x(d.name))
  .attr('y', (d, i) => {
    return containerHeight - y(d.height)
  })
  .attr('width', x.bandwidth())
  .attr('height', (d, i) => y(d.height))
  .attr('fill', (d, i) => colourScale(d.name))

const labels = svg.selectAll('text').data(data)

labels.enter().append('text')
  .attr('text-anchor', 'end')
  .attr('x', (d, i) => (x(d.name) + (x.bandwidth() / 2)))
  .attr('y', (d, i) => containerHeight - 10)
  .attr('writing-mode', 'tb')
  .attr('fill', 'white')
  .text((d, i) => (d.name + ' - ' + d.height + 'm'))
<!-- Bootstrap grid setup -->
<div class="container">
  <div class="row">
    <div id="chart-area"></div>
  </div>
</div>

<!-- External JS libraries -->
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>

【问题讨论】:

    标签: javascript d3.js charts


    【解决方案1】:

    考虑以下几点:

    const data = [{
        "name": "Burj Khalifa",
        "height": "350"
      },
      {
        "name": "Shanghai Tower",
        "height": "263.34"
      },
      {
        "name": "Abraj Al-Bait Clock Tower",
        "height": "254.04"
      },
      {
        "name": "Ping An Finance Centre",
        "height": "253.20"
      },
      {
        "name": "Lotte World Tower",
        "height": "230.16"
      },
      {
        "name": "Burj Khalifa 2",
        "height": "350"
      },
      {
        "name": "Shanghai Tower 2",
        "height": "263.34"
      },
      {
        "name": "Abraj Al-Bait Clock Tower 2",
        "height": "254.04"
      },
      {
        "name": "Ping An Finance Centre 2",
        "height": "253.20"
      }
    ]
    
    data.forEach((d) => {
      d.height = Number(d.height)
    })
    
    const heightExtent = d3.extent(data, d => d.height)
    
    const bars = data.map(d => d.name)
    
    const containerWidth = 700
    const containerHeight = 300
    const barWidth = 16
    const radius = Math.min(containerWidth / 2, containerHeight / 2) - barWidth * 4;
    
    const y = d3.scaleLinear()
      .domain([0, heightExtent[1]])
      .range([0, radius])
    
    const angle = d3.scaleBand()
      .domain(bars)
      .range([0, 360])
      .paddingInner(0.02)
      .paddingOuter(0)
    
    const colourScale = d3.scaleOrdinal(d3.schemeBlues[data.length])
    
    const svg = d3.select("#chart-area").append("svg")
      .attr("width", containerWidth)
      .attr("height", containerHeight)
    
    const buildings = svg.selectAll('rect').data(data)
    
    buildings.enter().append('rect')
      .attr('x', - barWidth / 2)
      .attr('y', barWidth * 2)
      .attr('width', barWidth)
      .attr('height', (d, i) => y(d.height))
      .style('transform', (d, i) =>
        `translate(${containerWidth / 2}px, ${containerHeight / 2}px) rotate(${angle(d.name)}deg)`)
      .attr('fill', (d, i) => colourScale(d.name))
    
    const labels = svg.selectAll('text').data(data)
    
    labels.enter().append('text')
      .attr('x', -barWidth / 2)
      .attr('text-anchor', (d, i) => {
        const degrees = angle(d.name);
        if(10 <= degrees && degrees <= 170) { return 'end'; }
        if(190 <= degrees && degrees <= 350) { return 'start'; }
        return 'middle';
      })
      .style('transform', (d, i) => {
        const xBase = containerWidth / 2;
        const yBase = containerHeight / 2;
        
        const degrees = angle(d.name);
        const radians = (degrees + 90) / 360 * 2 * Math.PI;
        const barHeight = y(d.height) + 2.5 * barWidth;
    
        const yOffset = Math.sin(radians) * barHeight;
        const xOffset = Math.cos(radians) * barHeight;
    
        return `translate(${xBase + xOffset}px, ${yBase + yOffset}px)`;
      })
      .text((d, i) => (d.name + ' - ' + d.height + 'm'))
    <!-- Bootstrap grid setup -->
    <div class="container">
      <div class="row">
        <div id="chart-area"></div>
      </div>
    </div>
    
    <!-- External JS libraries -->
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>

    我已更改 x 比例以返回 0 到 360 之间的数字(旋转度数),然后将所有条形图放在中间,按其度数旋转。唯一的问题是旋转文本很难阅读,旋转文本更难,因为它的宽度是未知的。因此,我展示了如何使用正弦和余弦函数将文本放在每个条的末尾。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多