首页 > 解决方案 > 在同一元素上单击和拖动事件侦听器。单击事件未触发。+ D3.js 中的数据结构

问题描述

首先,为了让我的数据使用 d3.js 保持活力,我的数据如下 let data = { ... }。使用这些数据,我主要绘制嵌套在 svg "g" 标签中的矩形。(目标是制作平面图编辑器)。“数据”中的每一次更改都会触发我的drawIt()功能......每次发生变化时都要绘制数据(à la React,useState,......)。数据更改是通过移动、拖动、调整这些数据本身创建的 svg 元素的大小来进行的。

您如何看待这种使用 D3.js 进行数据可视化的方式?

接下来,我认为这种处理数据的方式是造成我问题的原因。我有一个具有拖动()行为的元素('rect')以及一个单击事件侦听器。click 事件永远不会被调用,因为拖动事件会拦截 mousedown 事件。我不知道我怎么能让它工作。我注意到如果我注释掉drag().on('end', function()),就会触发click事件。所以我认为这是因为我的 drawIt() 被触发,然后每个元素都被重新创建,等等......实际上我不知道。问题是这个 drawIt() 函数也用于 d3.drag().on('drag', ...),并且单击事件运行良好。

这是我的代码。看看 drawIt() ,在 '.surface 上调用的 d3.drag()

索引.html

<!DOCTYPE html>
<head>
  <meta charset="utf-8">
  <link rel="stylesheet" href="style.css">

  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
  <script src="https://d3js.org/d3-dispatch.v1.min.js"></script>
  <script src="https://d3js.org/d3-selection.v1.min.js"></script>
  <script src="https://d3js.org/d3-drag.v1.min.js"></script>


<script src="https://d3js.org/d3-color.v1.min.js"></script>
<script src="https://d3js.org/d3-dispatch.v1.min.js"></script>
<script src="https://d3js.org/d3-ease.v1.min.js"></script>
<script src="https://d3js.org/d3-interpolate.v1.min.js"></script>
<script src="https://d3js.org/d3-selection.v1.min.js"></script>
<script src="https://d3js.org/d3-timer.v1.min.js"></script>
<script src="https://d3js.org/d3-transition.v1.min.js"></script>

</head>

<body>



<script src="draw.js"></script>
</body>

绘制.js

//Make an SVG Container

let width = screen.width-200
let height = screen.height-300

let rooms = []

var plan = d3.select("body").append("svg")
                                     .attr("width", width)
                                     .attr("height", height)
                                     .attr("transform", "translate(60, 60)")

let background = plan.append("rect")
    .attr("width", width)
    .attr("height", height)
    .attr('fill', "lightblue")   
    .attr('id', "background")

function handleMouseOver() {
    d3.select(this).classed("foo", true);
    d3.select(this).style("cursor", 'all-scroll')
}

function handleMouseOut() {
    d3.select(this).classed("foo", false);
}

function drawIt(data) {
  console.log("drawit")
  plan.selectAll('g').remove()

  let room = 
    plan
      .selectAll('.room')
      .data(data)
      .enter()
      .append('g')

    room
        .attr('id', d => d.id)
        .attr('transform', d => `translate(${d.tx} , ${d.ty})`)

    room
      .append('rect')
        .attr('x', 0)
        .attr('y', -5)
        .attr('width', d => d.w)
        .attr('height', 5)
        .attr('fill', 'grey')
        .attr('class', 'handle n')
    room
      .append('rect')
        .attr('x', 0)
        .attr('y', d => d.h)
        .attr('width', d => d.w)
        .attr('height', 5)
        .attr('fill', 'grey')
        .attr('class', 'handle s')
    room
      .append('rect')
        .attr('x', -5)
        .attr('y', 0)
        .attr('width', 5)
        .attr('height', d => d.h)
        .attr('fill', 'grey')
        .attr('class', 'handle w')
    room
      .append('rect')
        .attr('x', d => d.w)
        .attr('y', 0)
        .attr('width', 5)
        .attr('height', d => d.h)
        .attr('fill', 'grey')
        .attr('class', 'handle e')
    room
      .append('circle')
        .attr('cx', 0)
        .attr('cy', 0)
        .attr('r', 4.8)
        .attr('fill', 'grey')
    room
      .append('circle')
        .attr('cx', d => d.x)
        .attr('cy', d => d.h)
        .attr('r', 4.8)
        .attr('fill', 'grey')
    room
      .append('circle')
        .attr('cx', d=>d.w)
        .attr('cy', d=>d.x)
        .attr('r', 4.8)
        .attr('fill', 'grey')
    room
      .append('circle')
        .attr('cx', d=>d.w)
        .attr('cy', d=>d.h)
        .attr('r', 4.8)
        .attr('fill', 'grey')

    room    
      .append("rect")
        .attr('width', d => d.w)
        .attr('height', d => d.h )
        .attr('fill', 'white')
        .attr('class', 'surface')

    d3.selectAll('.surface')
      .call(d3.drag()
      .on('start', dragStarted)
      .on("drag", dragRoom)
      .on("end", release))
      .on("mouseover", handleMouseOver) 
      .on("mouseout", handleMouseOut) 

    d3.selectAll('.handle')
      .call(d3.drag()
      .on('drag', handling))
      .on("mouseover", handleHovering) 

    d3.selectAll('.surface')
      .on('dblclick', makeSomethingWhenDoubleClicked)


    d3.selectAll(".surface")
      .transition()
      .duration(400)
      .style("fill", "#fcda7c");

    d3.selectAll('.surface').on("click", clicked )
}

