首页 > 解决方案 > 如何迭代地在每个 SVG 元素旁边添加文本

问题描述

我是 JavaScript 新手,目前正在尝试在 Vue Javascript 框架上构建一些 d3 可视化。我的问题是我已经使用图形创建了这些迭代堆叠的水平条形图svg,并且我想添加与每个条形图或视觉对象相对应的标题或标签。我希望将label文本放置在视觉左侧(如下图所示)或在每个视觉上方单独一行。我目前只能通过将文本附加到svg元素来迭代地在每个栏的顶部添加标签,但这并不理想,因为它与视觉重叠。

在此处输入图像描述

寻求帮助以最好地将这些标签放置在视觉效果旁边(左侧或上方)

我当前负责生成 SVG 元素的方法和循环如下所示:

async genViz() {
      const rawDataAllTime = await d3.csv(this.dataUrlAllTime); // import raw data currently selected
      this.allData = this.processData(rawDataAllTime);
      const rawDataNow = await d3.csv(this.dataUrlNow);
      console.log("rawDataNow", rawDataNow);
      this.nowData = this.processData(rawDataNow);

      const height = 33;
      const width = 800;
      const color_interp = d3
        .scaleLinear()
        .domain([0, 1 / 3.0, 2 / 3.0, 1])
        .range([
          colors.accent,
          colors.secondary_green,
          colors.primary_teal,
          colors.primary_dark,
        ])
        .interpolate(d3.interpolateHcl);

      const x = d3.scaleLinear([0, 1], [0, width]);
      this.x = x;

      const formatPercent = x.tickFormat(null, "%");
      this.formatPercent = formatPercent;

      this.svgs = [];

      // loops through all stacks and generates d3 visualizations

      this.allData.forEach((stack) => {
        // each chart is a separate svg
        const svg = d3
          .create("svg")
          .attr("viewBox", [0, 0, width, height])
          .style("display", "block");
        this.svgs.push(svg);

        let color = d3
          .scaleOrdinal()
          .domain(stack.map((d) => d.reason))
          .range(d3.quantize((t) => color_interp(t), stack.length));
        this.color = color;
        svg
          .append("g")
          .attr("stroke", "white")
          .selectAll("rect")
          .data(stack)
          .join("rect")
          .attr("fill", (d) => color(d.reason))
          .attr("x", (d) => x(d.startValue))
          .attr("y", 0)
          .attr("width", (d) => x(d.endValue) - x(d.startValue))
          .attr("height", height)
        .append("title")
        .text((d) => d.type)
        .attr("text-anchor","middle");

        svg
          .append("g")
          .attr("font-family", "sans-serif")
          .attr("font-size", 12)
          .selectAll("text")
          .data(stack)
          .join("text")
          .attr("fill", (d) =>
            d3.lab(color(d.reason)).l < 50 ? "white" : "black"
          )
          .attr("transform", (d) => `translate(${x(d.startValue) + 6}, 6)`)
          .call((text) =>
            text
              .append("tspan")
              .attr("class", "reason")
              .attr("y", "0.7em")
              .attr("font-weight", "bold")
              .text((d) => d.reason)
          )
          .call((text) =>
            text
              .append("tspan")
              .attr("class", "value")
              .attr("x", 10)
              .attr("y", "1.7em")
              .attr("dx", 0)
              .attr("fill-opacity", 0.7)
              .text((d) => formatPercent(d.value))
          )
        svg
            .append("g")
            .attr("font-family", "sans-serif")
            .attr("font-size", 12)
            .selectAll("text")
            .data(stack)
            .join("text")
            .call((text) =>
                text
                  .append("tspan")
                  .attr("x", 0)
                  .attr("y", "1.5em")
                  .attr("font-weight", "bold")
                  .text("LABEL")
                  .attr("fill","#f30303"))

        return svg.node();
      });

      return this.svgs.map((svg) => svg.node());
    }

编辑:能够遍历文本标签并将它们显示在 svg 元素上方。但是,我仍然无法强制文本标签与其对应的 svg 元素位于同一行。这就是我当前的template语法:

<template>
  <div class="stacked-bar">
    <div class="row">
      <div class="col-xs-12"><ModeToggle @toggled="toggleMode" /></div>
    </div>
    <div ref="d3">
      <div v-for="ppe in ppe_types" v-bind:key="ppe">
        <span>
<!--           text labels-->
          {{ppe[0]}}
        </span>
      </div>
      </div>


  </div>
</template>

<script>
// Vue.js main code starts here

标签: javascripthtmlvue.jssvg

解决方案


因此,如果我从标签中正确理解,您正在使用 vue.js,而您的这个函数正在返回一个包含这些 svgs 的数组?我想你用 v-for 显示这些。所以我的解决方案是这样的:

<div v-for="(svg, i) in svgs" :key="i">
   <span>Label Text</span>
   <div>{{svg}}</div>
</div>

并添加一些 css 样式以强制它们成为一行。

编辑:从您更新的代码中,我收集到您有 2 个不同的数组,我以为您在一个数组中有展位,好的,这很容易解决,如果订购了它们,您可以像计算变量一样编写或返回它们。或者只是这样做(如果它们的长度相同并且订购相同)。

<div v-for="(svg, i) in svgArray" :key="i">
   <span>{{labelArray[i]}}</span>
   <span>{{svg}}</span>
</div>

或者

<div v-for="(label, i) in labelArray" :key="i">
   <span>{{label}}</span>
   <span>{{svgArray[i]}}</span>
</div>

推荐阅读