首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >重新追加路径后D3单击事件不起作用

重新追加路径后D3单击事件不起作用
EN

Stack Overflow用户
提问于 2021-01-03 00:09:07
回答 1查看 54关注 0票数 2

我正在尝试使用D3从Mike Bostock重新创建this可拖动地球,但作为svg版本。由于拖动时的性能问题,我正在重新渲染地球。到目前一切尚好。现在,我想实现一个单击事件,但它不起作用。Here提到问题可能是重新添加。mousedown事件工作正常,但会干扰稍后的拖动。为什么mousedown事件起作用而click事件不起作用?对于重构代码以解决此问题的提示,我们非常感谢。

为了更好地理解,我创建了一个提琴:Fiddle

PS。我是编程和D3的新手,所以请不要太苛刻:)

HTML:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>D3</title>
</head>
<body>

<div id="world"></div>

<script src="https://d3js.org/d3.v6.min.js"></script>
<script src="https://unpkg.com/topojson-client@2"></script>

</body>
</html>

JS:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let width, height
height = 150
width = 150

const projection = d3.geoOrthographic()
    .scale((height - 10) / 2)
    .translate([100, height / 2])
    .precision(0);
let path = d3.geoPath().projection(projection)

const svg = d3.select("#world")
    .append("svg")

const g = svg.append("g")

d3.json("https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json").then(data =>  {
  let data1 = data
  renderGlobe(data1);
})


function renderGlobe(world){
  g.call(drag(projection)
   .on("drag.render",  ()=>render(world, true))   
   .on("end.render",  ()=>render(world, false)  ))
   .call( () => render(world, false))
}


function render(world, x){
  if(x){
    variable = "land"
     world = topojson.feature(world, world.objects.land).features;

  }
  else{
    variable = "countries"
    world = topojson.feature(world, world.objects.countries).features;
  }
  g.selectAll("path").remove()

  g.selectAll(`${variable}`)
   .data(world)
   .enter().append("path")
   .attr("class", `${variable}`)
   .attr("d", path)
   // This click event doesn't work
   .on("click",()=>console.log("Do something"))
   // But mousedown event works
   .on("mousedown",()=>console.log("Mousedown event works"))
        
}


function drag(projection){
   var LonLatStart, eulerStart
   function dragstarted(event){
      LonLatStart = projection.invert(d3.pointer(event))
  
      eulerStart = projection.rotate()
}

var LonLatEnd, eulerEnd
function dragged(event){
  LonLatEnd = projection.rotate(eulerStart).invert(d3.pointer(event))
  eulerEnd = getEulerAngles(LonLatStart, eulerStart, LonLatEnd)
 
  projection.rotate(eulerEnd)
  refresh()
}
return drag = d3.drag()
  .on("start", dragstarted)
  .on("drag", dragged)
}

function refresh(){
  svg.selectAll("path").attr("d", path)
}

// Dragging Math

  let cos = Math.cos,
  acos = Math.acos,
  sin = Math.sin,
  asin = Math.asin,
  atan2 = Math.atan2,
  sqrt = Math.sqrt,
  min = Math.min,
  max = Math.max,
  PI = Math.PI,
  radians = PI / 180,
  degrees = 180 / PI;

// a: original vector, b: ending vector
function crossProduct(a, b){
  return [
    a[1] * b[2] - a[2] * b[1],
    a[2] * b[0] - a[0] * b[2], 
    a[0] * b[1] - a[1] * b[0]
  ]
}

function dotProduct(a, b){
    return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]    
}

function LengthOfVector(c){
    return sqrt(c[0] * c[0] + c[1] * c[1] + c[2] * c[2])
}

function quaternionEulerFormula(a, b){ 
    let rotationAxis = crossProduct(a,b) , normalizationFactor = sqrt(dotProduct(rotationAxis,rotationAxis))
if (!normalizationFactor) return [1, 0, 0, 0]
    let theta = acos(max(-1, min(1, dotProduct(a, b)))) 
return [
  cos(theta / 2), 
  sin(theta / 2) * rotationAxis[2] / normalizationFactor,
  - sin(theta / 2) * rotationAxis[1] / normalizationFactor,       
  sin(theta / 2) * rotationAxis[0] / normalizationFactor
]
}   

// returns unit quaternion from euler angles [λ, φ, γ]
function unitQuaternion(d){
var lambda = d[0] / 2 * radians, cosLambda = cos(lambda), sinLambda = sin(lambda),
  phi = d[1] / 2 * radians, cosPhi = cos(phi), sinPhi = sin(phi),
  gamma = d[2] / 2 * radians, cosGamma = cos(gamma), sinGamma = sin(gamma)

return [
  cosLambda * cosPhi * cosGamma + sinLambda * sinPhi * sinGamma,
  sinLambda * cosPhi * cosGamma - cosLambda * sinPhi * sinGamma,
  cosLambda * sinPhi * cosGamma + sinLambda * cosPhi * sinGamma,
  cosLambda * cosPhi * sinGamma - sinLambda * sinPhi * cosGamma,
]
}

