【问题标题】:Vue D3 - Select newly generated nodes only to stop event propagationVue D3 - 仅选择新生成的节点以停止事件传播
【发布时间】:2020-03-08 20:51:17
【问题描述】:

现在我正在使用自定义 Vue 库组件来生成带有 Pan/Zoom 的力图。节点的点击事件使用 Vue 的 @click 指令,但平移/缩放使用 D3 的 svg call(),所以这里有一个奇怪的 Vue 和 D3 混合。

问题是点击一个节点也触发了平移/缩放功能,这会中断点击事件并导致节点卡在拖动模式。现在,当加载图表时,所有节点都设置为停止事件传播,如下所示:

mounted () {
    /* Panning currently blocks clicks, but this is bypassed by holding
       a key while clicking. */

    const svg = d3.select("svg")
    this.zoom = d3.zoom()
      .scaleExtent([1 / 4, 4])
      .on('zoom', () => {
        d3.select('.zLayer').attr("transform", d3.event.transform)
      })
    const node = d3.selectAll(".node").on("mousedown", () => {        
        d3.event.stopPropagation()
      })
    svg.call(this.zoom)
  }

问题是,我的图表上的节点可以“扩展”,这会将新节点添加到 Vue 数据中,然后导致节点显示在图表上,但没有 d3.event.stopPropagation()。似乎没有一种独特的方式来调用它们,并且尝试选择所有节点并在它们上应用stopPropagation 实际上会为所有节点删除它。

如何让这些新节点停止同时激活平移/缩放,同时保留 Vue @click 功能?

【问题讨论】:

    标签: vue.js d3.js


    【解决方案1】:

    您不需要在 SVG 元素上分配所有事件。我的解决方案是我们可以给 svg 一个 rect 元素作为背景,它可以触发所有与缩放相关的事件。

    这是工作代码:

    var app = new Vue({
        el: "#app",
        data: {
            circles: [{cx: 45, cy: 35, r: 15}]
        },
        methods: {
            onClick: function ($event) {
                console.log($event); 
            }
        },
        mounted: function () {
            var svg = d3.select(this.$refs.svg);
            var layer = svg.select('.zLayer');
            var bgRect = svg.select('.bg');
            var zoom = d3.zoom()
            .scaleExtent([1 / 4, 4])
            .on("zoom", function () {
                // transform still need to work on ".zLayer"
                layer.attr("transform", d3.event.transform);
            });
            
            // Instead of svg I prefer rect to trigger events
            bgRect.call(zoom);
        }
    });
    
    // periodically update circles
    setInterval(function () {
        app.$data.circles.push({
            cx: Math.floor(Math.random() * 500),
            cy: Math.floor(Math.random() * 400),
            r: Math.floor(Math.random() * 20)
        });
    }, 1500);
    svg {
        outline: 1px solid gray;
    }
    circle {
        stroke-width: 1;
        stroke: #333;
        fill: rgba(100,150,100,0.3);
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
    
    
    <div id="app">
        <svg ref="svg" width="500" height="400">
            <!-- 
            <rect> will trigger all zoom events
            Set its width and height to "100%" to make it cover the whole chart
            -->
            <rect class="bg" 
            width="100%" 
            height="100%" 
            fill="transparent"></rect>
            <g class="zLayer">
                <circle
                    v-for="c in circles"
                    :cx="c.cx" :cy="c.cy" :r="c.r"
                    @click="onClick(c)"
                ></circle>
            </g>
        </svg>
    </div>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多