SVG基本图形转路径
本帖最后由 马黑黑 于 2023-10-2 20:22 编辑 <br /><br /><style>#wrapper { margin: auto; width: 760px; height: fit-content; font: normal 16px/20px Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; position: relative; }
#wrapper h2 { margin: 20px; text-align: center; font-family: sans-serif; }
#wrapper > div, #wrapper > p { margin: 8px 0; }
#wrapper > hr { margin: 18px 0; border-width: 1px 0 0 0; border-color: #eee; }
#ssShape { margin-right: 16px; font-size: 14px; }
#eCode { padding: 20px; width: calc(100% - 40px); box-sizing: border-box; overflow-x: auto; tab-size: 4; caret-color: red; white-space: pre-wrap; word-wrap: break-word; }
#eCode:not(:focus):empty::before { content: attr(data-ph); }
#svgShape, #svgPath { width: fit-content; height: fit-content; min-width: 200px; min-height: 200px; border: 1px solid gray; }
#mysite { position: absolute; right: 10px; bottom: -110px; }
svg { border: 0px solid gray; }
.rred { color: red; }
</style>
<div id="wrapper">
<h2>SVG基本图形转路径</h2>
<hr>
<p>
<label for="ssShape">选择 : </label>
<select id="ssShape" name="shape" title="更改SVG基本图形"><option value="">svg基本图形</option></select>
<input id="btnColor" type="button" value="代码着色" />
</p>
<pre id="eCode" contenteditable="true" data-ph=">> svg图形代码待选择或输入"></pre>
<p>
<label for="btnOk"></label>
<input id="btnOk" type="button" value="确定" />
</p>
<hr>
<p>原图 : </p>
<div id="svgShape"></div>
<p>路径图 : </p>
<div id="svgPath">
<svg id="svg2" width="100%" height="100%">
<path id="cPath" d="" fill="none" stroke="blue"></path>
</svg>
</div>
<p>路径数据 : </p>
<div id="pathData"></div>
<div id="mysite"><a href="http://mhh.52qingyin.cn/">马黑网站</a></div>
</div>
<script>
/* 预定义基本svg图形 */
let shapes = {
line: '<line x1="10" y1="10" x2="190" y2="190" fill="none" stroke="red" />',
polyline: '<polyline points="10 10,10 190,100 10, 190 10" fill="none" stroke="red" />',
polygon: '<polygon points="100 10,190 190, 10 190" fill="none" stroke="red" />',
rect: '<rect x="10" y="10" width="180" height="180" rx="0" ry="0" fill="none" stroke="red" />',
circle: '<circle cx="100" cy="100" r="90" fill="none" stroke="red" />',
ellipse: '<ellipse cx="100" cy="100" rx="90" ry="60" fill="none" stroke="red" />'
};
let eles = Object.keys(shapes); /* 图形标签数组 */
/*函数 : 代码着色*/
let colorCode = (code) => {
let reg = /([\'\"]([^\"]+)[\'\"])/g;
code = code.replaceAll('<', '<');
code = code.replaceAll('>', '>');
return (code.replace(reg,'<span class="rred">$1</span>'));
};
//函数 : 生成 path
let getEleData = () => {
let svgEle = document.querySelector('svg').children;
let tagName = svgEle.tagName.toLowerCase();
if(!eles.includes(tagName)) return;
let path, x, y, r, cx, cy, rx, ry, w, h;
switch(tagName) {
case 'line':
x = 1 * svgEle.getAttribute('x1');
y = 1 * svgEle.getAttribute('y1');
cx = 1 * svgEle.getAttribute('x2');
cy = 1 * svgEle.getAttribute('y2');
path = `M${x} ${y} L${cx} ${cy}`;
break;
case 'polyline':
case 'polygon':
path = 'M';
let points = svgEle.getAttribute('points');
let ar = points.replace(/[^\d.]/g, ',').split(',').filter(Number);
for(j = 0; j < ar.length; j ++) {
if(j <= 1) {
path += ar + ' ';
}else{
path += (j % 2 === 0 ? 'L' + ar : ar) + ' ';
}
}
path += tagName === 'polygon' ? 'z' : '';
break;
case 'rect':
x = 1 * svgEle.getAttribute('x');
y = 1 * svgEle.getAttribute('y');
w = 1* svgEle.getAttribute('width');
h = 1* svgEle.getAttribute('height');
rx = svgEle.getAttribute('rx') ? 1 * svgEle.getAttribute('rx') : 1 * svgEle.getAttribute('ry');
ry = svgEle.getAttribute('ry') ? 1 * svgEle.getAttribute('ry') : 1 * svgEle.getAttribute('rx');
if(rx > w / 2) rx = w / 2;
if(ry > h / 2) ry = h / 2;
path = (rx === 0 || ry === 0)
? `M${x} ${y} h${w} v${h} h-${w} v-${h}`
: `M${x} ${y + ry}
a${rx} ${ry} 0 0 1 ${rx} -${ry}
h${w - rx * 2}
a${rx} ${ry} 0 0 1 ${rx} ${ry}
v${h - ry * 2}
a${rx} ${ry} 0 0 1 -${rx} ${ry}
h-${w - rx * 2}
a${rx} ${ry} 0 0 1 -${rx} -${ry}
v-${h - ry * 2}`;
break;
case 'circle':
cx = 1 * svgEle.getAttribute('cx');
cy = 1 * svgEle.getAttribute('cy');
r = 1 * svgEle.getAttribute('r');
path = `
M${cx-r} ${cy}
A${r} ${r} 0 1 1 ${(cx + r)} ${cy}
A${r} ${r} 0 1 1 ${cx-r} ${cy}
`;
break;
case 'ellipse':
cx = 1 * svgEle.getAttribute('cx');
cy = 1 * svgEle.getAttribute('cy');
rx = 1 * svgEle.getAttribute('rx');
ry = 1 * svgEle.getAttribute('ry');
path = `
M${cx-rx} ${cy}
A${rx} ${ry} 0 1 1 ${(cx + rx)} ${cy}
A${rx} ${ry} 0 1 1 ${cx-rx} ${cy}
`;
break;
}
return path;
};
/* select初始化 : 生成option菜单 */
(function() {
let hcode = '';
for(j = 0; j < eles.length; j++) {
hcode += `<option value="${j}">${eles}</option>`;
}
ssShape.innerHTML += hcode;
})();
/* 确定按钮 : 生成图形 */
btnOk.onclick = () => {
if(!eCode.innerText) return;
svgShape.innerHTML = eCode.innerText;
if(getEleData()) cPath.setAttribute('d', getEleData());
pathData.innerText = cPath.getAttribute('d').replaceAll('\n',' ');
};
/* select按钮 : 选择基本图形 */
ssShape.onchange = () => {
let code = '<svg width="200" height="200">\n\t';
let idx = ssShape.options.value;
code += shapes] + '\n<svg>';
eCode.innerHTML = code;
}
/* 着色按钮点击 : 代码上色 */
eCode.onfocus = btnColor.onclick = () => eCode.innerHTML = colorCode(eCode.innerText);
eCode.onmouseover = () => eCode.focus();
const resizeObserver = new ResizeObserver(() => {
svgPath.style.width = svgShape.offsetWidth + 'px';
svgPath.style.height =svgShape.offsetHeight + 'px';
});
resizeObserver.observe(svgShape);
</script>
首发链接:
http://mhh.52qingyin.cn/api/svg/svg2path.html 【说明】
SVG总共也就是6个基本图形标签:
[*]line
[*]polyline
[*]polygon
[*]rect
[*]circle
[*]ellipse
这些标签,都有基于位置、尺寸等相应的属性,通过这些属性,可以将图形相关数据转成 path 路径,最终达成使用万能的 path 标签绘制基本图形的目的。
本工具仅处理 svg 标签的第一个基本图形子标签,所生成的路径为 path 标签的 d 属性值:
<svg width="200" height="200">
<path d="路径数据" fill="none" stroke="blue" />
</svg>
20:22 修正原图与路径图绝对同步问题 本帖最后由 马黑黑 于 2023-10-2 22:28 编辑
代码
<style>
#wrapper { margin: auto; width: 760px; height: fit-content; font: normal 16px/20px Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; position: relative; }
#wrapper h2 { margin: 20px; text-align: center; font-family: sans-serif; }
#wrapper > div, #wrapper > p { margin: 8px 0; }
#wrapper > hr { margin: 18px 0; border-width: 1px 0 0 0; border-color: #eee; }
#ssShape { margin-right: 16px; font-size: 14px; }
#eCode { padding: 20px; width: calc(100% - 40px); box-sizing: border-box; overflow-x: auto; tab-size: 4; caret-color: red; white-space: pre-wrap; word-wrap: break-word; }
#eCode:not(:focus):empty::before { content: attr(data-ph); }
#svgShape, #svgPath { width: fit-content; height: fit-content; min-width: 200px; min-height: 200px; border: 1px solid gray; }
#mysite { position: absolute; right: 10px; bottom: -110px; }
svg { border: 0px solid gray; }
.rred { color: red; }
</style>
<div id="wrapper">
<h2>SVG基本图形转路径</h2>
<hr>
<p>
<label for="ssShape">选择 : </label>
<select id="ssShape" name="shape" title="更改SVG基本图形"><option value="">svg基本图形</option></select>
<input id="btnColor" type="button" value="代码着色" />
</p>
<pre id="eCode" contenteditable="true" data-ph=">> svg图形代码待选择或输入"></pre>
<p>
<label for="btnOk"></label>
<input id="btnOk" type="button" value="确定" />
</p>
<hr>
<p>原图 : </p>
<div id="svgShape"></div>
<p>路径图 : </p>
<div id="svgPath">
<svg id="svg2" width="100%" height="100%">
<path id="cPath" d="" fill="none" stroke="blue"></path>
</svg>
</div>
<p>路径数据 : </p>
<div id="pathData"></div>
<div id="mysite"><a href="http://mhh.52qingyin.cn/">马黑网站</a></div>
</div>
<script>
/* 预定义基本svg图形 */
let shapes = {
line: '<line x1="10" y1="10" x2="190" y2="190" fill="none" stroke="red" />',
polyline: '<polyline points="10 10,10 190,100 10, 190 10" fill="none" stroke="red" />',
polygon: '<polygon points="100 10,190 190, 10 190" fill="none" stroke="red" />',
rect: '<rect x="10" y="10" width="180" height="180" rx="0" ry="0" fill="none" stroke="red" />',
circle: '<circle cx="100" cy="100" r="90" fill="none" stroke="red" />',
ellipse: '<ellipse cx="100" cy="100" rx="90" ry="60" fill="none" stroke="red" />'
};
let eles = Object.keys(shapes); /* 图形标签数组 */
/*函数 : 代码着色*/
let colorCode = (code) => {
let reg = /([\'\"]([^\"]+)[\'\"])/g;
code = code.replaceAll('<', '<');
code = code.replaceAll('>', '>');
return (code.replace(reg,'<span class="rred">$1</span>'));
};
//函数 : 生成 path
let getEleData = () => {
let svgEle = document.querySelector('svg').children;
let tagName = svgEle.tagName.toLowerCase();
if(!eles.includes(tagName)) return;
let path, x, y, r, cx, cy, rx, ry, w, h;
switch(tagName) {
case 'line':
x = 1 * svgEle.getAttribute('x1');
y = 1 * svgEle.getAttribute('y1');
cx = 1 * svgEle.getAttribute('x2');
cy = 1 * svgEle.getAttribute('y2');
path = `M${x} ${y} L${cx} ${cy}`;
break;
case 'polyline':
case 'polygon':
path = 'M';
let points = svgEle.getAttribute('points');
let ar = points.replace(/[^\d.]/g, ',').split(',').filter(Number);
for(j = 0; j < ar.length; j ++) {
if(j <= 1) {
path += ar + ' ';
}else{
path += (j % 2 === 0 ? 'L' + ar : ar) + ' ';
}
}
path += tagName === 'polygon' ? 'z' : '';
break;
case 'rect':
x = 1 * svgEle.getAttribute('x');
y = 1 * svgEle.getAttribute('y');
w = 1* svgEle.getAttribute('width');
h = 1* svgEle.getAttribute('height');
rx = svgEle.getAttribute('rx') ? 1 * svgEle.getAttribute('rx') : 1 * svgEle.getAttribute('ry');
ry = svgEle.getAttribute('ry') ? 1 * svgEle.getAttribute('ry') : 1 * svgEle.getAttribute('rx');
if(rx > w / 2) rx = w / 2;
if(ry > h / 2) ry = h / 2;
path = (rx === 0 || ry === 0)
? `M${x} ${y} h${w} v${h} h-${w} v-${h}`
: `M${x} ${y + ry}
a${rx} ${ry} 0 0 1 ${rx} -${ry}
h${w - rx * 2}
a${rx} ${ry} 0 0 1 ${rx} ${ry}
v${h - ry * 2}
a${rx} ${ry} 0 0 1 -${rx} ${ry}
h-${w - rx * 2}
a${rx} ${ry} 0 0 1 -${rx} -${ry}
v-${h - ry * 2}`;
break;
case 'circle':
cx = 1 * svgEle.getAttribute('cx');
cy = 1 * svgEle.getAttribute('cy');
r = 1 * svgEle.getAttribute('r');
path = `
M${cx-r} ${cy}
A${r} ${r} 0 1 1 ${(cx + r)} ${cy}
A${r} ${r} 0 1 1 ${cx-r} ${cy}
`;
break;
case 'ellipse':
cx = 1 * svgEle.getAttribute('cx');
cy = 1 * svgEle.getAttribute('cy');
rx = 1 * svgEle.getAttribute('rx');
ry = 1 * svgEle.getAttribute('ry');
path = `
M${cx-rx} ${cy}
A${rx} ${ry} 0 1 1 ${(cx + rx)} ${cy}
A${rx} ${ry} 0 1 1 ${cx-rx} ${cy}
`;
break;
}
return path;
};
/* select初始化 : 生成option菜单 */
(function() {
let hcode = '';
for(j = 0; j < eles.length; j++) {
hcode += `<option value="${j}">${eles}</option>`;
}
ssShape.innerHTML += hcode;
})();
/* 确定按钮 : 生成图形 */
btnOk.onclick = () => {
if(!eCode.innerText) return;
svgShape.innerHTML = eCode.innerText;
if(getEleData()) cPath.setAttribute('d', getEleData());
pathData.innerText = cPath.getAttribute('d').replaceAll('\n',' ');
};
/* select按钮 : 选择基本图形 */
ssShape.onchange = () => {
let code = '<svg width="200" height="200">\n\t';
let idx = ssShape.options.value;
code += shapes] + '\n<svg>';
eCode.innerHTML = code;
}
/* 着色按钮点击 : 代码上色 */
eCode.onfocus = btnColor.onclick = () => eCode.innerHTML = colorCode(eCode.innerText);
eCode.onmouseover = () => eCode.focus();
const resizeObserver = new ResizeObserver(() => {
svgPath.style.width = svgShape.offsetWidth + 'px';
svgPath.style.height =svgShape.offsetHeight + 'px';
});
resizeObserver.observe(svgShape);
</script>
这个厉害了,能把这些基本图形都转换成路径呢{:4_199:} 矩形也可以用多边形来写的吧,麻烦点而已{:4_173:} 这些黑黑都单独讲解过,这个是在线直接转换,特别方便。可以自己输入数值,直接看效果了,太好了{:4_199:} 红影 发表于 2023-10-2 20:28
这个厉害了,能把这些基本图形都转换成路径呢
代码也不算复杂吧:CSS和HTML有耐心就能做;JS算法略微有点深,构思需要一点技巧。 红影 发表于 2023-10-2 20:29
矩形也可以用多边形来写的吧,麻烦点而已
既然存在这样的标签,那就以用它、处理它。 红影 发表于 2023-10-2 20:30
这些黑黑都单独讲解过,这个是在线直接转换,特别方便。可以自己输入数值,直接看效果了,太好了
唯独 line 标签没讲到,不过它很简单,通过这个小程序,马上能学会 https://pic.imgdb.cn/item/651adc45c458853aef1b047e.jpg
这高科技的代码,我是看不懂,送一串芭蕉慰劳一下吧{:4_189:}
上海朝阳 发表于 2023-10-2 21:05
这高科技的代码,我是看不懂,送一串芭蕉慰劳一下吧
谢谢,等几天才能吃{:4_170:} 图里面打入line,下面没有看见路径出来 line
polyline
polygon
rect
circle
ellipse 我一个个都输入了,路径没有转出来{:4_203:}
我可能错了? 小辣椒 发表于 2023-10-2 22:36
图里面打入line,下面没有看见路径出来
代码要完整。你可以选择一个标签,就有完整的代码,然后修改相关数据,也可以不修改,最后点击一下确定按钮 小辣椒 发表于 2023-10-2 22:36
line
polyline
polygon
只输入标签单词是不行的,需要完整的svg代码。程序有选择基本图形,随便选一个,就会有完整结构的SVG代码出来 马黑黑 发表于 2023-10-2 22:41
只输入标签单词是不行的,需要完整的svg代码。程序有选择基本图形,随便选一个,就会有完整结构的SVG代码 ...
可以图形上去啊,那我再试 小辣椒 发表于 2023-10-2 22:44
可以图形上去啊,那我再试
一楼里,你需要瞧瞧怎么做