【问题标题】:Repositioning SVG:rect elements created using d3.js (Using either d3 or jQuery)重新定位 SVG:使用 d3.js 创建的矩形元素(使用 d3 或 jQuery)
【发布时间】:2014-03-14 13:57:14
【问题描述】:

情况:

我正在制作带有 d3 叠加层的谷歌地图。它主要基于 Mike Bostock 的这个例子:http://bl.ocks.org/mbostock/899711

我在 Rails 4 应用程序中运行它,并从 CDN 加载到 d3.js 库中。因此可以访问 d3.js 和 jQuery 库,并且希望避免添加更多库。

任务:

数据点(由svg:rect 元素表示)有时会掩盖谷歌地图上的地名。我想要它,以便用户可以将光标移到这些数据点上,并且它们会立即弹出。

我应该注意我正在尝试让点随机重新定位,所以不能真正走悬停 CSS 路线。

所以...

  • 在 SVG 上放置一个事件监听器(或 rect,具体取决于实际操作的元素)

  • 编写一个回调函数来重新定位(偏移?动画?过渡?)触发事件的元素

  • 等待 setTimeout(或使用 d3 .each("end",function()))然后将元素返回到它的先前位置

简单吧?

尝试:

所以,尝试使用 d3,然后尝试使用 jQuery。在 jQuery 上保释,因为它似乎无法处理 SVG(尽管如果 d3.js 没有随货提供,我可能不得不求助于使用 jQuery-SVG 库)

使用 d3 尝试,事件处理程序分配很好。我已将处理程序分配给 svg 父元素,而不是 rect 子元素。

.on('mouseover', scatter)

这会产生事件触发的预期行为以及当我将鼠标悬停时调用的分散函数。

但是,我无法对我正在寻找的行为进行任何合理的表达。

我已尝试更改 x,y 值。我试过改变顶部和左侧的值。对象上的值似乎发生了变化,但没有任何变化。我已经尝试过使用直接 d3 样式选择和 d3 样式转换的那些。

我在 rect 子元素上尝试了相同的更改。这会移动 rect 元素,但不会同时带上 SVG,因此不会显示。

相关代码:

将 SVG 附加到每个数据点,为 SVG 设置正确的 x 和 y,并为每个数据点添加一个事件侦听器。

var marker = layer.selectAll("svg")
      .data(data)
      .each(transformMarker)
      .enter().append("svg:svg")
      .each(transformMarker)
      .on('mouseover', scatter);

另外,我设置了 SVG 父元素的样式以包含一个 rect 子元素,并设置了 rect 的样式,因此屏幕上实际显示了一些内容。

transformMarker 函数,您可能需要了解标记的定位方式。

    function transformMarker(d) {
      d = new google.maps.LatLng(J.lat(d), J.lon(d));
      d = projection.fromLatLngToDivPixel(d);
      return d3.select(this)
          .style("left", (d.x) + "px")
          .style("top", (d.y) + "px");

分散函数(带有大量控制台日志,以便您查看发生了什么)

function scatter(d){
      console.log("the event has fired")
      d = new google.maps.LatLng(J.lat(d), J.lon(d));
      d = projection.fromLatLngToDivPixel(d);
      console.log(d.x)
      console.log((d.x + 15) + "px")
      console.log(this)
      d3.select(this)
        .attr("x", (d.x + 15))
        .attr("y", (d.y + 15))
      console.log(this)

以及 console.logs 输出的内容

the event has fired
602.0032221842557
617.0032221842557px

<svg style=​"left:​ 602.0032221842557px;​ top:​ 230.00228557549417px;​" 
    x="617.0032221842557" y=​"245.00228557549417">​
    <rect height=​"7" width=​"7" fill=​"#138770" stroke=​"#0f0f02" stroke-width=​"0.5">​</rect>​
</svg>​

<svg style=​"left:​ 602.0032221842557px;​ top:​ 230.00228557549417px;​"
    x="617.0032221842557" y=​"245.00228557549417">​
    <rect height=​"7" width=​"7" fill=​"#138770" stroke=​"#0f0f02" stroke-width=​"0.5">​</rect>​
</svg>​

还有 SVG 的 CSS,因为这可能会在工作中造成麻烦

.overlay, .overlay svg {
  position: absolute;
}

.wrapper, .overlay svg {
  /*border: 1px solid black;*/
  width: 7px;
  height: 7px;

  transform: translateZ(0px);
  -webkit-transform: translateZ(0px);
  -moz-transform: translateZ(0px);
}

我离文明还有几天的路程,所以希望把这个问题摆在那里,然后用一种新的方法回来,并从 Stack Overflow 和 d3 google 小组中提出一些建议。

如果您有任何其他问题或需要更多信息,请告诉我。

【问题讨论】:

    标签: jquery svg d3.js event-handling mouseover


    【解决方案1】:

    我会看看这个漂亮的random function for Sass

    module Sass::Script::Functions
      def random(max = Sass::Script::Number.new(100))
        Sass::Script::Number.new(rand(max.value), max.numerator_units, max.denominator_units)
      end
    end
    

    要将随机函数添加到您的 sass 中,请查看 this guide。如果您希望您的项目在 mousever 上随机重新定位,我将使用 random 函数创建一个具有随机位置的类,然后将该类删除/添加到元素中。当然,我提供的链接中有一些使用随机函数的示例。

    当然很简单,只需将上面的代码添加到 sass.rb 中的 config/initializers 即可。这是一个使用它的example rails app

    它的一个问题是,如果你在一个类中设置它,它只会生成一次随机数。在刷新和重新加载时,它不会为它生成的 css 生成新的随机顶部/左侧等。我不确定是否有解决方法,或者这是否真的有帮助。但它很有趣,所以我想我还是会发布它。

    【讨论】:

      【解决方案2】:

      回顾一下:

      每个数据点矩形都在其自己的、独立的 &lt;svg&gt; 元素内。在 transformMarker 事件中,每个 svg 元素都使用绝对定位(lefttop 样式)进行定位。

      显然,如果要移动与数据点关联的 svg 图形,则需要更改 &lt;svg&gt; 元素的定位。您正在尝试使用 xy 属性来实现,但这并不是 SVG 最初的定位方式。这些属性只有在 &lt;svg&gt; 元素嵌入 另一个 SVG 时才相关。您的元素是绝对定位在页面上的独立元素。所以需要更改绝对定位样式值:

      function scatter(d){
            d = new google.maps.LatLng(J.lat(d), J.lon(d));
            d = projection.fromLatLngToDivPixel(d);
            d3.select(this)
              .style("left", (d.x + 15) + "px")
              .style("top", (d.y + 15) + "px")
      }
      

      另外,我不知道你为什么在初始化时调用transformMarker 两次;您只需要一次,但请确保您保留的版本是在创建元素之后 调用的版本:

      var marker = layer.selectAll("svg")
            .data(data)
            .enter().append("svg:svg")
              .each(transformMarker)
              .on('mouseover', scatter);
      

      【讨论】:

      • 感谢您抽出宝贵时间帮助我了解 Amelia。
      • 我已经删除了不必要的transformMarker。你关于一致定位的观点最终是关键。我想我之前尝试过使用lefttop,但使用.attr 分配了它们,而不是.style。我已经让它在最基本的水平上工作,所以现在看看我是否可以使运动随机、动画(通过过渡)并在一段时间后重置!干杯。
      • 啊,是的,移动地图时需要第二个transformMarker 来重绘元素。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-15
      • 2012-11-15
      • 2016-04-18
      相关资源
      最近更新 更多