首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用SVG animateTransform在SVG内旋转路径

使用SVG animateTransform在SVG内旋转路径
EN

Stack Overflow用户
提问于 2020-12-29 00:20:09
回答 1查看 734关注 0票数 1

这是我之前问过的一个问题的后续,使HTML画布生成PDF就绪的线条艺术?。在@Peter o.的建议下,我重新设计了我的软件以使用SVG而不是HTML画布。我现在更愿意创建一个高质量的图像:

整个对象有一个外部<svg>。每个视图都有一个翻译好的<svg>。相反,箭头是作为路径绘制的。

下面是生成箭头的相关代码:

代码语言:javascript
复制
    transformForPiece(p) {
        return 'translate(' + this.radius + ',' + this.radius + ') rotate('+p.angle+')'
    }
    idForPiece(p) {
        return "arrow-" + p.col + "," + p.row;
    }
    addPathForPiece(p) {
        let x = this.x(p.col);
        let y = this.y(p.row);
        let r = this.radius;
        var box = document.createElementNS('http://www.w3.org/2000/svg','svg');
        box.setAttribute('x', x-r);
        box.setAttribute('y', y-r);
        box.setAttribute('width', r*2);
        box.setAttribute('height', r*2);
        this.svg.append(box)

        /* linexy - draw a line to the X,Y position */
        function lxy(x,y){
            return "L " + x + " " + y + " ";
        }

        let arrow = document.createElementNS('http://www.w3.org/2000/svg','path');
        arrow.setAttribute('style', 'stroke:none;fill:black');
        arrow.setAttribute('transform', this.transformForPiece(p));
        arrow.setAttribute('d',
                          'M 0 0 '+lxy(0,-r) + lxy(-r, 0) + lxy(-r/2,0)
                          + lxy(-r/2, r) + lxy(+r/2,r) + lxy(+r/2,0) + lxy(r,0) + lxy(0,-r) + " Z ");
        arrow.setAttribute('id', this.idForPiece(p));
        p.path = arrow;         // modify the piece with its svg path
        box.append(arrow);
    }

我可以使用JavaScript计时器对这些箭头的旋转进行动画化:

代码语言:javascript
复制
/* Given a board drawer db, a piece p, a start and and end rotation, rotate it */
const INCREMENT_DEGREES = 10;
const INCREMENT_MS      = 10;
function rotatePieceTimer( db, p, start, end ) {
    if (start > end) {
        start = end;
    }
    p.angle = start;            // set the current angle
    db.drawPiece(p);            // draw the piece at the curren totation
    if (p.angle < end) {         // if we have more to go
        setTimeout( function () { rotatePieceTimer( db, p, start+INCREMENT_DEGREES, end); }, INCREMENT_MS);
    }
}

这与一项活动有关:

代码语言:javascript
复制
    function roll (e) {
        /* Pick a random piece */
        let i = Math.floor(Math.random() * cells_wide);
        let j = Math.floor(Math.random() * cells_high);
        let p = board.piece(i,j); // get the piece
        console.log("roll p=",p);
        rotatePieceTimer(db, p, p.angle, Math.ceil(p.angle/180)*180+180) // rotate with timer
        //rotatePieceSVG(db, p, p.angle, Math.ceil(p.angle/180)*180+180) // rotate with timer
        //$('#time').text(boardHistory.length);
    }

    $('#roll').click(  roll );

从注释掉的代码中可以看出,我想使用SVG animationTransform来制作动画。我所拥有的一切都很好,但是如果知道如何使动画转换工作,那就太好了。

我已经将工作代码抽象到这里:

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en" class="stylish" type="text/css>"
      <body>
        <svg width="400" height="400">
          <svg x="203" y="203" width="60.66666666666667" height="60.66666666666667">
            <path style="stroke:none;fill:black" transform="translate(30.333333333333336,30.333333333333336) rotate(45)"
                  d="M 0 0 L 0 -30.333333333333336 L -30.333333333333336 0 L -15.166666666666668 0 L -15.166666666666668 30.333333333333336 L 15.166666666666668
                     30.333333333333336 L 15.166666666666668 0 L 30.333333333333336 0 L 0 -30.333333333333336  Z "
                  id="arrow-3,3">
              <animateTransform attributeName="transform" attributeType="XML" type="rotate" from="180 30.333333333333336 30.333333333333336"
                                to="360 30.333333333333336 30.333333333333336" dur="1s" repeatCount="100">
              </animateTransform>
            </path>
    </svg>
    </svg>
      </body>
