首页 > 网站 > WEB开发 > 正文

ZRender源码分析6:Shape对象详解之路径

2024-04-27 14:18:43
字体:
来源:转载
供稿:网友

ZRender源码分析6:Shape对象详解之路径

开始

说到这里,就不得不提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>

好吧,得到的结果一模一样,我就不贴图了。不多说了,这就是移植,我喜欢。

_parsePathData

打开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]);
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表