SVG路径的透视变换(四角变形)

时间:2012-10-16 16:36:13

标签: javascript css svg transformation perspective

如何在浏览器中扭曲SVG中的路径,以便使用javascript或css将它们扭曲到某个角度?透视扭曲可以在Photoshop,Illustrator等中轻松实现,但是浏览器呢?

这是源路径:

enter image description here

这是转型后的道路:

enter image description here

2 个答案:

答案 0 :(得分:27)

这是我的拖拽扭曲提案(share you knowledge, Q&A-style)。

实例位于http://jsfiddle.net/xjHUk/278/,主要代码如下:
(仅输出窗口:http://jsfiddle.net/xjHUk/279/embedded/result/

function transferPoint (xI, yI, source, destination)
{
    var ADDING = 0.001; // to avoid dividing by zero

    var xA = source[0].x;
    var yA = source[0].y;

    var xC = source[2].x;
    var yC = source[2].y;

    var xAu = destination[0].x;
    var yAu = destination[0].y;

    var xBu = destination[1].x;
    var yBu = destination[1].y;

    var xCu = destination[2].x;
    var yCu = destination[2].y;

    var xDu = destination[3].x;
    var yDu = destination[3].y;

    // Calcultations
    // if points are the same, have to add a ADDING to avoid dividing by zero
    if (xBu==xCu) xCu+=ADDING;
    if (xAu==xDu) xDu+=ADDING;
    if (xAu==xBu) xBu+=ADDING;
    if (xDu==xCu) xCu+=ADDING;
    var kBC = (yBu-yCu)/(xBu-xCu);
    var kAD = (yAu-yDu)/(xAu-xDu);
    var kAB = (yAu-yBu)/(xAu-xBu);
    var kDC = (yDu-yCu)/(xDu-xCu);

    if (kBC==kAD) kAD+=ADDING;
    var xE = (kBC*xBu - kAD*xAu + yAu - yBu) / (kBC-kAD);
    var yE = kBC*(xE - xBu) + yBu;

    if (kAB==kDC) kDC+=ADDING;
    var xF = (kAB*xBu - kDC*xCu + yCu - yBu) / (kAB-kDC);
    var yF = kAB*(xF - xBu) + yBu;

    if (xE==xF) xF+=ADDING;
    var kEF = (yE-yF) / (xE-xF);

    if (kEF==kAB) kAB+=ADDING;
    var xG = (kEF*xDu - kAB*xAu + yAu - yDu) / (kEF-kAB);
    var yG = kEF*(xG - xDu) + yDu;

    if (kEF==kBC) kBC+=ADDING;
    var xH = (kEF*xDu - kBC*xBu + yBu - yDu) / (kEF-kBC);
    var yH = kEF*(xH - xDu) + yDu;

    var rG = (yC-yI)/(yC-yA);
    var rH = (xI-xA)/(xC-xA);

    var xJ = (xG-xDu)*rG + xDu;
    var yJ = (yG-yDu)*rG + yDu;

    var xK = (xH-xDu)*rH + xDu;
    var yK = (yH-yDu)*rH + yDu;

    if (xF==xJ) xJ+=ADDING;
    if (xE==xK) xK+=ADDING;
    var kJF = (yF-yJ) / (xF-xJ); //23
    var kKE = (yE-yK) / (xE-xK); //12

    var xKE;
    if (kJF==kKE) kKE+=ADDING;
    var xIu = (kJF*xF - kKE*xE + yE - yF) / (kJF-kKE);
    var yIu = kJF * (xIu - xJ) + yJ;

    var b={x:xIu,y:yIu}; 
    b.x=Math.round(b.x);
    b.y=Math.round(b.y);
    return b;
}

结果正确地扭曲到透视(两个消失点1)。两点透视计算的原理是here。如果SVG路径数据满足以下要求,则该脚本可以处理它:

  • 所有坐标都是绝对的(表示大写字母)。见this
  • 未使用Arc(“A”)
  • V和H归一化为L

Arcs可以标准化,但我还没有发现任何crossbrowser方式。 V和H到L是很容易的任务,你必须得到最后使用的x或y坐标并在L之后添加缺失的坐标。

相同的脚本也可以处理路径中的曲线(曲线来自Times)。以下是完全相同的代码,但路径属性(“d”)不同:

http://jsfiddle.net/xjHUk/277/ function dummy(a) {return a;} (此代码没有检查无效位置,如上所述。)

以上示例的路径来自Arial和Times的SVG版本。请注意,字体使用Cartesian coordinate system,其中y坐标在向上时会增加。否则SVG使用极坐标系,用于位图图像和css。这意味着在上面的代码中使用SVG字体的路径时,必须垂直翻转路径并缩放到所需的字体大小。 TTF字体(及其SVG对应物)通常具有2048个字符,因此字形的边界框没有缩放2048像素,这在SVG字形路径转换为SVG路径时通常太多。

但是如果你想扭曲其他SVG路径,那么翻转和缩放不必要的。

这是相当长的代码(很多是因为拖动功能),但我认为同样的效果也可以通过一些css-3D-transform-way来实现,但在这样的实现中却没有运气......

为了比较一个非透视扭曲的例子(SVG的主要竞争对手SWF):
http://www.rubenswieringa.com/code/as3/flex/DistortImage/

进一步比较VALID透视计算的一个例子:
http://zehfernando.com/f/TriangleTest.swf

答案 1 :(得分:11)

所选答案已过时。

尽管SVG不支持更改元素的透视图,但可以通过使用CSS3 Transforms实现此目的。

CSS3是一种非常强大的动画对象动画方式。可以使用Javascript实时创建和应用CSS3转换。

对CSS3 Transform有很好的支持,所有最新版本的主流浏览器都支持它。 (IE 8 +,Chrome 31 +,Safari 7.1 +,Opera 26 +,Firefox 34 +,Android浏览器4.1+)。更多信息:http://caniuse.com/#feat=transforms2d

对于某些浏览器版本,您需要使用特定于浏览器的前缀来创建转换。但是,有一个非常简洁的网站,解释了处理这个问题的好方法: http://bl.ocks.org/mbostock/10571478

某些CSS3转换属性为:rotate(angle)scale(dimension)。我知道,它们看起来类似于SVG Transforms rotate(angle)scale(x y),但最好不要将它们视为相同,因为两者之间存在根本差异,CSS3比SVG变换更强大。此外,CSS3 Transforms有一个你肯定需要查看的属性perspective

没有比W3更好的地方找到有关CSS3变换的更多信息:http://www.w3.org/TR/css3-transforms/

这是一段代码: an example of CSS3 Transforms



div {
  height: 150px;
  width: 150px;
}
.container {
  perspective: 500px;
  border: 1px solid black;
  background: gray;
}
.transformed {
  transform: rotateY(50deg);
  background: blue;
}

<!doctype html>
<html>
<body>
<div class="container">
  <div class="transformed"> Hola! He sido transformado!</div>
</div>
</body>
</html>
&#13;
&#13;
&#13;

快乐转型!