以数学方式转换 SVG 路径中的值以填充 viewBox
Mathematically transform the values in an SVG path to fill the viewBox
所以,假设我有一个如下所示的 SVG:
<svg width="800" height="600" viewBox="0 0 800 600" style="border: 1px solid blue;">
<path fill="#f00" stroke="none" d="M720 394.5c-27.98 0-51.61-6.96-71.97-18.72-29.64-17.1-52.36-44.37-71.48-75.12-28-45.01-48.31-97.48-71.39-136.52-20.03-33.88-42.14-57.64-73.16-57.64-31.1 0-53.24 23.88-73.31 57.89-23.04 39.05-43.34 91.45-71.31 136.39-19.28 30.98-42.21 58.41-72.2 75.45C195 387.72 171.62 394.5 144 394.5Z"/>
</svg>
如你所见,路径只占据了SVG的一部分(和viewBox区域)。
我想知道如何转换它们填充 viewBox 的路径中的值(本质上是重新缩放和重新定位路径中的值,以便它填充整个 viewBox)。
[更新]
我正在添加更多细节...
举个例子 - 假设我开始使用带有 viewBox 的 SVG,如下所示:0 0 1600 1600
.
在该 SVG 中,有一条路径占据了从 1200,1200
到 1500,1400
的区域。 (即路径为 300 x 200)。
我希望能够提取该路径,并将其添加到视图框为 0 0 300 200
的新 SVG 中。
为此,需要相应地修改 d
属性中的值 - 基本上向上和向左移动 1200 点。
显然,绝对坐标需要改变,但相对坐标则不需要。 (这应该很容易)。
但我还必须处理曲线及其控制点,这可能会有点棘手。
一个完美的解决方案将能够检查一条路径,确定可能包含它的最小边界框,然后调整所有点,使它们适合该边界框,锚定在 0,0
。
我不想缩放或拉伸路径。
我对执行此操作的数学过程或函数或某种在线工具同样满意。
我知道我可以使用 SVG 转换来完成此操作,但我希望能够更改实际路径。
(即,我不希望我的网页包含 "incorrect" 数据并转换为 "correct" 它;我只希望我的代码包含 "correct" 数据。 )
有办法吗?
如果您不需要缩放新路径,那么您需要做的就是应用 transform
将其移动到正确的位置。如果它开始于 (1200, 1200),而你希望它位于 (0,0),则进行变换 "translate(-1200, -1200)"
<svg width="800" height="600" viewBox="0 0 800 600" style="border: 1px solid blue;">
<path fill="#f00" stroke="none" transform="translate(-1200,-1200)"
d="M720 394.5c-27.98 0-51.61-6.96-71.97-18.72-29.64-17.1-52.36-44.37-71.48-75.12-28-45.01-48.31-97.48-71.39-136.52-20.03-33.88-42.14-57.64-73.16-57.64-31.1 0-53.24 23.88-73.31 57.89-23.04 39.05-43.34 91.45-71.31 136.39-19.28 30.98-42.21 58.41-72.2 75.45C195 387.72 171.62 394.5 144 394.5Z"/>
</svg>
在您更新之前,我已经写下了大部分答案。因此,我的回答是对我认为您最初想要的内容的回应:能够直接更改 SVG 路径的 "d" 属性,以便该路径现在只填充 SVG 视口。因此,我的回答确实涉及缩放,您建议您在原始答案中确实需要但在更新中不需要。无论如何,我希望我的代码能让您了解如何在不使用转换的情况下直接更改 d 属性。
下面的代码片段以红色显示您提供的原始路径,"transformed" 路径以蓝色显示。请注意 svg 代码,前提是两条路径的起点相同。您可以获得蓝色路径的 d 属性,至少在 Firefox 中,通过右键单击路径并选择 "inspect element".
希望代码中的变量名和注释能为您提供理解我的方法所需的指导。
(更新:修复了代码片段中的代码,现在它也可以在 Chrome 和 Safari 中工作,而不仅仅是在 Firefox 中。 似乎一些 ES6 语言"let"、"const"、解构、符号等功能在 Firefox 中有效,但至少其中一些在 Chrome 或 Safari 中无效。我还没有检查 Internet Explorer 或 Opera或任何其他浏览器。)
// Retrieve the "d" attribute of the SVG path you wish to transform.
var $svgRoot = $("svg");
var $path = $svgRoot.find("path#moved");
var oldPathDStr = $path.attr("d");
// Calculate the transformation required.
var obj = getTranslationAndScaling($svgRoot, $path);
var pathTranslX = obj.pathTranslX;
var pathTranslY = obj.pathTranslY;
var scale = obj.scale;
// The path could be transformed at this point with a simple
// "transform" attribute as shown here.
// $path.attr("transform", `translate(${pathTranslX}, ${pathTranslY}), scale(${scale})`);
// However, as described in your question you didn't want this.
// Therefore, the code following this line mutates the actual svg path.
// Calculate the path "d" attributes parameters.
var newPathDStr = getTransformedPathDStr(oldPathDStr, pathTranslX, pathTranslY, scale);
// Apply the new "d" attribute to the path, transforming it.
$path.attr("d", newPathDStr);
document.write("<p>Altered 'd' attribute of path:</p><p>" + newPathDStr + "</p>");
// This is the end of the main code. Below are the functions called.
// Calculate the transformation, i.e. the translation and scaling, required
// to get the path to fill the svg area. Note that this assumes uniform
// scaling, a path that has no other transforms applied to it, and no
// differences between the svg viewport and viewBox dimensions.
function getTranslationAndScaling($svgRoot, $path) {
var svgWdth = $svgRoot.attr("width" );
var svgHght = $svgRoot.attr("height");
var origPathBoundingBox = $path[0].getBBox();
var origPathWdth = origPathBoundingBox.width ;
var origPathHght = origPathBoundingBox.height;
var origPathX = origPathBoundingBox.x ;
var origPathY = origPathBoundingBox.y ;
// how much bigger is the svg root element
// relative to the path in each dimension?
var scaleBasedOnWdth = svgWdth / origPathWdth;
var scaleBasedOnHght = svgHght / origPathHght;
// of the scaling factors determined in each dimension,
// use the smaller one; otherwise portions of the path
// will lie outside the viewport (correct term?)
var scale = Math.min(scaleBasedOnWdth, scaleBasedOnHght);
// calculate the bounding box parameters
// after the path has been scaled relative to the origin
// but before any subsequent translations have been applied
var scaledPathX = origPathX * scale;
var scaledPathY = origPathY * scale;
var scaledPathWdth = origPathWdth * scale;
var scaledPathHght = origPathHght * scale;
// calculate the centre points of the scaled but untranslated path
// as well as of the svg root element
var scaledPathCentreX = scaledPathX + (scaledPathWdth / 2);
var scaledPathCentreY = scaledPathY + (scaledPathHght / 2);
var svgRootCentreX = 0 + (svgWdth / 2);
var svgRootCentreY = 0 + (svgHght / 2);
// calculate translation required to centre the path
// on the svg root element
var pathTranslX = svgRootCentreX - scaledPathCentreX;
var pathTranslY = svgRootCentreY - scaledPathCentreY;
return {pathTranslX, pathTranslY, scale};
}
function getTransformedPathDStr(oldPathDStr, pathTranslX, pathTranslY, scale) {
// constants to help keep track of the types of SVG commands in the path
var BOTH_X_AND_Y = 1;
var JUST_X = 2;
var JUST_Y = 3;
var NONE = 4;
var ELLIPTICAL_ARC = 5;
var ABSOLUTE = 6;
var RELATIVE = 7;
// two parallel arrays, with each element being one component of the
// "d" attribute of the SVG path, with one component being either
// an instruction (e.g. "M" for moveto, etc.) or numerical value
// for either an x or y coordinate
var oldPathDArr = getArrayOfPathDComponents(oldPathDStr);
var newPathDArr = [];
var commandParams, absOrRel, oldPathDComp, newPathDComp;
// element index
var idx = 0;
while (idx < oldPathDArr.length) {
var oldPathDComp = oldPathDArr[idx];
if (/^[A-Za-z]$/.test(oldPathDComp)) { // component is a single letter, i.e. an svg path command
newPathDArr[idx] = oldPathDArr[idx];
switch (oldPathDComp.toUpperCase()) {
case "A": // elliptical arc command...the most complicated one
commandParams = ELLIPTICAL_ARC;
break;
case "H": // horizontal line; requires only an x-coordinate
commandParams = JUST_X;
break;
case "V": // vertical line; requires only a y-coordinate
commandParams = JUST_Y;
break;
case "Z": // close the path
commandParams = NONE;
break;
default: // all other commands; all of them require both x and y coordinates
commandParams = BOTH_X_AND_Y;
}
absOrRel = ((oldPathDComp === oldPathDComp.toUpperCase()) ? ABSOLUTE : RELATIVE);
// lowercase commands are relative, uppercase are absolute
idx += 1;
} else { // if the component is not a letter, then it is a numeric value
var translX, translY;
if (absOrRel === ABSOLUTE) { // the translation is required for absolute commands...
translX = pathTranslX;
translY = pathTranslY;
} else if (absOrRel === RELATIVE) { // ...but not relative ones
translX = 0;
translY = 0;
}
switch (commandParams) {
// figure out which of the numeric values following an svg command
// are required, and then transform the numeric value(s) from the
// original path d-attribute and place it in the same location in the
// array that will eventually become the d-attribute for the new path
case BOTH_X_AND_Y:
newPathDArr[idx ] = Number(oldPathDArr[idx ]) * scale + translX;
newPathDArr[idx + 1] = Number(oldPathDArr[idx + 1]) * scale + translY;
idx += 2;
break;
case JUST_X:
newPathDArr[idx ] = Number(oldPathDArr[idx ]) * scale + translX;
idx += 1;
break;
case JUST_Y:
newPathDArr[idx ] = Number(oldPathDArr[idx ]) * scale + translY;
idx += 1;
break;
case ELLIPTICAL_ARC:
// the elliptical arc has x and y values in the first and second as well as
// the 6th and 7th positions following the command; the intervening values
// are not affected by the transformation and so can simply be copied
newPathDArr[idx ] = Number(oldPathDArr[idx ]) * scale + translX;
newPathDArr[idx + 1] = Number(oldPathDArr[idx + 1]) * scale + translY;
newPathDArr[idx + 2] = Number(oldPathDArr[idx + 2]) ;
newPathDArr[idx + 3] = Number(oldPathDArr[idx + 3]) ;
newPathDArr[idx + 4] = Number(oldPathDArr[idx + 4]) ;
newPathDArr[idx + 5] = Number(oldPathDArr[idx + 5]) * scale + translX;
newPathDArr[idx + 6] = Number(oldPathDArr[idx + 6]) * scale + translY;
idx += 7;
break;
case NONE:
throw new Error('numeric value should not follow the SVG Z/z command');
break;
}
}
}
return newPathDArr.join(" ");
}
function getArrayOfPathDComponents(str) {
// assuming the string from the d-attribute of the path has all components
// separated by a single space, then create an array of components by
// simply splitting the string at those spaces
str = standardizePathDStrFormat(str);
return str.split(" ");
}
function standardizePathDStrFormat(str) {
// The SVG standard is flexible with respect to how path d-strings are
// formatted but this makes parsing them more difficult. This function ensures
// that all SVG path d-string components (i.e. both commands and values) are
// separated by a single space.
return str
.replace(/,/g , " " ) // replace each comma with a space
.replace(/-/g , " -" ) // precede each minus sign with a space
.replace(/([A-Za-z])/g, " ") // sandwich each letter between 2 spaces
.replace(/ /g , " " ) // collapse repeated spaces to a single space
.replace(/ ([Ee]) /g , "" ) // remove flanking spaces around exponent symbols
.replace(/^ /g , "" ) // trim any leading space
.replace(/ $/g , "" ); // trim any tailing space
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<svg width="800" height="600" viewBox="0 0 800 600" style="border: 1px solid blue;">
<path id="notmoved" fill="#f00" stroke="none" d="M720 394.5c-27.98 0-51.61-6.96-71.97-18.72-29.64-17.1-52.36-44.37-71.48-75.12-28-45.01-48.31-97.48-71.39-136.52-20.03-33.88-42.14-57.64-73.16-57.64-31.1 0-53.24 23.88-73.31 57.89-23.04 39.05-43.34 91.45-71.31 136.39-19.28 30.98-42.21 58.41-72.2 75.45C195 387.72 171.62 394.5 144 394.5Z" opacity="0.5" />
<path id="moved" fill="#00f" stroke="none" d="M720 394.5c-27.98 0-51.61-6.96-71.97-18.72-29.64-17.1-52.36-44.37-71.48-75.12-28-45.01-48.31-97.48-71.39-136.52-20.03-33.88-42.14-57.64-73.16-57.64-31.1 0-53.24 23.88-73.31 57.89-23.04 39.05-43.34 91.45-71.31 136.39-19.28 30.98-42.21 58.41-72.2 75.45C195 387.72 171.62 394.5 144 394.5Z" opacity="0.5" />
</svg>
所以,假设我有一个如下所示的 SVG:
<svg width="800" height="600" viewBox="0 0 800 600" style="border: 1px solid blue;">
<path fill="#f00" stroke="none" d="M720 394.5c-27.98 0-51.61-6.96-71.97-18.72-29.64-17.1-52.36-44.37-71.48-75.12-28-45.01-48.31-97.48-71.39-136.52-20.03-33.88-42.14-57.64-73.16-57.64-31.1 0-53.24 23.88-73.31 57.89-23.04 39.05-43.34 91.45-71.31 136.39-19.28 30.98-42.21 58.41-72.2 75.45C195 387.72 171.62 394.5 144 394.5Z"/>
</svg>
如你所见,路径只占据了SVG的一部分(和viewBox区域)。
我想知道如何转换它们填充 viewBox 的路径中的值(本质上是重新缩放和重新定位路径中的值,以便它填充整个 viewBox)。
[更新]
我正在添加更多细节...
举个例子 - 假设我开始使用带有 viewBox 的 SVG,如下所示:0 0 1600 1600
.
在该 SVG 中,有一条路径占据了从 1200,1200
到 1500,1400
的区域。 (即路径为 300 x 200)。
我希望能够提取该路径,并将其添加到视图框为 0 0 300 200
的新 SVG 中。
为此,需要相应地修改 d
属性中的值 - 基本上向上和向左移动 1200 点。
显然,绝对坐标需要改变,但相对坐标则不需要。 (这应该很容易)。
但我还必须处理曲线及其控制点,这可能会有点棘手。
一个完美的解决方案将能够检查一条路径,确定可能包含它的最小边界框,然后调整所有点,使它们适合该边界框,锚定在 0,0
。
我不想缩放或拉伸路径。
我对执行此操作的数学过程或函数或某种在线工具同样满意。
我知道我可以使用 SVG 转换来完成此操作,但我希望能够更改实际路径。
(即,我不希望我的网页包含 "incorrect" 数据并转换为 "correct" 它;我只希望我的代码包含 "correct" 数据。 )
有办法吗?
如果您不需要缩放新路径,那么您需要做的就是应用 transform
将其移动到正确的位置。如果它开始于 (1200, 1200),而你希望它位于 (0,0),则进行变换 "translate(-1200, -1200)"
<svg width="800" height="600" viewBox="0 0 800 600" style="border: 1px solid blue;">
<path fill="#f00" stroke="none" transform="translate(-1200,-1200)"
d="M720 394.5c-27.98 0-51.61-6.96-71.97-18.72-29.64-17.1-52.36-44.37-71.48-75.12-28-45.01-48.31-97.48-71.39-136.52-20.03-33.88-42.14-57.64-73.16-57.64-31.1 0-53.24 23.88-73.31 57.89-23.04 39.05-43.34 91.45-71.31 136.39-19.28 30.98-42.21 58.41-72.2 75.45C195 387.72 171.62 394.5 144 394.5Z"/>
</svg>
在您更新之前,我已经写下了大部分答案。因此,我的回答是对我认为您最初想要的内容的回应:能够直接更改 SVG 路径的 "d" 属性,以便该路径现在只填充 SVG 视口。因此,我的回答确实涉及缩放,您建议您在原始答案中确实需要但在更新中不需要。无论如何,我希望我的代码能让您了解如何在不使用转换的情况下直接更改 d 属性。
下面的代码片段以红色显示您提供的原始路径,"transformed" 路径以蓝色显示。请注意 svg 代码,前提是两条路径的起点相同。您可以获得蓝色路径的 d 属性,至少在 Firefox 中,通过右键单击路径并选择 "inspect element".
希望代码中的变量名和注释能为您提供理解我的方法所需的指导。
(更新:修复了代码片段中的代码,现在它也可以在 Chrome 和 Safari 中工作,而不仅仅是在 Firefox 中。 似乎一些 ES6 语言"let"、"const"、解构、符号等功能在 Firefox 中有效,但至少其中一些在 Chrome 或 Safari 中无效。我还没有检查 Internet Explorer 或 Opera或任何其他浏览器。)
// Retrieve the "d" attribute of the SVG path you wish to transform.
var $svgRoot = $("svg");
var $path = $svgRoot.find("path#moved");
var oldPathDStr = $path.attr("d");
// Calculate the transformation required.
var obj = getTranslationAndScaling($svgRoot, $path);
var pathTranslX = obj.pathTranslX;
var pathTranslY = obj.pathTranslY;
var scale = obj.scale;
// The path could be transformed at this point with a simple
// "transform" attribute as shown here.
// $path.attr("transform", `translate(${pathTranslX}, ${pathTranslY}), scale(${scale})`);
// However, as described in your question you didn't want this.
// Therefore, the code following this line mutates the actual svg path.
// Calculate the path "d" attributes parameters.
var newPathDStr = getTransformedPathDStr(oldPathDStr, pathTranslX, pathTranslY, scale);
// Apply the new "d" attribute to the path, transforming it.
$path.attr("d", newPathDStr);
document.write("<p>Altered 'd' attribute of path:</p><p>" + newPathDStr + "</p>");
// This is the end of the main code. Below are the functions called.
// Calculate the transformation, i.e. the translation and scaling, required
// to get the path to fill the svg area. Note that this assumes uniform
// scaling, a path that has no other transforms applied to it, and no
// differences between the svg viewport and viewBox dimensions.
function getTranslationAndScaling($svgRoot, $path) {
var svgWdth = $svgRoot.attr("width" );
var svgHght = $svgRoot.attr("height");
var origPathBoundingBox = $path[0].getBBox();
var origPathWdth = origPathBoundingBox.width ;
var origPathHght = origPathBoundingBox.height;
var origPathX = origPathBoundingBox.x ;
var origPathY = origPathBoundingBox.y ;
// how much bigger is the svg root element
// relative to the path in each dimension?
var scaleBasedOnWdth = svgWdth / origPathWdth;
var scaleBasedOnHght = svgHght / origPathHght;
// of the scaling factors determined in each dimension,
// use the smaller one; otherwise portions of the path
// will lie outside the viewport (correct term?)
var scale = Math.min(scaleBasedOnWdth, scaleBasedOnHght);
// calculate the bounding box parameters
// after the path has been scaled relative to the origin
// but before any subsequent translations have been applied
var scaledPathX = origPathX * scale;
var scaledPathY = origPathY * scale;
var scaledPathWdth = origPathWdth * scale;
var scaledPathHght = origPathHght * scale;
// calculate the centre points of the scaled but untranslated path
// as well as of the svg root element
var scaledPathCentreX = scaledPathX + (scaledPathWdth / 2);
var scaledPathCentreY = scaledPathY + (scaledPathHght / 2);
var svgRootCentreX = 0 + (svgWdth / 2);
var svgRootCentreY = 0 + (svgHght / 2);
// calculate translation required to centre the path
// on the svg root element
var pathTranslX = svgRootCentreX - scaledPathCentreX;
var pathTranslY = svgRootCentreY - scaledPathCentreY;
return {pathTranslX, pathTranslY, scale};
}
function getTransformedPathDStr(oldPathDStr, pathTranslX, pathTranslY, scale) {
// constants to help keep track of the types of SVG commands in the path
var BOTH_X_AND_Y = 1;
var JUST_X = 2;
var JUST_Y = 3;
var NONE = 4;
var ELLIPTICAL_ARC = 5;
var ABSOLUTE = 6;
var RELATIVE = 7;
// two parallel arrays, with each element being one component of the
// "d" attribute of the SVG path, with one component being either
// an instruction (e.g. "M" for moveto, etc.) or numerical value
// for either an x or y coordinate
var oldPathDArr = getArrayOfPathDComponents(oldPathDStr);
var newPathDArr = [];
var commandParams, absOrRel, oldPathDComp, newPathDComp;
// element index
var idx = 0;
while (idx < oldPathDArr.length) {
var oldPathDComp = oldPathDArr[idx];
if (/^[A-Za-z]$/.test(oldPathDComp)) { // component is a single letter, i.e. an svg path command
newPathDArr[idx] = oldPathDArr[idx];
switch (oldPathDComp.toUpperCase()) {
case "A": // elliptical arc command...the most complicated one
commandParams = ELLIPTICAL_ARC;
break;
case "H": // horizontal line; requires only an x-coordinate
commandParams = JUST_X;
break;
case "V": // vertical line; requires only a y-coordinate
commandParams = JUST_Y;
break;
case "Z": // close the path
commandParams = NONE;
break;
default: // all other commands; all of them require both x and y coordinates
commandParams = BOTH_X_AND_Y;
}
absOrRel = ((oldPathDComp === oldPathDComp.toUpperCase()) ? ABSOLUTE : RELATIVE);
// lowercase commands are relative, uppercase are absolute
idx += 1;
} else { // if the component is not a letter, then it is a numeric value
var translX, translY;
if (absOrRel === ABSOLUTE) { // the translation is required for absolute commands...
translX = pathTranslX;
translY = pathTranslY;
} else if (absOrRel === RELATIVE) { // ...but not relative ones
translX = 0;
translY = 0;
}
switch (commandParams) {
// figure out which of the numeric values following an svg command
// are required, and then transform the numeric value(s) from the
// original path d-attribute and place it in the same location in the
// array that will eventually become the d-attribute for the new path
case BOTH_X_AND_Y:
newPathDArr[idx ] = Number(oldPathDArr[idx ]) * scale + translX;
newPathDArr[idx + 1] = Number(oldPathDArr[idx + 1]) * scale + translY;
idx += 2;
break;
case JUST_X:
newPathDArr[idx ] = Number(oldPathDArr[idx ]) * scale + translX;
idx += 1;
break;
case JUST_Y:
newPathDArr[idx ] = Number(oldPathDArr[idx ]) * scale + translY;
idx += 1;
break;
case ELLIPTICAL_ARC:
// the elliptical arc has x and y values in the first and second as well as
// the 6th and 7th positions following the command; the intervening values
// are not affected by the transformation and so can simply be copied
newPathDArr[idx ] = Number(oldPathDArr[idx ]) * scale + translX;
newPathDArr[idx + 1] = Number(oldPathDArr[idx + 1]) * scale + translY;
newPathDArr[idx + 2] = Number(oldPathDArr[idx + 2]) ;
newPathDArr[idx + 3] = Number(oldPathDArr[idx + 3]) ;
newPathDArr[idx + 4] = Number(oldPathDArr[idx + 4]) ;
newPathDArr[idx + 5] = Number(oldPathDArr[idx + 5]) * scale + translX;
newPathDArr[idx + 6] = Number(oldPathDArr[idx + 6]) * scale + translY;
idx += 7;
break;
case NONE:
throw new Error('numeric value should not follow the SVG Z/z command');
break;
}
}
}
return newPathDArr.join(" ");
}
function getArrayOfPathDComponents(str) {
// assuming the string from the d-attribute of the path has all components
// separated by a single space, then create an array of components by
// simply splitting the string at those spaces
str = standardizePathDStrFormat(str);
return str.split(" ");
}
function standardizePathDStrFormat(str) {
// The SVG standard is flexible with respect to how path d-strings are
// formatted but this makes parsing them more difficult. This function ensures
// that all SVG path d-string components (i.e. both commands and values) are
// separated by a single space.
return str
.replace(/,/g , " " ) // replace each comma with a space
.replace(/-/g , " -" ) // precede each minus sign with a space
.replace(/([A-Za-z])/g, " ") // sandwich each letter between 2 spaces
.replace(/ /g , " " ) // collapse repeated spaces to a single space
.replace(/ ([Ee]) /g , "" ) // remove flanking spaces around exponent symbols
.replace(/^ /g , "" ) // trim any leading space
.replace(/ $/g , "" ); // trim any tailing space
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<svg width="800" height="600" viewBox="0 0 800 600" style="border: 1px solid blue;">
<path id="notmoved" fill="#f00" stroke="none" d="M720 394.5c-27.98 0-51.61-6.96-71.97-18.72-29.64-17.1-52.36-44.37-71.48-75.12-28-45.01-48.31-97.48-71.39-136.52-20.03-33.88-42.14-57.64-73.16-57.64-31.1 0-53.24 23.88-73.31 57.89-23.04 39.05-43.34 91.45-71.31 136.39-19.28 30.98-42.21 58.41-72.2 75.45C195 387.72 171.62 394.5 144 394.5Z" opacity="0.5" />
<path id="moved" fill="#00f" stroke="none" d="M720 394.5c-27.98 0-51.61-6.96-71.97-18.72-29.64-17.1-52.36-44.37-71.48-75.12-28-45.01-48.31-97.48-71.39-136.52-20.03-33.88-42.14-57.64-73.16-57.64-31.1 0-53.24 23.88-73.31 57.89-23.04 39.05-43.34 91.45-71.31 136.39-19.28 30.98-42.21 58.41-72.2 75.45C195 387.72 171.62 394.5 144 394.5Z" opacity="0.5" />
</svg>