gpt4 book ai didi

javascript - 旋转 SVG 路径点以获得更好的变形

转载 作者:行者123 更新时间:2023-11-30 12:05:02 24 4
gpt4 key购买 nike

我正在使用 Snap.SVG 中的几个函数,主要是 path2curve 和它周围的函数来构建一个 SVG 变形插件。

我已经设置了一个演示 here on Codepen以更好地说明问题。基本上变形形状从简单到复杂,反之亦然作为 Javascript 功能正常工作,但是,视觉效果不是很令人愉悦。

第一个形状变形看起来很糟糕,第二个看起来好一点,因为我稍微改变/旋转了它的点,但最后一个例子很完美。

所以我需要一个更好的 path2curve 或一个函数来在另一个函数构建曲线数组之前准备路径字符串。 Snap.SVG 有一个名为 getClosest 的函数,我认为它可能有用,但没有记录在案。

没有关于此主题的任何文档,因此我非常感谢 RaphaelJS/SnapSVG/d3.js/three/js 开发人员的任何建议/输入。

最佳答案

我在下面提供了一个使用 Snap.svg 的可运行代码片段,我相信它展示了解决您的问题的一种方法。关于试图找到将起始形状变形为结束形状的最佳方法,该算法本质上是一次旋转一个位置的起始形状的点,对(旋转的)起始形状上对应点之间的距离的平方求和形状和(不变的)结束形状​​,并找到所有这些总和的最小值。即它基本上是最小二乘法。最小值标识旋转,作为第一个猜测,将提供“最短”变形轨迹。然而,尽管进行了这些坐标重新分配,但所有“旋转”都应根据需要产生视觉上相同的起始形状。

当然,这是一种“盲”的数学方法,但它可能有助于在进行手动可视化分析之前为您提供一个起点。作为奖励,即使您不喜欢算法选择的旋转,它也会为所有其他旋转提供路径“d”属性字符串,因此其中一些工作已经为您完成。

您可以修改代码片段以提供您想要的任何开始和结束形状。限制如下:

  • 每个形状应具有相同数量的点(尽管点类型,例如“lineto”、“三次贝塞尔曲线”、“水平线”等可以完全不同)
  • 每个形状都应该是闭合的,即以“Z”结尾
  • 所需的变形应该只涉及翻译。如果需要缩放或旋转,则应在仅基于平移计算变形后应用这些。

顺便说一句,作为对您的一些评论的回应,虽然我发现 Snap.svg 很有趣,但我也发现它的文档有些欠缺。

更新:下面的代码片段适用于 Firefox(Mac 或 Windows)和 Safari。但是,Chrome 似乎无法从其外部网站(<script...github...>)访问 Snap.svg 库。 Opera 和 Internet Explorer 也有问题。因此,请在可用的浏览器中尝试该片段,或尝试将片段代码和 Snap 库代码复制到您自己的计算机上。 (这是从代码片段中访问第三方库的问题吗?为什么浏览器存在差异?将不胜感激有见地的评论。)

var
s = Snap(),
colors = ["red", "blue", "green", "orange"], // colour list can be any length
staPath = s.path("M25,35 l-15,-25 C35,20 25,0 40,0 L80,40Z"), // create the "start" shape
endPath = s.path("M10,110 h30 l30,20 C30,120 35,135 25,135Z"), // create the "end" shape
staSegs = getSegs(staPath), // convert the paths to absolute values, using only cubic bezier
endSegs = getSegs(endPath), // segments, & extract the pt coordinates & segment strings
numSegs = staSegs.length, // note: the # of pts is one less than the # of path segments
numPts = numSegs - 1, // b/c the path's initial 'moveto' pt is also the 'close' pt
linePaths = [],
minSumLensSqrd = Infinity,
rotNumOfMin,
rotNum = 0;

