d3.js - 在同一元素上单击和拖动事件侦听器。单击事件未触发。+ 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;
}
解决方案
推荐阅读
- wordpress - W3总缓存WordPress插件启用后仍然查询数据库
- google-bigquery - 将行从一个表过滤到另一个表
- sql-server - 如何用分号分隔 nvarchar 列
- javascript - HTML Canvas - 创建荧光笔效果
- java - 使用带有休眠 3 的 c3p0 连接池
- elasticsearch - elasticsearch中的span_containing和span_within查询有什么区别?
- angularjs - Angular $destroy 正在堆叠
- r - 将 tableGrob 中的 colhead 向左对齐,而不剪切 colnames
- angular - 如果用户刷新/重新加载,请保留表单数据
- android-studio - 在指定位置找不到 Flutter SDK - 在 Android Studio 中