烘焙转变成SVG路径元素的命令 [英] Baking transforms into SVG Path Element commands

查看:401
本文介绍了烘焙转变成SVG路径元素的命令的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

TL;博士总结:给我的资源或帮助解决以下code转化为SVG路径命令<路径> 通过任意矩阵元素

详情
我正在写一个库转换任意SVG捏成一个<路径> 元素。我也当有它的工作没有变换=...层次结构中的元素,但现在我要烤本地的对象转变为的 命令本身路径数据。

这是主要工作的(以下code)时,使用简单的通过MoveTo /命令了lineTo处理的。但是,我不知道的适当的方式来改造贝塞尔手柄或包含arcTo参数。

例如,我能够在这个圆角矩形转换为<路径>

 <矩形X =10Y =30RX =10RY =20WIDTH =80高度=70/>
 - > <路径D =M20,30 L80,30 A10,20,0,0,1,90,50 L90,80 A10,20,0,0,1,80,100
             L20,100 A10,20,0,0,1,10,80 L10,50 A10,20,0,0,1,20,30/>
 

和我没有任何圆角转化时,得到一个有效的结果:

 <矩形X =10Y =30WIDTH =80高度=70
      变换=翻译(-200,0)规模(1.5)旋转(50)/>
 - > <路径D =M10,30 L90,30 L90,100 L10,100 L10,30/>
 

然而,改造椭圆弧命令产量有趣的结果:
的虚线是实际转化RECT,绿色填充是我的道路。

