【问题标题】:How to add a text alongside each SVG element iteratively如何迭代地在每个 SVG 元素旁边添加文本
【发布时间】:2020-10-25 20:41:18
【问题描述】:

我是 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

【问题讨论】:

    标签: javascript html vue.js svg


    【解决方案1】:

    所以如果我从标签中正确理解,你使用的是 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>
    

    【讨论】:

    • 谢谢!正确 - svg 存储在数组 this.svgs 中。我仍然无法在同一对应行上获取相应的文本标签和 svg 元素。目前,文本标签在每行输出,后跟所有 svg 元素。我用我根据您建议的解决方案更新的template 语法更新了我上面的问题。关于如何最好地进行的任何建议?
    • 我更新了我的答案,你试过了吗?我第一次没有正确理解它,我以为你将标签和 svgs 放在同一个数组中。
    猜你喜欢
    • 2021-02-05
    • 1970-01-01
    • 2020-08-06
    • 2014-05-07
    • 2020-02-28
    • 2020-02-06
    • 2022-10-23
    • 2022-08-18
    • 1970-01-01
    相关资源
    最近更新 更多