dom - 在不直接操作 DOM 的情况下绘制 d3 轴

标签 dom d3.js svg vue.js vuejs2

有没有办法在不直接操作 DOM 的情况下使用 d3 轴? D3.js 在用于辅助函数(尤其是 d3-scale )时很好地补充了 Vue.js。

我目前正在使用一个简单的 Vue 模板来生成 SVG。 要生成轴,我首先创建一个 <g ref="axisX">元素,然后调用 d3.select(this.$refs.axisX).call(d3.axisBottom(this.scale.x)) .

我想避免使用 d3.select以防止直接修改 DOM。它运行良好,但与 Vue.js 的作用域样式冲突。

有没有办法访问d3-axis不从 DOM 元素调用它?访问它的 path 会很有用独立生成函数,而不是通过 DOM。

这是一个示例 CodePen:https://codepen.io/thibautg/pen/BYRBXW

最佳答案

这种情况需要 custom directive .自定义指令允许您在它们所附加的元素内操作 DOM。

在这种情况下,我创建了一个指令,它接受一个轴的参数和一个计算比例的值。根据轴是 x 还是 y,它使用 scale[axis] 调用 axisBottomaxisLeft

不看了。该指令将在任何更新时调用。如果需要,您可以进行检查以查看 scale 是否特别改变了其先前的值。

new Vue({
  el: "#app",
  data() {
    return {
      width: 600,
      height: 400,
      margin: {
        top: 20,
        right: 20,
        bottom: 20,
        left: 20
      },
      items: [
        { name: "a", val: 10 },
        { name: "b", val: 8 },
        { name: "c", val: 1 },
        { name: "d", val: 5 },
        { name: "e", val: 6 },
        { name: "f", val: 3 }
      ]
    };
  },
  computed: {
    outsideWidth() {
      return this.width + this.margin.left + this.margin.right;
    },
    outsideHeight() {
      return this.height + this.margin.top + this.margin.bottom;
    },
    scale() {
      const x = d3
        .scaleBand()
        .domain(this.items.map(x => x.name))
        .rangeRound([0, this.width])
        .padding(0.15);
      const y = d3
        .scaleLinear()
        .domain([0, Math.max(...this.items.map(x => x.val))])
        .rangeRound([this.height, 0]);
      return { x, y };
    }
  },
  directives: {
    axis(el, binding) {
      const axis = binding.arg;
      const axisMethod = { x: "axisBottom", y: "axisLeft" }[axis];
      const methodArg = binding.value[axis];

      d3.select(el).call(d3[axisMethod](methodArg));
    }
  }
});
rect.bar {
  fill: steelblue;
}
<script src="//unpkg.com/vue@2"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/4.11.0/d3.min.js"></script>
<div id="app">
  <svg :width="outsideWidth"
       :height="outsideHeight">
    <g :transform="`translate(${margin.left},${margin.top})`">
      <g class="bars">
        <template v-for="item in items">
          <rect class="bar"
                :x="scale.x(item.name)"
                :y="scale.y(item.val)"
                :width="scale.x.bandwidth()"
                :height="height - scale.y(item.val)"
                />
        </template>
        <g v-axis:x="scale" :transform="`translate(0,${height})`"></g>
        <g v-axis:y="scale"></g>
      </g>
    </g>
  </svg>
</div>

关于dom - 在不直接操作 DOM 的情况下绘制 d3 轴,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48726636/

相关文章:

javascript - 如何通过 Javascript 对象(如 HTMLElement)进行枚举?

javascript - querySelector 与 getElementById

python - 我只想要 XML 元素的直接子元素,而不是所有后代

javascript - 如何通过 Google CAJA 传递库?

javascript - 带有 viewBox 和宽度的 SVG 在 IE 中无法正确缩放高度

powershell - Powershell 中 getElementById 和 getElementsByName 之间的差异

javascript - D3.js 维恩图文本标签

javascript - 在 D3 可缩放森伯斯特中,如何根据缩放级别制作标签?

javascript - Web Audio API 循环与 SVG 动画同步

javascript - 旋转图像的 SVG 低性能