以下是code我至今(略有删节过)。我也有一个测试页面在那里我测试各种形状。请帮我确定如何正确地将椭圆弧和其他各种贝塞尔命令给任意变换矩阵。

 函数flattenToPaths(EL,转换SVG){
  (!SVG)如果SVG =报;而(SVG和放大器;&安培;!svg.tagName ='SVG')SVG = svg.parentNode;
  VAR DOC = el.ownerDocument;
  变种svgNS = svg.getAttribute('的xmlns');

  //恒等变换,如果没有传递
  (!变换)如果变换= svg.createSVGMatrix();

  //计算本地变换矩阵为对象
  变种localMatrix = svg.createSVGMatrix();
  对于(VAR XS = el.transform.baseVal,I = xs.numberOfItems-1; I> = 0;  - 我){
    localMatrix = xs.getItem(我).matrix.multiply(localMatrix);
  }
  //通过任何已通过递归的变换局部变换
  变换= transform.multiply(localMatrix);

  VAR路径= doc.createElementNS(svgNS,'路径');
  开关(el.tagName){
    案矩形:
      path.setAttribute('中风',el.getAttribute('中风'));
      VAR X = el.getAttribute('X')* 1,Y = el.getAttribute(Y)* 1,
          W = el.getAttribute('宽')* 1,H = el.getAttribute('高度')* 1,
          RX = el.getAttribute(RX)* 1,RY = el.getAttribute('RY')* 1;
      如果(Rx和放大器;&安培;!el.hasAttribute(RY))RY = RX;
      否则,如果(RY和放大器;&安培;!el.hasAttribute(RX))RX = RY;
      如果(RX>瓦特/ 2)RX =瓦特/ 2;
      如果(RY> H / 2)RY = H / 2;
      path.setAttribute(D,
        M+(X + RX)+,+ Y +
        ''L +(X + W-RX)+','+ Y +
        ((RX || RY)(A+ RX +','+ RY +,0,0,'+(RX * RY℃,0:α1)+','+(X + W)+' ,'+(Y + RY)):'')+
        ''L +(X + W)+','+(Y + H-RY)+
        ((RX || RY)(A+ RX +','+ RY +,0,0,'+(RX * RY℃,0:α1)+','+(X + W-RX) +','+(Y + h))进行:')+
        ''L +(X + RX)+','+(Y + H)+
        ((RX || RY)(A+ RX +','+ RY +,0,0,'+(RX * RY℃,0:α1)+','+ X +','+(γ + H-RY)):'')+
        L+ X +','+(Y + RY)+
        ((RX || RY)(A+ RX +','+ RY +,0,0,'+(RX * RY℃,0:α1)+','+(X + RX)+' ,'+ Y):'')
      );
    打破;

    案圈:
      VAR CX = el.getAttribute('CX')* 1,CY = el.getAttribute('CY')* 1,
          R = el.getAttribute(为'r')* 1时,r0 = R / 2 +','+ R / 2;
      path.setAttribute(D,M+ CX +','+(CY-R)+A+ R0 +',0,0,0,'+ CX +','+(CY + R)+' + R0 +',0,0,0,'+ CX +','+(CY-R));
    打破;

    案椭圆形:
      VAR CX = el.getAttribute('CX')* 1,CY = el.getAttribute('CY')* 1,
          RX = el.getAttribute(RX)* 1,RY = el.getAttribute('RY')* 1;
      path.setAttribute(D,M+ CX +','+(CY-RY)+A+ RX +','+ RY +',0,0,0,'+ CX +','+(CY + RY)+''+ RX +','+ RY +',0,0,0,'+ CX +','+(CY-RY));
    打破;

    案线:
      变种X1 = el.getAttribute('×1')* 1,Y1 = el.getAttribute('-Y 1')* 1,
          X2 = el.getAttribute('×2')* 1,Y2 = el.getAttribute('Y2')* 1;
      path.setAttribute(D,M+ X1 +','+ Y1 +''L + 2 +','+ y2)上;
    打破;

    案折线:
    案多边形:
      对于(VAR I = 0,1 = [],PTS = el.points,LEN = pts.numberOfItems; I< LEN ++我){
        变种p值= pts.getItem(ⅰ);
        升[I] = p.x +','+ p.y;
      }
      path.setAttribute(D,M+ l.shift()+L+ l.join('')+(el.tagName =='多边形')?'Z':'');
    打破;

    案路径:
      PATH = el.cloneNode(假);
    打破;
  }

  //通过变换矩阵转换局部空间
  VAR的x,y;
  VAR PT = svg.createSVGPoint();
  VAR setXY =功能(X,Y,XN,YN){
    pt.x = X; pt.y = Y;
    PT = pt.matrixTransform(变换);
    如果(XN)赛格[XN] = pt.x;
    如果(YN)赛格[YN] = pt.y;
  };

  //提取物旋转和缩放的变换
  VAR旋转= Math.atan2(transform.b,transform.d)* 180 / Math.PI;
  VAR SX =的Math.sqrt(transform.a * transform.a + transform.c * transform.c);
  变种SY =的Math.sqrt(transform.b * transform.b + transform.d * transform.d);

  // FIXME:必须把任何水平或垂直命令了lineTo成绝对通过MoveTo
  对于(VAR SEGS = path.pathSegList,C = segs.numberOfItems,我= 0;我c为C ++我){
    VAR SEG = segs.getItem(我);

    //奇数路径段都是相对的
    // http://www.w3.org/TR/SVG/paths.html#InterfaceSVGPathSeg
    变种而isRelative =(seg.pathSegType%2 == 1);
    VAR hasX = seg.x!= NULL;
    VAR hasY = seg.y!= NULL;
    如果(hasX)X =而isRelative? X + seg.x:seg.x;
    如果(hasY)Y =而isRelative? Y + seg.y:seg.y;
    如果(hasX || hasY)setXY(X,Y,hasX&安培;&安培;'X',hasY&安培;&安培;'Y');

    如果(seg.x1!= NULL)setXY(seg.x1,seg.y1,'×1','1 -Y 1');
    如果(seg.x2!= NULL)setXY(seg.x2,seg.y2,'×2','Y 2');
    如果(seg.angle!= NULL){
      seg.angle + =旋转;
      seg.r1 * = SX; // 整我;仅适用于均匀缩放
      seg.r2 * = SY; // 整我;仅适用于均匀缩放
    }
  }

  返回路径;
}
 

解决方案

我已经做了一般SVG拼合flatten.js,支持所有的形状和路径命令: https://gist.github.com/timo22345/9413158

基本用法:扁平化(的document.getElementById('SVG'));

它的作用:展平元素(转换元素路径和变平转换)。 如果参数元素(id为上面的'SVG')有孩子,或者它的后代有孩子, 这些孩子元素也平坦化。

什么可以展平:整个SVG文件,单个形状(道路,圆,椭圆等)和团体。嵌套组自动处理。

怎么样的属性?所有属性被复制。仅不在路径元件有效参数,被丢弃(例如,R,RX,RY,CX,CY),但它们不再需要。同时变换属性被丢弃,因为转换被夷为平地,路径命令。

如果要修改使用非仿射方法(如透视扭曲)坐标路径, 你可以将所有段使用三次曲线: 扁平化(的document.getElementById('SVG),真);

也有争论toAbsolute(坐标转换为绝对)和'月', 小数分隔符后位数。

至尊路径和形状测试仪: http://jsfiddle.net/xqq5w/embedded/result/

基本用法例如:<一href="http://jsfiddle.net/Nv78L/3/embedded/result/">http://jsfiddle.net/Nv78L/3/embedded/result/

缺点:text元素不工作。这可能是我的下一个目标。

tl;dr summary: Give me the resources or help fix the below code to transform path commands for SVG <path> elements by an arbitrary matrix.

details:
I'm writing a library to convert any arbitrary SVG shape into a <path> element. I have it working when there are no transform="..." elements in the hierarchy, but now I want to bake the local transform of the object into the path data commands themselves.

This is mostly working (code below) when dealing with the simple moveto/lineto commands. However, I'm not sure of the appropriate way to transform the bezier handles or arcTo parameters.

For example, I am able to convert this rounded rectangle to a <path>:

<rect x="10" y="30" rx="10" ry="20" width="80" height="70" />
--> <path d=​"M20,30 L80,30 A10,20,0,0,1,90,50 L90,80 A10,20,0,0,1,80,100
             L20,100 A10,20,0,0,1,10,80 L10,50 A10,20,0,0,1,20,30" />

And I get a valid result when transforming without any round corners:

<rect x="10" y="30" width="80" height="70"
      transform="translate(-200,0) scale(1.5) rotate(50)" />
--> <path d=​"M10,30 L90,30 L90,100 L10,100 L10,30" />

However, transforming only the x/y coords of the elliptical arc commands yields amusing results:
The dotted line is the actual transformed rect, the green fill is my path.

Following is the code I have so far (slightly pared-down). I also have a test page where I'm testing various shapes. Please help me determine how to properly transform the elliptical arc and various other bezier commands given an arbitrary transformation matrix.

function flattenToPaths(el,transform,svg){
  if (!svg) svg=el; while(svg && svg.tagName!='svg') svg=svg.parentNode;
  var doc = el.ownerDocument;
  var svgNS = svg.getAttribute('xmlns');

  // Identity transform if nothing passed in
  if (!transform) transform= svg.createSVGMatrix();

  // Calculate local transform matrix for the object
  var localMatrix = svg.createSVGMatrix();
  for (var xs=el.transform.baseVal,i=xs.numberOfItems-1;i>=0;--i){
    localMatrix = xs.getItem(i).matrix.multiply(localMatrix);
  }
  // Transform the local transform by whatever was recursively passed in
  transform = transform.multiply(localMatrix);

  var path = doc.createElementNS(svgNS,'path');
  switch(el.tagName){
    case 'rect':
      path.setAttribute('stroke',el.getAttribute('stroke'));
      var x  = el.getAttribute('x')*1,     y  = el.getAttribute('y')*1,
          w  = el.getAttribute('width')*1, h  = el.getAttribute('height')*1,
          rx = el.getAttribute('rx')*1,    ry = el.getAttribute('ry')*1;
      if (rx && !el.hasAttribute('ry')) ry=rx;
      else if (ry && !el.hasAttribute('rx')) rx=ry;
      if (rx>w/2) rx=w/2;
      if (ry>h/2) ry=h/2;
      path.setAttribute('d',
        'M'+(x+rx)+','+y+
        'L'+(x+w-rx)+','+y+
        ((rx||ry) ? ('A'+rx+','+ry+',0,0,'+(rx*ry<0?0:1)+','+(x+w)+','+(y+ry)) : '') +
        'L'+(x+w)+','+(y+h-ry)+
        ((rx||ry) ? ('A'+rx+','+ry+',0,0,'+(rx*ry<0?0:1)+','+(x+w-rx)+','+(y+h)) : '')+
        'L'+(x+rx)+','+(y+h)+
        ((rx||ry) ? ('A'+rx+','+ry+',0,0,'+(rx*ry<0?0:1)+','+x+','+(y+h-ry)) : '')+
        'L'+x+','+(y+ry)+
        ((rx||ry) ? ('A'+rx+','+ry+',0,0,'+(rx*ry<0?0:1)+','+(x+rx)+','+y) : '')
      );
    break;

    case 'circle':
      var cx = el.getAttribute('cx')*1, cy = el.getAttribute('cy')*1,
          r  = el.getAttribute('r')*1,  r0 = r/2+','+r/2;
      path.setAttribute('d','M'+cx+','+(cy-r)+' A'+r0+',0,0,0,'+cx+','+(cy+r)+' '+r0+',0,0,0,'+cx+','+(cy-r) );
    break;

    case 'ellipse':
      var cx = el.getAttribute('cx')*1, cy = el.getAttribute('cy')*1,
          rx = el.getAttribute('rx')*1, ry = el.getAttribute('ry')*1;
      path.setAttribute('d','M'+cx+','+(cy-ry)+' A'+rx+','+ry+',0,0,0,'+cx+','+(cy+ry)+' '+rx+','+ry+',0,0,0,'+cx+','+(cy-ry) );
    break;

    case 'line':
      var x1=el.getAttribute('x1')*1, y1=el.getAttribute('y1')*1,
          x2=el.getAttribute('x2')*1, y2=el.getAttribute('y2')*1;
      path.setAttribute('d','M'+x1+','+y1+'L'+x2+','+y2);
    break;

    case 'polyline':
    case 'polygon':
      for (var i=0,l=[],pts=el.points,len=pts.numberOfItems;i<len;++i){
        var p = pts.getItem(i);
        l[i] = p.x+','+p.y;
      }
      path.setAttribute('d',"M"+l.shift()+"L"+l.join(' ') + (el.tagName=='polygon') ? 'z' : '');
    break;

    case 'path':
      path = el.cloneNode(false);
    break;
  }

  // Convert local space by the transform matrix
  var x,y;
  var pt = svg.createSVGPoint();
  var setXY = function(x,y,xN,yN){
    pt.x = x; pt.y = y;
    pt = pt.matrixTransform(transform);
    if (xN) seg[xN] = pt.x;
    if (yN) seg[yN] = pt.y;
  };

  // Extract rotation and scale from the transform
  var rotation = Math.atan2(transform.b,transform.d)*180/Math.PI;
  var sx = Math.sqrt(transform.a*transform.a+transform.c*transform.c);
  var sy = Math.sqrt(transform.b*transform.b+transform.d*transform.d);

  // FIXME: Must translate any Horizontal or Vertical lineto commands into absolute moveto
  for (var segs=path.pathSegList,c=segs.numberOfItems,i=0;i<c;++i){
    var seg = segs.getItem(i);

    // Odd-numbered path segments are all relative
    // http://www.w3.org/TR/SVG/paths.html#InterfaceSVGPathSeg
    var isRelative = (seg.pathSegType%2==1);
    var hasX = seg.x != null;
    var hasY = seg.y != null;
    if (hasX) x = isRelative ? x+seg.x : seg.x;
    if (hasY) y = isRelative ? y+seg.y : seg.y;
    if (hasX || hasY) setXY( x, y, hasX && 'x', hasY && 'y' );

    if (seg.x1 != null) setXY( seg.x1, seg.y1, 'x1', 'y1' );
    if (seg.x2 != null) setXY( seg.x2, seg.y2, 'x2', 'y2' );
    if (seg.angle != null){
      seg.angle += rotation;
      seg.r1 *= sx; // FIXME; only works for uniform scale
      seg.r2 *= sy; // FIXME; only works for uniform scale
    }
  }

  return path;
}

解决方案

I have made a general SVG flattener flatten.js, that supports all shapes and path commands: https://gist.github.com/timo22345/9413158

Basic usage: flatten(document.getElementById('svg'));

What it does: Flattens elements (converts elements to paths and flattens transformations). If the argument element (whose id is above 'svg') has children, or it's descendants has children, these children elements are flattened also.

What can be flattened: entire SVG document, individual shapes (path, circle, ellipse etc.) and groups. Nested groups are handled automatically.

How about attributes? All attributes are copied. Only arguments that are not valid in path element, are dropped (eg. r, rx, ry, cx, cy), but they are not needed anymore. Also transform attribute is dropped, because transformations are flattened to path commands.

If you want to modify path coordinates using non-affine methods (eg. perspective distort), you can convert all segments to cubic curves using: flatten(document.getElementById('svg'), true);

There are also arguments 'toAbsolute' (convert coordinates to absolute) and 'dec', number of digits after decimal separator.

Extreme path and shape tester: http://jsfiddle.net/xqq5w/embedded/result/

Basic usage example: http://jsfiddle.net/Nv78L/3/embedded/result/

CONS: text element is not working. It could be my next goal.

这篇关于烘焙转变成SVG路径元素的命令的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