function clicked() {
  console.log('click')
}

function dragStarted () {
  console.log('start')
}

function dragRoom (d) {
  console.log('drag')

  d3.select(this).raise().style('cursor', 'grabbing')

  let index = rooms.findIndex(x => x.id === d.id);

  let moveX = d3.event.sourceEvent.movementX
  let moveY = d3.event.sourceEvent.movementY

  if (index >= 0) {
    rooms[index] = { 
      id : rooms[index].id,
      w : rooms[index].w,
      h : rooms[index].h,
      tx : rooms[index].tx +moveX, 
      ty : rooms[index].ty +moveY
    }
  }
  drawIt(rooms)
}

function handling() {
  let roomId = d3.event.subject.id

  d3.select(this)
  .attr("x", parseInt(this.getAttribute('x'), 10) + d3.event.sourceEvent.movementX ) 
  .attr("y", parseInt(this.getAttribute('y'), 10) + d3.event.sourceEvent.movementY )

  let index = rooms.findIndex( (e) => e.id === roomId)

  if (this.getAttribute('class') === 'handle w') {
    rooms[index].tx = rooms[index].tx + d3.event.sourceEvent.movementX
    rooms[index].w = rooms[index].w - d3.event.sourceEvent.movementX
  } else if (this.getAttribute('class') === 'handle e') {
    rooms[index].tx = rooms[index].tx
    rooms[index].w = rooms[index].w + d3.event.sourceEvent.movementX
  } else if (this.getAttribute('class') === 'handle s') {
    rooms[index].ty = rooms[index].ty
    rooms[index].h = rooms[index].h + d3.event.sourceEvent.movementY
  } else if (this.getAttribute('class') === 'handle n') {
    rooms[index].ty = rooms[index].ty + d3.event.sourceEvent.movementY
    rooms[index].h = rooms[index].h - d3.event.sourceEvent.movementY
  } 
  drawIt(rooms)
}

function makeSomethingWhenDoubleClicked(d) {
  let room = d3.event.srcElement
  console.log(room)
  d3.selectAll('.back').classed('selected', false)
  d3.select(room).classed('selected', true)
}

function release(d) {
  console.log("release")
  let index = rooms.findIndex(x => x.id === d.id);

  for (let i = 0; i < rooms.length; i++) {

    if (rooms[index] !== rooms[i]) {
      if (( rooms[index].tx + rooms[index].w) - rooms[i].tx < 20 && ( rooms[index].tx + rooms[index].w) - rooms[i].tx > -20 ) {
        rooms[index].tx = rooms[i].tx - rooms[index].w-5
      }
      if (Math.abs((rooms[index].tx + rooms[index].w) - (rooms[i].tx + rooms[i].w)) < 20) {
        rooms[index].tx = rooms[i].tx + rooms[i].w - rooms[index].w 
      }
      if (rooms[index].tx - rooms[i].tx < 20 && rooms[index].tx - rooms[i].tx > -20) {
        rooms[index].tx = rooms[i].tx
      }  
      if (rooms[index].tx - (rooms[i].tx + rooms[i].w) < 20 && rooms[index].tx - (rooms[i].tx + rooms[i].w) > -20 ) {
        rooms[index].tx = rooms[i].tx + rooms[i].w + 5
      }
      if (Math.abs(rooms[index].ty - rooms[i].ty) < 20) {
        rooms[index].ty = rooms[i].ty
      }
      if (Math.abs(rooms[index].ty - (rooms[i].ty + rooms[i].h)) < 20) {
        rooms[index].ty = rooms[i].ty + rooms[i].h +5
      }
      if (Math.abs((rooms[index].ty + rooms[index].h) - rooms[i].ty) < 20) {
        rooms[index].ty = rooms[i].ty - rooms[index].h -5
      }
      if (Math.abs((rooms[index].ty + rooms[index].h) - (rooms[i].ty + rooms[i].h)) < 20) {
        rooms[index].ty = rooms[i].ty + rooms[i].h - rooms[index].h
      }
     }
  }
  drawIt(rooms)
}

plan.on("dblclick", () => makeRoom())

function makeRoom() {
  console.log(this)
  let myRect = {
    id : Math.random(),
    w : 100,
    h : 100,
    tx : 100,
    ty : 100,
    class : ""
  }

  rooms = [ ...rooms, myRect]

  drawIt(rooms)
}

function handleHovering() {
  if (this.getAttribute("class") === "handle w" || this.getAttribute("class") === "handle e") {
    d3.select(this).style('cursor', 'e-resize')
  } else {
    d3.select(this).style('cursor', 'n-resize')
  }
}


样式.css

body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }

svg { width:100%; height: 100% }

.foo { fill: #98fad8}

.selected {
      fill : #82ff88;
    }

.popup {
    background-color: antiquewhite;
    /*display: none;*/
    position: fixed;
    top: 200px;
    right: 115px;
    border: 3px solid #f1f1f1;
    z-index: 1;
}

标签: d3.jsclickdrag

解决方案


推荐阅读