</html>

这种使用animateTransform的问题是箭头不是很好地旋转,就像我在path中简单地更新rotate(45) CSS转换时那样。

我遇到的另一个问题是,当用户单击按钮时,我想要实现这一点,这与下面的代码差不多(这是坏的):

代码语言:javascript
复制
function rotatePieceSVG( db, p, start, end){
    // this just changes the attribute:

    var noAnimation = false;
    if (noAnimation) {
        p.angle = end;
        p.path.setAttribute('transform',db.transformForPiece(p));
        return;
    }

    // This is the animation I can't get to work...
    var t = document.createElementNS('http://www.w3.org/2000/svg', 'animateTransform');
    t.setAttribute('attributeName','transform');
    t.setAttribute('attributeType','XML');
    t.setAttribute('type','rotate');
    t.setAttribute('from',p.angle+' '+db.radius+' '+db.radius);
    p.angle = end;
    t.setAttribute('to',p.angle+' '+db.radius+' '+db.radius);
    t.setAttribute('dur','1s');
    p.path.append(t);
    console.log("after rotate: p=",p);
}

我已经验证了这段代码是否将animateTransform添加到DOM的正确位置。但是,将animateTransform添加到现有的path对象似乎并不会导致路径被动画化。我读过说明书,但它很大,我可能漏掉了什么。我是否需要删除旧的path对象并添加一个新的path对象,而不仅仅是添加动画转换?可能吧。我还没试过呢。

所以我的问题是:

  1. 如何使animateTransform正确地旋转箭头?我尝试过摆弄from=to=中的值,我永远也无法让箭头在中心旋转。
  2. 是否有将animateTransform添加到现有路径的方法,还是需要删除当前路径并添加新路径?

完整的代码在这里:https://jsfiddle.net/simsong/9udcga6r/1/

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-12-29 01:40:27

代码中最突出的是begin<animateTransform>中缺少一个值。begin值是非常通用的。

  • 您可以将它们设置为"0s",这是默认的,并且与文档时间线开始的时间(基本上是load窗口事件)相吻合。现在不是您以后交互添加标记的时候。到那时,0已经过去了。
  • 您可以将它们设置为"indefinite",这样动画就不会启动。这通常与基于脚本的触发器一起使用,例如SVGAnimationElement.beginElement()
  • 您可以将它们设置为任意事件。当带有"myButton.click"的元素收到单击时,id="myButton"将启动动画。

语法还提供了启动时间列表和延迟。研究等级库中的语法是非常值得的,以了解您可以做些什么。(浏览器没有实现时间值规范的唯一部分是wallclock()函数。)

对于旋转中心,你需要仔细观察你转换什么,旋转什么。正如您的代码所显示的那样,<animateTransform> 替换了元素上的transform属性的整个。您可能需要的是添加到现有转换列表末尾的补充转换。这是由属性additive="sum" (默认为"replace")控制的。

代码语言:javascript
复制
        <path style="stroke:none;fill:black" transform="translate(30.333,30.333) rotate(45)"
              d="M 0 0 L 0 -30.333 L -30.333 0 L -15.166 0 L -15.166 30.333 L 15.166
                 30.333 L 15.166 0 L 30.333 0 L 0 -30.333 Z "
              id="arrow-3,3">
          <animateTransform attributeName="transform" type="rotate"
                            from="0"
                            to="180"
                            begin="myButton.click" dur="1s" repeatCount="100"
                            additive="sum">
          </animateTransform>
        </path>

这将使元素在本地用户空间坐标中围绕(0, 0)点旋转100次,从0到180度。有三件事需要仔细研究:

  1. 旋转中心必须在应用所有以前的转换后,在本地用户空间坐标中设置,因为它被添加到转换列表的末尾。(坐标系统的转换,而不是对象的转换。)这相当于:它与路径数据所使用的坐标系相同。该路径的中心位于坐标系原点。
  2. 转换添加到基础元素转换中。这意味着,它没有取代最初的45度旋转,而是增加了它。因此,从恒等变换开始加性变换通常是个好主意。
  3. 正如您所定义的,旋转从0到180度,然后立即重新启动。这意味着它将毫不拖延地跳回到最初的位置。如果您想要平稳的移动,可以从0度旋转到360度,或者设置属性accumulate="sum",这将在上一次迭代结束时开始下一次迭代。
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65485761

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档