// quaternion multiplication, returns another quaternion which represents the rotation
function quaternionMultiplication(q0 , q1){
return [
q0[0] * q1[0] - q0[1] * q1[1] - q0[2] * q1[2] - q0[3] * q1[3],
q0[0] * q1[1] + q0[1] * q1[0] + q0[2] * q1[3] - q0[3] * q1[2],
q0[0] * q1[2] - q0[1] * q1[3] + q0[2] * q1[0] + q0[3] * q1[1],
q0[0] * q1[3] + q0[1] * q1[2] - q0[2] * q1[1] + q0[3] * q1[0]
]
}

// converts quaternion to euler angles
function quaternion2eulerAngles(q){
return [
  atan2(2 * (q[0] * q[1] + q[2] * q[3]), 1 - 2 * (q[1] * q[1] + q[2] * q[2])) * degrees,
  //asin(2 * (q[0] * q[2] - q[3] * q[1])) * degrees,
  asin(max(-1, min(1, 2 * (q[0] * q[2] - q[3] * q[1])))) * degrees,
  atan2(2 * (q[0] * q[3] + q[1] * q[2]), 1 - 2 * (q[2] * q[2] + q[3] * q[3])) * degrees
]
}


// converts long, lat to cartesian coordinates x,y,z
function lonlat2cartesian(e) {
let l = e[0] * radians, p = e[1] * radians, cp = cos(p);
return [cp * cos(l), cp * sin(l), sin(p)];
};

