说到这里,就不得不提SVG的路径操作了,因为ZRender完全的模拟了SVG原生的path元素的用法,很是强大。 关于SVG的Path,请看这里: Path (英文版) 或者 【MDN】SVG教程(5) 路径 [译] (中文版), 很明显的是canvas中的路径没有SVG的用着舒服,那到底ZRender是如何实现的呢,让我给你娓娓道来(不过要想继续进行下去,上面的SVG的PATH必须了解。)。
打开API,shape.path,可以看到,path的配置有MLHVCSQTZ等字母组成的字符串,svg的path也支持小写,也有一个A命令,难道ZRender没有实现? 错,实现了,只是在API上没有写明而已,支持大小写,支持A(圆弧)命令!为了证明我所说,来个示例:
require([ '../src/zrender', '../src/shape/Path'], function( zrender, PathShape ){var box = document.getElementById('box');var zr = zrender.init(box);zr.addShape(new PathShape({style:{x: 0,y: 0,path: 'M80 80 A 45 45, 0, 0, 0, 125 125 L 125 80 Z',color: '#F60',textPosition: 'inside',textColor: 'red',strokeColor: 'black'},draggable: true}));zr.addShape(new PathShape({style:{x: 0,y: 0,path: 'M230 80 A 45 45, 0, 1, 0, 275 125 L 275 80 Z',color: '#F60',textPosition: 'inside',textColor: 'red',strokeColor: 'black'},draggable: true}));zr.render();});
得到如下结果:
再用SVG来一个相同配置的:
<svg version="1.1" basePRofile="full" xmlns="http://www.w3.org/2000/svg"> <path d="M80 80 A 45 45, 0, 0, 0, 125 125 L 125 80 Z" fill="#F60"/> <path d="M230 80 A 45 45, 0, 1, 0, 275 125 L 275 80 Z" fill="#F60"/></svg>
好吧,得到的结果一模一样,我就不贴图了。不多说了,这就是移植,我喜欢。
打开zrender/shape/Path,buildPath先调用的就是_parsePathData,作用为:解析path字符串为数组命令,也就是个解析器嘛。
_parsePathData : function(data) { if (!data) { return []; } // command string var cs = data; // command chars var cc = [ 'm', 'M', 'l', 'L', 'v', 'V', 'h', 'H', 'z', 'Z', 'c', 'C', 'q', 'Q', 't', 'T', 's', 'S', 'a', 'A' ]; cs = cs.replace(/-/g, ' -');// M 100 100 L 100 200 L 100-200 Z -> M 100 100 L 100 200 L 100 -200 Z cs = cs.replace(/ /g, ' ');// M 100 100 L 100 200 L 100 -200 -> M 100 100 L 100 200 L 100 -200 -> M 100 100 L 100 200 L 100 -200 cs = cs.replace(/ /g, ',');// M 100 100 L 100 200 L 100 -200 -> M,100,100,L,100,200,L,100,-200 cs = cs.replace(/,,/g, ',');//如果出现两个逗号,换成一个逗号 -> M,100,100,L,100,200,L,100,-200 //cs = cs.replace(/-/g, ' -').replace(/ /g, ' ').replace(/ /g, ',').replace(/,,/g, ','); 这样写,会不会很帅气,(- var n; // create pipes so that we can split the data for (n = 0; n < cc.length; n++) { cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]); } // |M,100,100,|L,100,200,|L,100,-200 // create array var arr = cs.split('|'); // ['','M,100,100,','L,100,200,','L,100,-200'] var ca = []; // init context point var cpx = 0; //cpx和cpy是循环里的全局都在使用,小写命令是累计计算,大写命令是复制计算。 var cpy = 0; for (n = 1; n < arr.length; n++) { // 从1开始,因为第一个元素肯定为空 var str = arr[n]; // M,100,100, var c = str.charAt(0); // M str = str.slice(1); //,100,100, str = str.replace(new RegExp('e,-', 'g'), 'e-'); var p = str.split(',');// ['','100','100',''] if (p.length > 0 && p[0] === '') { p.shift(); } // ['100','100',''] for (var i = 0; i < p.length; i++) { p[i] = parseFloat(p[i]); } // [100,100,NaN] while (p.length > 0) { if (isNaN(p[0])) { break; } var cmd = null; var points = []; var ctlPtx; var ctlPty; var prevCmd; var rx; var ry; var psi; var fa; var fs; var x1 = cpx; var y1 = cpy; // convert l, H, h, V, and v to L switch (c) { case 'l': cpx += p.shift(); cpy += p.shift(); cmd = 'L'; points.push(cpx, cpy); break; case 'L': cpx = p.shift(); cpy = p.shift(); points.push(cpx, cpy); break; //在l的时候,是直接相加的,而L的时候,是直接赋值的 ,这就说明大小写是不一样的 // L 表示lineTo case 'm': cpx += p.shift(); cpy += p.shift(); cmd = 'M'; points.push(cpx, cpy); c = 'l'; break; case 'M': cpx = p.shift(); cpy = p.shift(); cmd = 'M'; points.push(cpx, cpy); c = 'L'; break;// M 表示moveTo case 'h': cpx += p.shift(); cmd = 'L'; points.push(cpx, cpy); break; case 'H': cpx = p.shift(); cmd = 'L'; points.push(cpx, cpy); break; // H 表示水平lineTo,只改变X值 case 'v': cpy += p.shift(); cmd = 'L'; points.push(cpx, cpy); break; case 'V': cpy = p.shift(); cmd = 'L'; points.push(cpx, cpy); break; // H 表示垂直lineTo,只改变Y值 case 'C': points.push(p.shift(), p.shift(), p.shift(), p.shift()); cpx = p.shift(); cpy = p.shift(); points.push(cpx, cpy); break; case 'c': points.push( cpx + p.shift(), cpy + p.shift(), cpx + p.shift(), cpy + p.shift() ); cpx += p.shift(); cpy += p.shift(); cmd = 'C'; points.push(cpx, cpy); break; // C表示二次贝塞尔曲线 case 'S': ctlPtx = cpx; ctlPty = cpy; prevCmd = ca[ca.length - 1]; if (prevCmd.command === 'C') { ctlPtx = cpx + (cpx - prevCmd.points[2]); ctlPty = cpy + (cpy - prevCmd.points[3]); } points.push(ctlPtx, ctlPty, p.shift(), p.shift()); cpx = p.shift(); cpy = p.shift(); cmd = 'C'; points.push(cpx, cpy); break; case 's': ctlPtx = cpx, ctlPty = cpy; prevCmd = ca[ca.length - 1]; if (prevCmd.command === 'C') { ctlPtx = cpx + (cpx - prevCmd.points[2]); ctlPty = cpy + (cpy - prevCmd.points[3]); } points.push( ctlPtx, ctlPty, cpx + p.shift(), cpy + p.shift() ); cpx += p.shift(); cpy += p.shift(); cmd = 'C'; points.push(cpx, cpy); break; // C表示光滑二次贝塞尔曲线 case 'Q': points.push(p.shift(), p.shift()); cpx = p.shift(); cpy = p.shift(); points.push(cpx, cpy); break; case 'q': points.push(cpx + p.shift(), cpy + p.shift()); cpx += p.shift(); cpy += p.shift(); cmd = 'Q'; points.push(cpx, cpy); break; // Q表示三次贝塞尔曲线 case 'T': ctlPtx = cpx, ctlPty = cpy; prevCmd = ca[ca.length - 1]; if (prevCmd.command === 'Q') { ctlPtx = cpx + (cpx - prevCmd.points[0]); ctlPty = cpy + (cpy - prevCmd.points[1]); } cpx = p.shift(); cpy = p.shift(); cmd = 'Q'; points.push(ctlPtx, ctlPty, cpx, cpy); break; case 't': ctlPtx = cpx, ctlPty = cpy; prevCmd = ca[ca.length - 1]; if (prevCmd.command === 'Q') { ctlPtx = cpx + (cpx - prevCmd.points[0]);
新闻热点
疑难解答