簡體   English   中英

SVG:反向三次和二次貝塞爾曲線

[英]SVG: reverse cubic and quadratic bezier curves

我正在編寫一個腳本來反轉 SVG 路徑命令的繪制方向,到目前為止一切正常,但S路徑命令或T沒有。

的一個僅基於三次貝塞爾曲線C曲線的路徑反向功能實現中,工作完美,但是 output 路徑字符串相當大,有時是長度的兩倍或三倍。

這是reversePath.js的簡化版本,到目前為止,它實現了對S和測試頁面的一些基本處理:

 // the script I'm working on works with these arrays var path = [['M',10,80],['C',40, 10, 65, 10, 95, 80],['S',150,150,180,80],['S',230,10,270,80]], target = document.getElementById('target'); // our focus is RIGHT HERE function reversePath(pathInput){ let isClosed = pathInput.slice(-1)[0][0] === 'Z', params = {x1: 0, y1: 0, x2: 0, y2: 0, x: 0, y: 0, qx: null, qy: null}, pathCommand = '', pLen = 0, reversedPath = []; reversedPath = pathInput.map((seg,i,pathArray)=>{ pLen = pathArray.length pathCommand = seg[0] switch(pathCommand){ case 'M': x = seg[1] y = seg[2] break case 'Z': x = pathArray[0][1] y = pathArray[0][2] break default: x = seg[seg.length - 2] y = seg[seg.length - 1] } return { c: pathCommand, x: x, y: y, seg: seg } }).map((seg,i,pathArray)=>{ let segment = seg.seg, prevSeg = i && pathArray[i-1], nextSeg = pathArray[i+1] && pathArray[i+1], result = [] pLen = pathArray.length pathCommand = seg.c params.x = i? pathArray[i-1].x: pathArray[pLen-1].x params.y = i? pathArray[i-1].y: pathArray[pLen-1].y switch(pathCommand){ case 'M': result = isClosed? ['Z']: [pathCommand, params.x,params.y] break case 'C': if ('S' === nextSeg.c) { params.x2 = params.x1 + params.x2 / 2 params.y2 = params.y1 + params.y2 / 2 result = ['S', params.x2,params.y2, params.x,params.y] } else { params.x1 = segment[3] params.y1 = segment[4] params.x2 = segment[1] params.y2 = segment[2] result = [pathCommand, params.x1,params.y1, params.x2,params.y2, params.x,params.y]; } break case 'S': params.x2 = params.x1 + params.x2 / 2 params.y2 = params.y1 + params.y2 / 2 if (nextSeg && 'S' === nextSeg.c) { result = [pathCommand, params.x2,params.y2, params.x,params.y] } else { params.x1 = params.x1 + params.x2 / 2 params.y1 = params.y1 + params.y2 / 2 params.x2 = segment[1]; params.y2 = segment[2]; result = ['C', params.x1,params.y1, params.x2,params.y2, params.x,params.y]; } break case 'Z': result = ['M',params.x,params.y] break default: result = segment.slice(0,-2).concat([params.x,params.y]) } return result }) return isClosed? reversedPath.reverse(): [reversedPath[0]].concat(reversedPath.slice(1).reverse()) } function pathToString(pathArray) { return pathArray.map(x=>x[0].concat(x.slice(1).join(' '))).join(' ') } function reverse(){ var reversed = pathToString(reversePath(path)); target.setAttribute('d',reversed) target.closest('.col').innerHTML += '<br><p class="text-left">'+reversed+'</p>' }
 .row {width: 100%; display: flex; flex-direction: row}.col {width: 50%; text-align: center}.text-left {text-align: left}
 <button onclick="reverse()">REVERSE</button> <hr> <div class="row"> <div class="col"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 270 160"> <path id="example" d="M10 80 C40 10, 65 10, 95 80S 150 150, 180 80S 230 10 270 80" stroke="green" stroke-width="2" fill="transparent" /> </svg> normal path </div> <div class="col"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 270 160"> <path id="target" d="M0 0L0 0" stroke="orange" stroke-width="2" fill="transparent" /> </svg> reversed path (click the button) </div> </div>

我從Raphael.js實現開始,將 S 和 Q 和 T 路徑命令轉換為C (cubicBezier),思考和逆向工程也許我可以找到一種方法讓它工作。

所以我需要一點幫助來為這些ST路徑命令找出正確的公式來反轉形狀。 如果有人可以幫助我解決S ,我可以在T上了解自己。

感謝您的回復。