function getEulerAngles(positionLonLatStart, eulerAnglesStart, positionLonLatEnd){
let v0 = lonlat2cartesian(positionLonLatStart)
let v1 = lonlat2cartesian(positionLonLatEnd)

let quaternionEnd = quaternionMultiplication(unitQuaternion(eulerAnglesStart), quaternionEulerFormula(v0,v1))
return quaternion2eulerAngles(quaternionEnd)
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-01-03 01:51:19

你永远不会像现在这样触发点击,你已经看到了。这是因为单击既涉及鼠标按下又涉及鼠标释放。它们与拖动行为交互,每个都分别触发开始和结束事件。

在您的例子中,mousedown触发了您添加到路径中的拖动和mousedown侦听器。然后在鼠标释放时,首先触发拖动事件侦听器,删除所有路径和关联的侦听器。render函数会在事件发生后添加新的路径,但是太晚了,无法注册该事件。

有许多解决方案,但可能最简单的方法是删除已有的拖动结束侦听器,并仅在实际发生拖动时才替换它(不是在start事件中,而是在drag事件中):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function renderGlobe(world){
   g.call(drag(projection)
     .on("drag.render",  function(event) {
        render(world, true) 
       event.on("end.render",()=>render(world,false))
   }))
  .call( () => render(world, false))
}

event.on()方法允许侦听器仅应用于当前手势。只有在mousedown和mouseup之间有鼠标移动时,拖动监听器才会触发,所以如果只有一次简单的单击,则不会使用这个end监听器。

这是一个分叉的fiddle

对于这个问题,有相当多的替代解决方案,这里的一个,虽然简单,但可能对点击过程中的移动有点敏感。Here是区分鼠标相关事件(单击和拖动结束)的另一种方法的可能基础。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65545439

复制
相关文章
如何获取ssl证书
在前面的文章中,我们分析了SSL/TLS的一些基本概念和为什么他们安全,尤其提到了公钥和私钥的概念,还有一个很重要的文件,就是CA证书,关于CA证书的官方解释,可以参考百科的解释,这里我们可以简单的认为,CA证书是一个网站的 二维码,这个二维码包括了服务器的一些信息,比如服务器所在的组织、支持的加密算法,还有更重要的公钥信息。
用户8418197
2021/12/08
3.8K0
超方便获取免费ssl证书
有的时候懒,去腾讯云申请证书有些麻烦,用这个方法只需要点一下即可获取3个月的免费ssl证书
MGS浪疯
2022/05/03
1.6K0
docker获取Let's Encrypt永久免费SSL证书
还不如野蛮生长的acme.sh,而这里介绍docker运行cerbot获取Let's Encrypt永久免费SSL证书
ydymz
2021/01/05
6.6K0
Python 获取 NCBI 基因名 SSL 证书异常?
即想要通过 Python 在线获取某个转录本对应的基因 symbol 时,发现出现 SSL 无法获取本地证书:unable to get local issuer certificate (_ssl.c:1056)!
章鱼猫先生
2021/10/15
8760
Python 获取 NCBI 基因名 SSL 证书异常?
apache安装ssl证书_apache ssl证书配置
生成三个证书 server.crt 、server-ca.crt、server.key
全栈程序员站长
2022/09/27
8.9K0
ssl证书怎么申请?网站ssl证书申请
随着搜索引擎巨头百度、谷歌等大力倡导网站https加密访问,以及加强互联网信息安全建设的需要,越来越多的网站、系统开始实现https加密。网站实现https加密需要用到SSL证书,那么网站SSL证书如
沃通WoTrus数字证书
2023/02/27
16.1K0
ssl证书怎么申请?网站ssl证书申请
国密SSL证书与国际SSL证书的区别
随着数字安全的重要性日益凸显,SSL证书在保护数据传输和网站安全方面发挥着关键作用。国密SSL证书和国际SSL证书是两种不同类型的证书,本文将探讨这两者之间的区别,帮助您了解在不同情境下应该选择哪种类型的SSL证书。
涂样丶
2023/10/25
1.3K0
国密SSL证书与国际SSL证书的区别
SSL 证书
SSL证书的运用促使网址更加安全性,做为一种加密传输协议书技术性。SSL的挥手协议书让顾客和集群服务器进行彼此之间的身份验证。为了让各位能进一步了解ssl证书,小编来向各位介绍SSL证书原理。
用户8418197
2021/12/26
7.3K0
SSL证书的作用
SSL证书是一种数字证书,由权威认证的第三方机构颁发,用于验证网站的真实性、可信赖性以及安全性。它是在互联网传输中保护用户信息安全的协议,是安全套接字层(SSL)的简写。
网盾JoySSL小张
2023/09/27
4650
通配符SSL证书与多域名SSL证书区别
您仍然不确定哪个多域名SSL证书适合您的网站吗?因此,在这里我们详细提出了通配符SSL与多域名SSL证书之间的区别,这很容易理解。因此,找出哪种多域证书和通配符更适合您的需求。
Gworg
2022/03/03
8.8K0
通配符SSL证书与多域名SSL证书区别
gitlab-ssl证书过期,以及更换ssl证书
# cat /etc/gitlab/gitlab.rb |grep test.com
用户8851537
2021/07/30
9.1K0
SSL证书是什么?SSL证书怎么申请?
  SSL证书是数字证书的一种,由权威数字证书机构(CA)验证网站身份后颁发,可实现浏览器和网站服务器数据传输加密。网站安装SSL证书后会在浏览器显示安全锁标志,数据传输协议从http(传统协议) 升级为 https(加密协议)。
安信SSL证书
2019/08/15
8.9K0
SSL证书是什么?SSL证书怎么申请?
nginx ssl证书生成_docker ssl证书
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
全栈程序员站长
2022/09/30
6.4K0
免费SSL证书
Let’s Encrypt是由EFF、Mozilla、Cisco、Akamai、IdenTrust与密西根大学研究人员共同创立的,持续可靠的免费SSL证书,几乎兼容所有浏览器。
用户4432529
2019/01/15
11.6K0
SSL IP证书
SSL证书通常颁发给域名,但是很多组织机构需要公共IP地址的SSL证书,我们将部署在IP上的SSL证书类型叫做IP SSL证书,IP SSL证书安全、可靠、有着极强的兼容性,证书分别在主题(DN)或主题备用名称(SAN)字段中提供对IP地址的支持。
柳絮云泡泡
2023/02/03
4.8K0
SSL证书的分类有哪些?如何选择合适的SSL证书?
为了保护网站数据和用户信息,很多企业开始为网站安装SSL证书,有效提升了网站安全性。那么SSL证书的分类有哪些?如何选择合适的SSL证书?
稀糊牛肉粥
2023/09/25
6140
SSL证书介绍
上文提到的都是单向非对称加密,对于安全性很高的场景(比如网银), 不仅客户端要验证服务端的合法性,服务端也要验证每个访问的客户端的合法性,对于这种场景,往往给每个用户发一个U盘,里面装的就是客户端的公钥和私钥对,用于双向非对称加密。
柳絮云泡泡
2023/02/01
3.8K0
SSL证书介绍
EV证书:级别最高的SSL证书
在当今数字化时代,保护网站和用户数据的安全应该是网站最重要的功能。EV(Extended Validation)证书是一种高级别的SSL证书,提供了更强的身份验证和在线安全保障。
稀糊牛肉粥
2023/10/18
4690
关于SSL证书
注意: 本栏只叙述如何续费免费的 Let's Encrypt 及 TrustAsia 证书。
axiomxs
2021/11/26
3.4K0
SSL证书申请
对于很多企业而言,使用SSL证书加密网站已经显得尤为重要,这不仅仅是关乎企业的网站安全,同时也关系着企业的形象以及用户的信赖。既然使用https协议已经众多企业认可,那么我们该如何给自己的网站申请以及安装SSL证书?
JoySSL
2023/03/08
6.2K0
SSL证书申请

相似问题

Spring WebFlux Mono的异常中断Mono.zip

10

Spring webflux中的flatMap()与subscribe()

12

未抛出spring webflux的自定义异常

20

Spring Security Webflux/反应式异常处理

33

Spring Webflux:根据抛出的异常返回statuscode和message

149
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文