document.querySelector('button').addEventListener('click', function() {
if (rotNum < numPts) {
linePaths.forEach(function(linePath) {linePath.remove();}); // erase any previous coloured lines
var sumLensSqrd = 0;
for (var ptNum = 0; ptNum < numPts; ptNum += 1) { // draw new lines, point-to-point
var linePt1 = staSegs[(rotNum + ptNum) % numPts]; // the new line begins on the 'start' shape
var linePt2 = endSegs[ ptNum % numPts]; // and finished on the 'end' shape
var linePathStr = "M" + linePt1.x + "," + linePt1.y + "L" + linePt2.x + "," + linePt2.y;
var linePath = s.path(linePathStr).attr({stroke: colors[ptNum % colors.length]}); // draw it
var lineLen = Snap.path.getTotalLength(linePath); // calculate its length
sumLensSqrd += lineLen * lineLen; // square the length, and add it to the accumulating total
linePaths[ptNum] = linePath; // remember the path to facilitate erasing it later
}
if (sumLensSqrd < minSumLensSqrd) { // keep track of which rotation has the lowest value
minSumLensSqrd = sumLensSqrd; // of the sum of lengths squared (the 'lsq sum')
rotNumOfMin = rotNum; // as well as the corresponding rotation number
}
show("ROTATION OF POINTS #" + rotNum + ":"); // display info about this rotation
var rotInfo = getRotInfo(rotNum);
show("&nbsp;&nbsp;point coordinates: " + rotInfo.ptsStr); // show point coordinates
show("&nbsp;&nbsp;path 'd' string: " + rotInfo.dStr); // show 'd' string needed to draw it
show("&nbsp;&nbsp;sum of (coloured line lengths squared) = " + sumLensSqrd); // the 'lsq sum'
rotNum += 1; // analyze the next rotation of points
} else { // once all the rotations have been analyzed individually...
linePaths.forEach(function(linePath) {linePath.remove();}); // erase any coloured lines
show("&nbsp;");
show("BEST ROTATION, i.e. rotation with lowest sum of (lengths squared): #" + rotNumOfMin);
// show which rotation to use
show("Use the shape based on this rotation of points for morphing");
$("button").off("click");
}
});

function getSegs(path) {
var absCubDStr = Snap.path.toCubic(Snap.path.toAbsolute(path.attr("d")));
return Snap.parsePathString(absCubDStr).map(function(seg, segNum) {
return {x: seg[segNum ? 5 : 1], y: seg[segNum ? 6 : 2], seg: seg.toString()};
});
}

function getRotInfo(rotNum) {
var ptsStr = "";
for (var segNum = 0; segNum < numSegs; segNum += 1) {
var oldSegNum = rotNum + segNum;
if (segNum === 0) {
var dStr = "M" + staSegs[oldSegNum].x + "," + staSegs[oldSegNum].y;
} else {
if (oldSegNum >= numSegs) oldSegNum -= numPts;
dStr += staSegs[oldSegNum].seg;
}
if (segNum !== (numSegs - 1)) {
ptsStr += "(" + staSegs[oldSegNum].x + "," + staSegs[oldSegNum].y + "), ";
}
}
ptsStr = ptsStr.slice(0, ptsStr.length - 2);
return {ptsStr: ptsStr, dStr: dStr};
}

function show(msg) {
var m = document.createElement('pre');
m.innerHTML = msg;
document.body.appendChild(m);
}
pre {
margin: 0;
padding: 0;
}
<script src="//cdn.jsdelivr.net/snap.svg/0.4.1/snap.svg-min.js"></script>
<p>Best viewed on full page</p>
<p>Coloured lines show morph trajectories for the points for that particular rotation of points. The algorithm seeks to optimize those trajectories, essentially trying to find the "shortest" cumulative routes.</p>
<p>The order of points can be seen by following the colour of the lines: red, blue, green, orange (at least when this was originally written), repeating if there are more than 4 points.</p>
<p><button>Click to show rotation of points on top shape</button></p>

关于javascript - 旋转 SVG 路径点以获得更好的变形,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35491152/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com