马黑黑 发表于 2023-10-2 18:01

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="&gt;&gt; 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: '&lt;line x1="10" y1="10" x2="190" y2="190" fill="none" stroke="red" /&gt;',
        polyline: '&lt;polyline points="10 10,10 190,100 10, 190 10" fill="none" stroke="red" /&gt;',
        polygon: '&lt;polygon points="100 10,190 190, 10 190" fill="none" stroke="red" /&gt;',
        rect: '&lt;rect x="10" y="10" width="180" height="180" rx="0" ry="0" fill="none" stroke="red" /&gt;',
        circle: '&lt;circle cx="100" cy="100" r="90" fill="none" stroke="red" /&gt;',
        ellipse: '&lt;ellipse cx="100" cy="100" rx="90" ry="60" fill="none" stroke="red" /&gt;'
};

let eles = Object.keys(shapes); /* 图形标签数组 */

/*函数 : 代码着色*/
let colorCode = (code) => {
        let reg = /([\'\"]([^\"]+)[\'\"])/g;
        code = code.replaceAll('<', '&lt;');
        code = code.replaceAll('>', '&gt;');
        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 = '&lt;svg width="200" height="200"&gt;\n\t';
        let idx = ssShape.options.value;
        code += shapes] + '\n&lt;svg&gt;';
        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>

马黑黑 发表于 2023-10-2 18:05

首发链接:

http://mhh.52qingyin.cn/api/svg/svg2path.html

马黑黑 发表于 2023-10-2 18:23

【说明】

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>

马黑黑 发表于 2023-10-2 20:23

20:22 修正原图与路径图绝对同步问题

马黑黑 发表于 2023-10-2 20:25

本帖最后由 马黑黑 于 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="&gt;&gt; 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: '&lt;line x1="10" y1="10" x2="190" y2="190" fill="none" stroke="red" /&gt;',
        polyline: '&lt;polyline points="10 10,10 190,100 10, 190 10" fill="none" stroke="red" /&gt;',
        polygon: '&lt;polygon points="100 10,190 190, 10 190" fill="none" stroke="red" /&gt;',
        rect: '&lt;rect x="10" y="10" width="180" height="180" rx="0" ry="0" fill="none" stroke="red" /&gt;',
        circle: '&lt;circle cx="100" cy="100" r="90" fill="none" stroke="red" /&gt;',
        ellipse: '&lt;ellipse cx="100" cy="100" rx="90" ry="60" fill="none" stroke="red" /&gt;'
};

let eles = Object.keys(shapes); /* 图形标签数组 */

/*函数 : 代码着色*/
let colorCode = (code) => {
        let reg = /([\'\"]([^\"]+)[\'\"])/g;
        code = code.replaceAll('<', '&lt;');
        code = code.replaceAll('>', '&gt;');
        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 = '&lt;svg width="200" height="200"&gt;\n\t';
        let idx = ssShape.options.value;
        code += shapes] + '\n&lt;svg&gt;';
        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>


红影 发表于 2023-10-2 20:28

这个厉害了,能把这些基本图形都转换成路径呢{:4_199:}

红影 发表于 2023-10-2 20:29

矩形也可以用多边形来写的吧,麻烦点而已{:4_173:}

红影 发表于 2023-10-2 20:30

这些黑黑都单独讲解过,这个是在线直接转换,特别方便。可以自己输入数值,直接看效果了,太好了{:4_199:}

马黑黑 发表于 2023-10-2 20:33

红影 发表于 2023-10-2 20:28
这个厉害了,能把这些基本图形都转换成路径呢

代码也不算复杂吧:CSS和HTML有耐心就能做;JS算法略微有点深,构思需要一点技巧。

马黑黑 发表于 2023-10-2 20:35

红影 发表于 2023-10-2 20:29
矩形也可以用多边形来写的吧,麻烦点而已

既然存在这样的标签,那就以用它、处理它。

马黑黑 发表于 2023-10-2 20:35

红影 发表于 2023-10-2 20:30
这些黑黑都单独讲解过,这个是在线直接转换,特别方便。可以自己输入数值,直接看效果了,太好了

唯独 line 标签没讲到,不过它很简单,通过这个小程序,马上能学会

上海朝阳 发表于 2023-10-2 21:05

https://pic.imgdb.cn/item/651adc45c458853aef1b047e.jpg

这高科技的代码,我是看不懂,送一串芭蕉慰劳一下吧{:4_189:}

马黑黑 发表于 2023-10-2 22:21

上海朝阳 发表于 2023-10-2 21:05
这高科技的代码,我是看不懂,送一串芭蕉慰劳一下吧

谢谢,等几天才能吃{:4_170:}

小辣椒 发表于 2023-10-2 22:36

图里面打入line,下面没有看见路径出来

小辣椒 发表于 2023-10-2 22:36

line
polyline
polygon
rect
circle
ellipse

小辣椒 发表于 2023-10-2 22:38

我一个个都输入了,路径没有转出来{:4_203:}

我可能错了?

马黑黑 发表于 2023-10-2 22:39

小辣椒 发表于 2023-10-2 22:36
图里面打入line,下面没有看见路径出来

代码要完整。你可以选择一个标签,就有完整的代码,然后修改相关数据,也可以不修改,最后点击一下确定按钮

马黑黑 发表于 2023-10-2 22:41

小辣椒 发表于 2023-10-2 22:36
line
polyline
polygon


只输入标签单词是不行的,需要完整的svg代码。程序有选择基本图形,随便选一个,就会有完整结构的SVG代码出来

小辣椒 发表于 2023-10-2 22:44

马黑黑 发表于 2023-10-2 22:41
只输入标签单词是不行的,需要完整的svg代码。程序有选择基本图形,随便选一个,就会有完整结构的SVG代码 ...

可以图形上去啊,那我再试

马黑黑 发表于 2023-10-2 22:44

小辣椒 发表于 2023-10-2 22:44
可以图形上去啊,那我再试

一楼里,你需要瞧瞧怎么做
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: SVG基本图形转路径