SVG路径的透视变换(四角扭曲)
Posted
技术标签:
【中文标题】SVG路径的透视变换(四角扭曲)【英文标题】:Perspective transform of SVG paths (four corner distort) 【发布时间】:2012-10-06 19:55:38 【问题描述】:如何在浏览器中扭曲 SVG 中的路径,以便使用可能的 javascript 或 css 将它们扭曲到某些角度?透视扭曲可以在 Photoshop、Illustrator 等中轻松制作,但是浏览器呢?
这是源路径:
这是改造后的路径:
【问题讨论】:
【参考方案1】:这是我的拖动扭曲提案 (share you knowledge, Q&A-style)。
现场示例在http://jsfiddle.net/xjHUk/278/,主要代码如下: (仅输出窗口:http://jsfiddle.net/xjHUk/279/embedded/result/)
函数 transferPoint (xI, yI, source, destination) 变量添加 = 0.001; // 避免被零除 var xA = 源[0].x; var yA = 源[0].y; var xC = 源[2].x; var yC = 源[2].y; var xAu = 目的地[0].x; var yAu = 目的地[0].y; var xBu = 目的地[1].x; var yBu = 目的地[1].y; var xCu = 目的地[2].x; var yCu = 目的地[2].y; var xDu = 目的地[3].x; var yDu = 目的地[3].y; // 计算 // 如果点相同,必须添加一个 ADDING 以避免被零除 如果(xBu==xCu)xCu+=添加; 如果(xAu==xDu)xDu+=添加; 如果(xAu==xBu)xBu+=添加; 如果(xDu==xCu)xCu+=添加; var kBC = (yBu-yCu)/(xBu-xCu); var kAD = (yAu-yDu)/(xAu-xDu); var kAB = (yAu-yBu)/(xAu-xBu); var kDC = (yDu-yCu)/(xDu-xCu); 如果(kBC==kAD)kAD+=添加; var xE = (kBC*xBu - kAD*xAu + yAu - yBu) / (kBC-kAD); var yE = kBC*(xE - xBu) + yBu; 如果(kAB==kDC)kDC+=添加; var xF = (kAB*xBu - kDC*xCu + yCu - yBu) / (kAB-kDC); 变量 yF = kAB*(xF - xBu) + yBu; 如果(xE==xF)xF+=添加; var kEF = (yE-yF) / (xE-xF); 如果(kEF==kAB)kAB+=加法; var xG = (kEF*xDu - kAB*xAu + yAu - yDu) / (kEF-kAB); 变量 yG = kEF*(xG - xDu) + yDu; 如果(kEF==kBC)kBC+=添加; 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); 变量 xJ = (xG-xDu)*rG + xDu; var yJ = (yG-yDu)*rG + yDu; var xK = (xH-xDu)*rH + xDu; var yK = (yH-yDu)*rH + yDu; 如果(xF==xJ)xJ+=添加; 如果(xE==xK)xK+=添加; var kJF = (yF-yJ) / (xF-xJ); //23 var kKE = (yE-yK) / (xE-xK); //12 变量 xKE; 如果(kJF==kKE)kKE+=添加; var xIu = (kJF*xF - kKE*xE + yE - yF) / (kJF-kKE); var yIu = kJF * (xIu - xJ) + yJ; 变量 b=x:xIu,y:yIu; b.x=数学.round(b.x); b.y=数学.round(b.y); 返回 b;结果被正确地扭曲为透视图(两个消失点一)。两点透视计算的原理是here。如果满足以下要求,该脚本可以处理 SVG 路径数据:
所有坐标都是绝对坐标(即大写字母)。见this。 未使用圆弧(“A”) V 和 H 归一化为 L弧可以被规范化,但我还没有找到任何跨浏览器的方式。 V 和 H 到 L 是简单的任务,你必须得到最后使用的 x 或 y 坐标,并在 L 之后添加缺少的一个。
相同的脚本也可以处理路径中的曲线(曲线来自时代)。以下是完全相同的代码,但路径属性(“d”)不同:
http://jsfiddle.net/xjHUk/277/function dummy(a) return a;
(此代码没有检查无效位置,如上)。
以上示例的路径来自于 Arial 和 Times 的 SVG 版本。请注意字体使用Cartesian coordinate system,向上时y坐标增加。否则 SVG 使用极坐标系,用于位图图像和 css。这意味着当在上述代码中使用来自 SVG 字体的路径时,路径必须垂直翻转并缩放到所需的字体大小。 TTF 字体(及其对应的 SVG 字体)通常 em 大小为 2048,因此字形的边界框没有缩放 2048 px,这在 SVG 字形路径转换为 SVG 路径时通常太大了。
但如果您想扭曲其他 SVG 路径,则无需进行翻转和缩放。
这是相当长的代码(很大程度上是因为拖动功能),但我认为同样的效果也可以通过一些 css-3D-transform-way 实现,但在这样的实现中还没有运气......
为了比较一个非透视扭曲的例子(SVG的主要竞争对手SWF):http://www.rubenswieringa.com/code/as3/flex/DistortImage/
还有一个 VALID 透视计算示例:http://zehfernando.com/f/TriangleTest.swf
【讨论】:
是否可以使用 SVG 中的图像进行四角变形?我想展示一些观点 - 想象一张折叠的贺卡。 抱歉回答迟了,但现在找到了一个库,虽然它从 2013 年就已经存在了。该技术使用转换属性。这种技术的好处是它可以转换任何 DOM 元素,而不仅仅是我建议的 svg 路径。不好的是质量不是很好,在矢量形状中有些像素化,但在使用位图图像时还可以。我举了一个例子:jsbin.com/xuxataqahe。该库来自edankwan.github.io/PerspectiveTransform.js。该库可能也适用于 SVG 图像,但尚未对其进行测试。 这很棒。修改这个求解器来计算包络线是曲线的失真会不会很困难,就像this example 中那样? 我们可以管理仅 SVG 的解决方案吗?我不明白为什么 SVG 转换不只是采用完整的 3x3 矩阵,因为将应用于 3 向量被用作 2 点的同质表示。这将允许简单的投影变换。这让我很烦恼,这是我的烦恼带给我的地方之一。 是否可以在没有 Javascript 的情况下做到这一点?仅在 SVG 中使用声明式样式?【参考方案2】:所选答案已过时。
尽管 SVG 不支持改变元素的视角,但可以通过使用 CSS3 变换来实现。
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 变换 rotate(angle)
、scale(x y)
非常相似,但最好不要把它们等同起来,因为两者之间存在根本差异,CSS3 比 SVG 变换强大得多。此外,CSS3 Transforms 有一个属性perspective
,你肯定需要看看。
没有比 W3 更好的地方可以找到有关 CSS3 转换的更多信息:http://www.w3.org/TR/css3-transforms/
这是一个代码sn-p:
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>
变身快乐!
【讨论】:
如何在您的方法中进行四角拖动?这是我 QA 的重点。 @timo 是的,您可以通过 javascript 控制透视图。这将是一些非常复杂的编码,可能需要我几天时间才能弄清楚。但理论上是可以做到的。 @AeroWindwalker:您的意思是通过使用 javascript 控制 css 变换属性值(matrix3D、translate3D、rotate3D、scale3D、transform-origin、perspective、perspective-origin 等)来拖动四个角?首先,您知道四个角的源点和目标点,然后尝试为这些 css 属性计算正确的值? 正确,但老实说,如果您有权访问 SVG 文件,则可以将属性添加到文件中,使其可拉伸(可能让用户上传文件或将它们镜像到远程 url) - 在在这种情况下你的生活会更轻松。 @user2070775 虽然 SVG 支持大多数 CSS3,但 SVG 渲染器始终会忽略透视/3d 变换(如您在示例中显示的变换)。以上是关于SVG路径的透视变换(四角扭曲)的主要内容,如果未能解决你的问题,请参考以下文章
OpenCV中的「透视变换 / 投影变换 / 单应性」—cv.warpPerspectivecv.findHomography