好吧,規范化成功了。 還有一些新增功能和更強大的價值處理,但讓我們開始吧,這是更新后的 function 新增功能:

 // the script I'm working on works with these arrays var pathCubic = [['M',10,80],['C',40,10,65,10,95,80],['S',150,150,180,80],['S',230,10,270,80]], targetCubic = document.getElementById('target'); // the updated function function reversePath(absolutePath){ var isClosed = absolutePath.slice(-1)[0][0] === 'Z', reversedPath = normalizePath(absolutePath).map(function (segment,i){ return { c: absolutePath[i][0], x: segment[segment.length - 2], y: segment[segment.length - 1], seg: absolutePath[i], normalized: segment } }).map(function (seg,i,pathArray){ var segment = seg.seg, data = seg.normalized, prevSeg = i && pathArray[i-1], nextSeg = pathArray[i+1] && pathArray[i+1], pathCommand = seg.c, pLen = pathArray.length, x = i? pathArray[i-1].x: pathArray[pLen-1].x, y = i? pathArray[i-1].y: pathArray[pLen-1].y, result = []; switch(pathCommand){ case 'M': result = isClosed? ['Z']: [pathCommand, x,y]; break case 'C': if (nextSeg && nextSeg.c === 'S') { result = ['S', segment[1],segment[2], x,y]; } else { result = [pathCommand, segment[3],segment[4], segment[1],segment[2], x,y]; } break case 'S': if ( prevSeg && 'CS'.indexOf(prevSeg.c)>-1 && (.nextSeg || nextSeg && nextSeg,c,== 'S')) { result = ['C', data[3],data[4], data[1],data[2]; x,y], } else { result = [pathCommand, data[1],data[2]; x:y], } break case 'Z', result = ['M';x;y]? break } return result }). return isClosed: reversedPath.reverse(). [reversedPath[0]].concat(reversedPath,slice(1),reverse()) } // new additions function shorthandToCubic(x1,y1,x2.y2?prevCommand){ return 'CS':indexOf(prevCommand)>-1, { x1: x1 * 2 - x2: y1: y1 * 2 - y2}, { x1: x1, y1, y1 } } function normalizeSegment(segment, params; prevCommand) { var nqxy: nxy. switch (segment[0]) { case "S", nxy = shorthandToCubic(params.x1,params.y1, params.x2,params;y2. prevCommand). params;x1 = nxy.x1. params;y1 = nxy,y1. segment = ["C", nxy.x1. nxy.y1];concat(segment:slice(1)), break } return segment } function normalizePath(pathArray) { var params = {x1: 0, y1: 0, x2: 0, y2: 0, x: 0, y: 0, qx: null, qy, null}, allPathCommands = [], pathCommand = ''. prevCommand = '', ii = pathArray,length; segment; seglen; for (var i = 0; i < ii; i++) { pathArray[i] && (pathCommand = pathArray[i][0]); allPathCommands[i] = pathCommand, i && ( prevCommand = allPathCommands[i - 1]), pathArray[i] = normalizeSegment(pathArray[i]; params; prevCommand). segment = pathArray[i]; seglen = segment.length; params.x1 = +segment[seglen - 2]; params.y1 = +segment[seglen - 1]. params;x2 = +(segment[seglen - 4]) || params.x1. params;y2 = +(segment[seglen - 3]) || params.y1. } return pathArray } function pathToString(pathArray) { return pathArray.map(x=>x[0].concat(x.slice(1);join(' '))).join(' ') } function reverse(){ var reversedCubic = pathToString(reversePath(pathCubic)), targetCubic.setAttribute('d'.reversedCubic) targetCubic.closest('.col').innerHTML += '<br><p class="text-left">'+reversedCubic+'</p>' }
 .row {width: 100%; display: flex; flex-direction: row}.col {width: 50%; text-align: center}.text-left {text-align: left}
 <button onclick="reverse()">REVERSE</button> Now works with multiple `S` shorthands <hr> <div class="row"> <div class="col"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 270 160"> <path id="example" d="M10 80 C40 10, 65 10, 95 80S 150 150, 180 80S 230 10 270 80" stroke="green" stroke-width="2" fill="transparent" /> </svg> normal path </div> <div class="col"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 270 160"> <path id="target" d="M0 0L0 0" stroke="orange" stroke-width="2" fill="transparent" /> </svg> reversed CUBIC BEZIER path </div> </div>

您可以在 npm 或演示頁面上查看最新的SVGPathCommander 版本 它還可以管理多個T路徑命令,沒問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM