डी 3 क्लिक घटना फिर से लागू होने के बाद काम नहीं करती है

Jan 03 2021

मैं 3 डी का उपयोग करते हुए माइक बेसिकॉक से इस ड्रैगबल ग्लोब को फिर से बनाने की कोशिश कर रहा हूं, लेकिन एक svg संस्करण के रूप में। प्रदर्शन के मुद्दों के कारण मैं दुनिया को फिर से प्रस्तुत कर रहा हूं। अब तक सब ठीक है। अब मैं एक क्लिक ईवेंट लागू करना चाहता हूं लेकिन यह काम नहीं करता है। यहाँ यह उल्लेख किया गया है कि समस्या फिर से लागू हो सकती है। एक मूसडाउन घटना ठीक काम कर रही है, लेकिन बाद में खींचने के साथ हस्तक्षेप करती है। मूसडाउन इवेंट क्यों काम कर रहा है और क्लिक इवेंट नहीं है? यह हल करने के लिए कोड के पुनर्गठन के लिए संकेत अत्यधिक सराहना की जाती है।

मैंने बेहतर समझ के लिए एक फिडेल बनाया: फिडल

पुनश्च मैं प्रोग्रामिंग और डी 3 के लिए नया हूं, इसलिए कृपया बहुत कठोर न हों :)

HTML:

<!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>

जेएस:

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)
}

जवाब

1 AndrewReid Jan 03 2021 at 08:51

आप कभी भी क्लिक को ट्रिगर नहीं करेंगे, जैसा कि आपने अभी देखा है। ऐसा इसलिए है क्योंकि एक क्लिक में माउस और माउस दोनों शामिल होते हैं। ये ड्रैग बिहेवियर के साथ इंटरैक्ट करते हैं, प्रत्येक के साथ क्रमशः स्टार्ट और एंड इवेंट शुरू होते हैं।

आपके मामले में जो हो रहा है वह मूसडाउन ड्रैग ट्रिगर है और मूसडाउन श्रोता जिन्हें आपने रास्तों में जोड़ा है। फिर माउस अप पर, ड्रैग इवेंट श्रोता सबसे पहले फायर करता है, किसी भी पथ और संबंधित श्रोताओं को हटाता है। रेंडर फ़ंक्शन इस तथ्य को दर्ज करने के बाद नए पथ जोड़ता है, घटना को पंजीकृत करने के लिए बहुत देर हो चुकी है।

समाधान के एक नंबर हैं, लेकिन शायद सबसे आसान आपके पास ड्रैग एंड श्रोता को निकालना है और इसे तब ही बदलना है जब वास्तव में एक ड्रैग होता है (न कि स्टार्ट इवेंट में, ड्रैग इवेंट में):

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()विधि श्रोताओं केवल वर्तमान भाव के लिए आवेदन किया जा सकता है। ड्रैग श्रोता केवल तभी ट्रिगर करता है जब मूसडाउन और माउसअप के बीच एक मूसमव होता है, इसलिए केवल साधारण क्लिक होने पर इस श्रोता का उपयोग नहीं किया जाएगा।

यहाँ एक कांटा भरा हुआ है ।

इस समस्या के लिए वैकल्पिक समाधानों की एक उचित संख्या है, यहां एक, जबकि सरल, क्लिक के दौरान आंदोलन के लिए थोड़ा संवेदनशील हो सकता है। यहाँ माउसअप संबंधित घटनाओं (क्लिक और ड्रैग एंड) को विभेदित करने के लिए एक वैकल्पिक दृष्टिकोण के लिए एक संभव आधार है।