在SVG元素中保留变换

Preserve a Transform in an SVG element

本文关键字:保留 变换 元素 SVG      更新时间:2023-09-26

我有一个SVG形状(它很复杂,为了理解起见,我把它简化了),我需要对它进行转换。我的要求是

  1. 鼠标悬停时,该组应缩放为2倍
  2. 当鼠标移出时,组应缩回到1x
  3. 拖动时,应预先保存组比例

到目前为止,除了一个问题,我已经完成了所有的部分。当我试图将鼠标悬停在组上时,拖动该元素后,它会恢复到其原始位置。我不明白为什么会发生这种事。这是一把正在工作的小提琴。有人能帮我吗?

index.html

<svg width="400" height="400" style="background-color: red">
    <g id="op" class="operator" transform="translate(0,0)">
        <circle class="head" cx="50" cy="50" r="20" style="fill: yellow"></circle>
        <circle cx="40" cy="40" r="10" style="fill: blue"></circle>
        <circle cx="60" cy="40" r="10" style="fill: blue"></circle>
    </g>
</svg>

script.js

d3.selectAll('.operator')
        .on('mouseenter', function () {
            console.log('Mouse Enter');
            var c = d3.select(this).select('.head');
            var x = c.attr('cx');
            var y = c.attr('cy');
            var scale = 2;
            var scaleX = -1 * x * (scale - 1);
            var scaleY = -1 * y * (scale - 1);
            d3.select(this).attr('transform', 'translate(' + scaleX + ',' + scaleY + ')' + 'scale(' + scale + ')');
        })
        .on('mouseleave', function () {
            console.log('Mouse Leave');
            var c = d3.select(this).select('.head');
            var x = c.attr('cx');
            var y = c.attr('cy');
            var scale = 1;
            var scaleX = -1 * x * (scale - 1);
            var scaleY = -1 * y * (scale - 1);
            d3.select(this).attr('transform', 'translate(' + scaleX + ',' + scaleY + ')' + 'scale(' + scale + ')');
        })
        .call(d3.behavior.drag()
                .origin(function () {
                    var t = d3.select(this);
                    return {
                        x: d3.transform(t.attr("transform")).translate[0],
                        y: d3.transform(t.attr("transform")).translate[1]
                    };
                })
                .on('drag', function () {
                    var oldScale = d3.transform(d3.select(this).attr('transform')).scale;
                    d3.select(this).attr('transform', 'translate(' + d3.event.x + ',' + d3.event.y + ')scale(' + oldScale + ')');
                }))

正如@PhilAnderson所说,你不应该混合translatecx/cy。事实上,按照嵌套元素的方式,您应该只是在转换。在SVG中翻译g,然后在g中翻译圆圈。纠正这一点,事情变得简单多了:

<!DOCTYPE html>
<html>
<head>
  <script data-require="d3@3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
  <link rel="stylesheet" href="style.css" />
  <script src="script.js"></script>
</head>
<body>
  <svg width="400" height="400" style="background-color: red">
    <g id="op" class="operator" transform="translate(50,50)">
      <circle class="head" r="20" style="fill: yellow"></circle>
      <circle transform="translate(-10,0)" r="10" style="fill: blue"></circle>
      <circle transform="translate(10,0)" r="10" style="fill: blue"></circle>
    </g>
  </svg>
  <script>
    d3.select('.operator')
      .on('mouseenter', function() {
        console.log('Mouse Enter');
        
        var self = d3.select(this),
            xy = d3.transform(self.attr('transform')).translate,
            scale = 2;
        self.attr('transform', 'translate(' + xy[0] + ',' + xy[1] + ')' + 'scale(' + scale + ')');
      })
      .on('mouseleave', function() {
        console.log('Mouse Leave');
        var self = d3.select(this),
            xy = d3.transform(self.attr('transform')).translate,
            scale = 1;
            
        self.attr('transform', 'translate(' + xy[0] + ',' + xy[1] + ')' + 'scale(' + scale + ')');
      })
      .call(d3.behavior.drag()
        
        .on('drag', function() {
          
          var self = d3.select(this),
              oldScale = d3.transform(self.attr('transform')).scale;
          self.attr('transform', 'translate(' + d3.event.x + ',' + d3.event.y + ')scale(' + oldScale + ')');
        }))
  </script>
</body>
</html>

看起来您将cx和cy属性与translate混淆了。

在mouseenter和mouseleave中,您可以根据cx和cy的值移动形状,但在拖动事件中,您只需使用事件的x和y值进行平移。

解决这个问题的一种方法是在拖动事件中设置cx和cy属性,尽管tbh最好选择一种方法并始终坚持它。