UNPKG

89.5 kBJavaScriptView Raw
1
2 /*!
3 * image2D - 🍇 使用ECMAScript绘制二维图片。Drawing Two-Dimensional Pictures Using ECMAScript.
4 * git+https://github.com/yelloxing/image2D.git
5 *
6 * author 心叶
7 *
8 * version 1.4.0
9 *
10 * build Thu Apr 11 2019
11 *
12 * Copyright yelloxing
13 * Released under the MIT license
14 *
15 * Date:Fri Sep 06 2019 23:09:26 GMT+0800 (中国标准时间)
16 */
17
18'use strict';
19
20var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
21
22(function () {
23 'use strict';
24
25 var toString = Object.prototype.toString;
26
27 /**
28 * 获取一个值的类型字符串[object type]
29 *
30 * @private
31 * @param {*} value 需要返回类型的值
32 * @returns {string} 返回类型字符串
33 */
34 function getType(value) {
35 if (value == null) {
36 return value === undefined ? '[object Undefined]' : '[object Null]';
37 }
38 return toString.call(value);
39 }
40
41 /**
42 * 判断一个值是不是一个朴素的'对象'
43 *
44 * @private
45 * @param {*} value 需要判断类型的值
46 * @returns {boolean} 如果是朴素的'对象'返回true,否则返回false
47 */
48
49 function isPlainObject(value) {
50 if (value === null || (typeof value === 'undefined' ? 'undefined' : _typeof(value)) !== 'object' || getType(value) != '[object Object]') {
51 return false;
52 }
53
54 // 如果原型为null
55 if (Object.getPrototypeOf(value) === null) {
56 return true;
57 }
58
59 var proto = value;
60 while (Object.getPrototypeOf(proto) !== null) {
61 proto = Object.getPrototypeOf(proto);
62 }
63 return Object.getPrototypeOf(value) === proto;
64 }
65
66 /**
67 * 判断一个值是不是结点元素。
68 *
69 * @since V0.1.2
70 * @public
71 * @param {*} value 需要判断类型的值
72 * @returns {boolean} 如果是结点元素返回true,否则返回false
73 */
74 function isElement(value) {
75 return value !== null && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' && (value.nodeType === 1 || value.nodeType === 9 || value.nodeType === 11) && !isPlainObject(value);
76 }
77
78 /**
79 * 判断一个值是不是Object。
80 *
81 * @since V0.1.2
82 * @public
83 * @param {*} value 需要判断类型的值
84 * @returns {boolean} 如果是Object返回true,否则返回false
85 */
86 function isObject(value) {
87 var type = typeof value === 'undefined' ? 'undefined' : _typeof(value);
88 return value != null && (type === 'object' || type === 'function');
89 }
90
91 /**
92 * 判断一个值是不是Function。
93 *
94 * @since V0.1.2
95 * @public
96 * @param {*} value 需要判断类型的值
97 * @returns {boolean} 如果是Function返回true,否则返回false
98 */
99 function isFunction(value) {
100 if (!isObject(value)) {
101 return false;
102 }
103
104 var type = getType(value);
105 return type === '[object Function]' || type === '[object AsyncFunction]' || type === '[object GeneratorFunction]' || type === '[object Proxy]';
106 }
107
108 /**
109 * 初始化配置文件
110 * @param {Json} init 默认值
111 * @param {Json} data
112 * @return {Json}
113 */
114 var initConfig = function initConfig(init, data) {
115 for (var key in data) {
116 try {
117 init[key] = data[key];
118 } catch (e) {
119 throw new Error("Illegal property value!");
120 }
121 }return init;
122 };
123
124 // 命名空间路径
125 var NAMESPACE = {
126 "svg": "http://www.w3.org/2000/svg",
127 "xhtml": "http://www.w3.org/1999/xhtml",
128 "xlink": "http://www.w3.org/1999/xlink",
129 "xml": "http://www.w3.org/XML/1998/namespace",
130 "xmlns": "http://www.w3.org/2000/xmlns/"
131 };
132
133 // 正则表达式
134 var REGEXP = {
135
136 // 空白字符:http://www.w3.org/TR/css3-selectors/#whitespace
137 "whitespace": "[\\x20\\t\\r\\n\\f]",
138
139 // 空格外的空白字符
140 "blank": "[\\n\\f\\r]",
141
142 // 标志符:http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
143 "identifier": "(?:\\\\.|[\\w-]|[^\0-\\xa0])+"
144 };
145
146 // 记录需要使用xlink命名空间常见的xml属性
147 var XLINK_ATTRIBUTE = ["href", "title", "show", "type", "role", "actuate"];
148
149 /**
150 * 判断一个值是不是文本结点。
151 *
152 * @since V0.1.2
153 * @public
154 * @param {*} value 需要判断类型的值
155 * @returns {boolean} 如果是结点元素返回true,否则返回false
156 */
157 function isText(value) {
158 return value !== null && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' && value.nodeType === 3 && !isPlainObject(value);
159 }
160
161 /**
162 * 设置svg字符串
163 * @param {dom} target
164 * @param {string} svgstring
165 */
166 var setSVG = function setSVG(target, svgstring) {
167 if ('innerHTML' in SVGElement.prototype === false || 'innerHTML' in SVGSVGElement.prototype === false) {
168 var frame = document.createElement("div");
169 frame.innerHTML = svgstring;
170 var toSvgNode = function toSvgNode(htmlNode) {
171 var svgNode = document.createElementNS(NAMESPACE.svg, htmlNode.tagName.toLowerCase());
172 var attrs = htmlNode.attributes,
173 i = void 0;
174 for (i = 0; attrs && i < attrs.length; i++) {
175 if (XLINK_ATTRIBUTE.indexOf(attrs[i].nodeName) >= 0) {
176 // 针对特殊的svg属性,追加命名空间
177 svgNode.setAttributeNS(NAMESPACE.xlink, 'xlink:' + attrs[i].nodeName, htmlNode.getAttribute(attrs[i].nodeName));
178 } else {
179 svgNode.setAttribute(attrs[i].nodeName, htmlNode.getAttribute(attrs[i].nodeName));
180 }
181 }
182 return svgNode;
183 };
184 var rslNode = toSvgNode(frame.firstChild);
185 (function toSVG(pnode, svgPnode) {
186 var node = pnode.firstChild;
187 if (isText(node)) {
188 svgPnode.textContent = pnode.innerText;
189 return;
190 }
191 while (node) {
192 var svgNode = toSvgNode(node);
193 svgPnode.appendChild(svgNode);
194 if (node.firstChild) toSVG(node, svgNode);
195 node = node.nextSibling;
196 }
197 })(frame.firstChild, rslNode);
198 target.appendChild(rslNode);
199 } else {
200 // 如果当前浏览器提供了svg类型结点的innerHTML,我们还是使用浏览器提供的
201 target.innerHTML = svgstring;
202 }
203 };
204
205 // 变成指定类型的结点
206 // type可以取:
207 // 1.'HTML',html结点
208 // 2.'SVG',svg结点(默认值)
209 var toNode = function toNode(template, type) {
210 var frame = void 0,
211 childNodes = void 0;
212 if (type === 'html' || type === 'HTML') {
213 frame = document.createElement("div");
214 frame.innerHTML = template;
215
216 // 比如tr标签,它应该被tbody或thead包含
217 // 这里容器是div,这类标签无法生成
218 if (!/</.test(frame.innerHTML)) {
219 throw new Error('This template cannot be generated using div as a container:' + template);
220 }
221 } else {
222 frame = document.createElementNS(NAMESPACE.svg, 'svg');
223 // 部分浏览器svg元素没有innerHTML
224 setSVG(frame, template);
225 }
226 childNodes = frame.childNodes;
227 for (var i = 0; i < childNodes.length; i++) {
228 if (isElement(childNodes[i])) return childNodes[i];
229 }
230 };
231
232 /**
233 * 变成结点
234 * @param {string} template
235 * @param {string} type
236 * @return {dom} 返回结点
237 */
238 function toNode$1(template, type) {
239
240 // 把传递元素类型和标记进行统一处理
241 if (new RegExp("^" + REGEXP.identifier + "$").test(template)) template = "<" + template + "></" + template + ">";
242
243 var mark = /<([^>]+)>.*/.exec(template)[1];
244
245 // 除了画布canvas,其余默认svg标签
246 if ("canvas" === mark.toLowerCase()) type = 'HTML';
247
248 return toNode(template, type);
249 }
250
251 /**
252 * 在指定上下文查找结点
253 * @param {string|dom|array|function|image2D} selector 选择器,必输
254 * @param {dom|'html'|'svg'} context 查找上下文,或标签类型,必输
255 * @return {array|image2D} 结点数组
256 *
257 * 特别注意:
258 * 1.id选择器或者传入的是维护的结点,查找上下文会被忽略
259 * 2.如果selector传入的是一个字符串模板,context可选,其表示模板类型
260 */
261 function sizzle(selector, context) {
262
263 // 如果是字符串
264 // context如果是字符串(应该是'html'或'svg')表示这是生成结点,也走这条路线
265 if (typeof context == 'string' || typeof selector === 'string') {
266 selector = selector.trim().replace(new RegExp(REGEXP.blank, 'g'), '');
267
268 // 如果以'<'开头表示是字符串模板
269 if (typeof context == 'string' || /^</.test(selector)) {
270 var node = toNode$1(selector, context);
271 if (isElement(node)) return [node];else return [];
272 }
273
274 // *表示查找全部
275 else if (selector === '*') {
276 return context.getElementsByTagName('*');
277 }
278
279 var id = selector.match(new RegExp('#' + REGEXP.identifier, 'g'));
280 // ID选择器
281 // 此选择器会忽略上下文
282 if (id) {
283 var _node = document.getElementById(id[0].replace('#', ''));
284 if (isElement(_node)) return [_node];else return [];
285 }
286
287 var cls = selector.match(new RegExp('\\.' + REGEXP.identifier, 'g')),
288 tag = selector.match(new RegExp('^' + REGEXP.identifier));
289
290 // 结点和class混合选择器
291 if (tag || cls) {
292 var allNodes = context.getElementsByTagName(tag ? tag[0] : "*"),
293 temp = [];
294 for (var i = 0; i < allNodes.length; i++) {
295 var clazz = " " + allNodes[i].getAttribute('class') + " ",
296 flag = true;
297 for (var j = 0; cls && j < cls.length; j++) {
298 if (!clazz.match(" " + cls[j].replace('.', '') + " ")) {
299 flag = false;
300 break;
301 }
302 }
303 if (flag) temp.push(allNodes[i]);
304 }
305 return temp;
306 }
307
308 // 未知情况,报错
309 else {
310 throw new Error('Unsupported selector:' + selector);
311 }
312 }
313
314 // 如果是结点
315 else if (isElement(selector)) {
316 return [selector];
317 }
318
319 // 如果是数组
320 // 数组中的内容如果不是结点会直接被忽略
321 else if (selector && (selector.constructor === Array || selector.constructor === HTMLCollection || selector.constructor === NodeList)) {
322 var _temp = [];
323 for (var _i = 0; _i < selector.length; _i++) {
324 if (isElement(selector[_i])) _temp.push(selector[_i]);
325 }
326 return _temp;
327 }
328
329 // 如果是image2D对象
330 else if (selector && selector.constructor === image2D) {
331 return selector;
332 }
333
334 // 如果是函数
335 else if (isFunction(selector)) {
336 var _allNodes = context.getElementsByTagName('*'),
337 _temp2 = [];
338 for (var _i2 = 0; _i2 < _allNodes.length; _i2++) {
339 // 如果选择器函数返回true,表示当前面对的结点被接受
340 if (selector(_allNodes[_i2])) _temp2.push(_allNodes[_i2]);
341 }
342 return _temp2;
343 }
344
345 // 未知情况,报错
346 else {
347 throw new Error('Unknown selector:' + selector);
348 }
349 }
350
351 /**
352 * 设计需求是:
353 * image2D和image2D(selector[, context])
354 * 分别表示绘图类和绘图对象
355 *
356 * 题外:为什么不选择image2D和new image2D(selector[, context])?
357 * 只是感觉没有前面的写法用起来简洁
358 *
359 * 为了实现需求,第一反应是:
360 * let image2D=function(selector,context){
361 * return new image2D();
362 * };
363 *
364 * 在image2D上挂载静态方法,在image2D.prototype上挂载对象方法,
365 * 看起来稳的很,其实这明显是一个死循环。
366 *
367 * 为了解决这个问题,我们在image2D的原型上定义了一个方法:
368 * image2D.prototype.init=function(selector,context){
369 * return this;
370 * };
371 *
372 * 执行下面的方法:
373 * let temp=image2D.prototype.init(selector, context);
374 * 上面返回的temp很明显就是image2D.prototype,其实就是image2D对象
375 * (例如:new A(),其实就是取A.prototype,这样对比就很好理解了)
376 *
377 * 因此可以改造代码如下:
378 *
379 * 这样image2D和new image2D(selector[, context])就分别表示类和对象。
380 *
381 * 问:看起来是不是实现了?
382 * 答:是的,实现了。
383 * 问:可是总感觉有点不好,说不出为什么。
384 * 答:是不是感觉image2D()打印出来的东西有点多?
385 * 问:是的。
386 *
387 * 事实上,因为直接取image2D.prototype作为new image2D(),
388 * 理论上说,使用上区别不大,唯一不足的是,
389 * 挂载在image2D.prototype上的方法会在打印image2D对象的时候看见,不舒服。
390 *
391 * 为了看起来好看些,代码再次改造:
392 * let image2D = function (selector, context) {
393 * return new image2D.prototype.init(selector, context);
394 * };
395 *
396 * 为了让image2D(selector, context)返回的是image2D对象,需要修改image2D.prototype.init的原型:
397 * image2D.prototype.init.prototype = image2D.prototype;
398 *
399 * 这样:
400 * image2D(selector, context) ==
401 * return new image2D.prototype.init(selector, context) ==
402 * image2D.prototype.init.prototype ==
403 * image2D.prototype ==
404 * new image2D(selector, context)
405 *
406 * 此时需求就实现了,
407 * 而且打印image2D(selector, context)的时候,
408 * 对象上的方法都在原型上,看起来就比较舒服了。
409 */
410
411 var image2D = function image2D(selector, context) {
412 return new image2D.prototype.init(selector, context);
413 };
414
415 image2D.prototype.init = function (selector, context) {
416
417 // 如果没有传递,默认使用document作为上下文
418 this.context = context = context || document;
419
420 // 使用sizzle获取需要维护的结点,并把结点维护到image2D对象中
421 var nodes = sizzle(selector, context),
422 flag = void 0;
423 for (flag = 0; flag < nodes.length; flag++) {
424 this[flag] = nodes[flag];
425 }
426
427 // 设置结点个数
428 this.length = nodes.length;
429 return this;
430 };
431
432 // 扩展方法
433 // 在image2D和image2D.prototype上分别调用extend方法就可以在类和对象上扩展方法了
434 image2D.prototype.extend = image2D.extend = function () {
435
436 var target = arguments[0] || {};
437 var source = arguments[1] || {};
438 var length = arguments.length;
439
440 /*
441 * 确定复制目标和源
442 */
443 if (length === 1) {
444 //如果只有一个参数,目标对象是自己
445 source = target;
446 target = this;
447 }
448 if (!isObject(target)) {
449 //如果目标不是对象或函数,则初始化为空对象
450 target = {};
451 }
452
453 /*
454 * 复制属性到对象上面
455 */
456 for (var key in source) {
457 try {
458 target[key] = source[key];
459 } catch (e) {
460
461 // 为什么需要try{}catch(e){}?
462 // 一些对象的特殊属性不允许覆盖,比如name
463 // 执行:image2D.extend({'name':'新名称'})
464 // 会抛出TypeError
465 throw new Error("Illegal property value!");
466 }
467 }
468
469 return target;
470 };
471
472 image2D.prototype.init.prototype = image2D.prototype;
473
474 /**
475 * 无论绘制的树结构是什么样子的
476 * 计算时都假想目标树的样子如下:
477 * 1.根结点在最左边,且上下居中
478 * 2.树是从左往右生长的结构
479 * 3.每个结点都是一块1*1的正方形,top和left分别表示正方形中心的位置
480 * @since V0.2.0
481 * @public
482 */
483 function treeLayout() {
484
485 var config = {},
486
487 // 维护的树
488 alltreedata = void 0,
489
490 // 根结点ID
491 rootid = void 0;
492
493 /**
494 * 把内部保存的树结点数据
495 * 计算结束后会调用配置的绘图方法
496 */
497 var update = function update() {
498
499 var beforeDis = [],
500 size = 0,
501 maxDeep = 0;
502 (function positionCalc(pNode, deep) {
503
504 if (deep > maxDeep) maxDeep = deep;
505 var flag = void 0;
506 for (flag = 0; flag < pNode.children.length; flag++) {
507 // 因为全部的子结点的位置确定了,父结点的y位置就是子结点的中间位置
508 // 因此有子结点的,先计算子结点
509 positionCalc(alltreedata[pNode.children[flag]], deep + 1);
510 } // left的位置比较简单,deep从0开始编号
511 // 比如deep=0,第一层,left=0+0.5=0.5,也就是根结点
512 alltreedata[pNode.id].left = deep + 0.5;
513 if (flag == 0) {
514
515 // beforeDis是一个数组,用以记录每一层此刻top下边缘(每一层是从上到下)
516 // 比如一层的第一个,top值最小可以取top=0.5
517 // 为了方便计算,beforeDis[deep] == undefined的时候表示现在准备计算的是这层的第一个结点
518 // 因此设置最低上边缘为-0.5
519 if (beforeDis[deep] == undefined) beforeDis[deep] = -0.5;
520 // 父边缘同意的进行初始化
521 if (beforeDis[deep - 1] == undefined) beforeDis[deep - 1] = -0.5;
522
523 // 添加的新结点top值第一种求法:本层上边缘+1(比如上边缘是-0.5,那么top最小是top=-0.5+1=0.5)
524 alltreedata[pNode.id].top = beforeDis[deep] + 1;
525
526 var pTop = beforeDis[deep] + 1 + (alltreedata[pNode.pid].children.length - 1) * 0.5;
527 // 计算的原则是:如果第一种可行,选择第一种,否则必须选择第二种
528 // 判断第一种是否可行的方法就是:如果第一种计算后确定的孩子上边缘不对导致孩子和孩子的前兄弟重合就是可行的
529 if (pTop - 1 < beforeDis[deep - 1])
530 // 必须保证父亲结点和父亲的前一个兄弟保存1的距离,至少
531 // 添加的新结点top值的第二种求法:根据孩子取孩子结点的中心top
532 alltreedata[pNode.id].top = beforeDis[deep - 1] + 1 - (alltreedata[pNode.pid].children.length - 1) * 0.5;
533 } else {
534
535 // 此刻flag!=0
536 // 意味着结点有孩子,那么问题就解决了,直接取孩子的中间即可
537 // 其实,flag==0的分支计算的就是孩子,是没有孩子的叶结点,那是关键
538 alltreedata[pNode.id].top = (alltreedata[pNode.children[0]].top + alltreedata[pNode.children[flag - 1]].top) * 0.5;
539 }
540
541 // 因为计算孩子的时候
542 // 无法掌握父辈兄弟的情况
543 // 可能会出现父亲和兄弟重叠问题
544 if (alltreedata[pNode.id].top <= beforeDis[deep]) {
545 var needUp = beforeDis[deep] + 1 - alltreedata[pNode.id].top;
546 (function doUp(_pid, _deep) {
547 alltreedata[_pid].top += needUp;
548 if (beforeDis[_deep] < alltreedata[_pid].top) beforeDis[_deep] = alltreedata[_pid].top;
549 var _flag = void 0;
550 for (_flag = 0; _flag < alltreedata[_pid].children.length; _flag++) {
551 doUp(alltreedata[_pid].children[_flag], _deep + 1);
552 }
553 })(pNode.id, deep);
554 }
555
556 // 计算好一个结点后,需要更新此刻该层的上边缘
557 beforeDis[deep] = alltreedata[pNode.id].top;
558
559 // size在每次计算一个结点后更新,是为了最终绘图的时候知道树有多宽(此处应该叫高)
560 if (alltreedata[pNode.id].top + 0.5 > size) size = alltreedata[pNode.id].top + 0.5;
561 })(alltreedata[rootid], 0);
562
563 // 传递的参数分别表示:记录了位置信息的树结点集合、根结点ID和树的宽
564 return {
565 "node": alltreedata,
566 "root": rootid,
567 "size": size,
568 "deep": maxDeep + 1
569 };
570 };
571
572 /**
573 * 根据配置的层次关系(配置的id,child,root)把原始数据变成内部结构,方便后期位置计算
574 * @param {any} initTree
575 *
576 * tempTree[id]={
577 * "data":原始数据,
578 * "pid":父亲ID,
579 * "id":唯一标识ID,
580 * "children":[cid1、cid2、...]
581 * }
582 */
583 var toInnerTree = function toInnerTree(initTree) {
584
585 var tempTree = {};
586 // 根结点
587 var temp = config.root(initTree),
588 id = void 0,
589 rid = void 0;
590 id = rid = config.id(temp);
591 tempTree[id] = {
592 "data": temp,
593 "pid": null,
594 "id": id,
595 "children": []
596 };
597 // 根据传递的原始数据,生成内部统一结构
598 (function createTree(pdata, pid) {
599 var children = config.child(pdata, initTree),
600 flag = void 0;
601 for (flag = 0; children && flag < children.length; flag++) {
602 id = config.id(children[flag]);
603 tempTree[pid].children.push(id);
604 tempTree[id] = {
605 "data": children[flag],
606 "pid": pid,
607 "id": id,
608 "children": []
609 };
610 createTree(children[flag], id);
611 }
612 })(temp, id);
613
614 return [rid, tempTree];
615 };
616
617 // 可以传递任意格式的树原始数据
618 // 只要配置对应的解析方法即可
619 var tree = function tree(initTree) {
620
621 var treeData = toInnerTree(initTree);
622 alltreedata = treeData[1];
623 rootid = treeData[0];
624 return update();
625 };
626
627 // 获取根结点的方法:root(initTree)
628 tree.root = function (rootback) {
629 config.root = rootback;
630 return tree;
631 };
632
633 // 获取子结点的方法:child(parentTree,initTree)
634 tree.child = function (childback) {
635 config.child = childback;
636 return tree;
637 };
638
639 // 获取结点ID方法:id(treedata)
640 tree.id = function (idback) {
641 config.id = idback;
642 return tree;
643 };
644
645 return tree;
646 }
647
648 /**
649 * 点(x,y)围绕中心(cx,cy)旋转deg度
650 */
651 var _rotate2 = function _rotate2(cx, cy, deg, x, y) {
652 var cos = Math.cos(deg),
653 sin = Math.sin(deg);
654 return [+((x - cx) * cos - (y - cy) * sin + cx).toFixed(7), +((x - cx) * sin + (y - cy) * cos + cy).toFixed(7)];
655 };
656
657 /**
658 * 点(x,y)沿着向量(ax,ay)方向移动距离d
659 */
660 var _move2 = function _move2(ax, ay, d, x, y) {
661 var sqrt = Math.sqrt(ax * ax + ay * ay);
662 return [+(ax * d / sqrt + x).toFixed(7), +(ay * d / sqrt + y).toFixed(7)];
663 };
664
665 /**
666 * 点(x,y)围绕中心(cx,cy)缩放times倍
667 */
668 var _scale2 = function _scale2(cx, cy, times, x, y) {
669 return [+(times * (x - cx) + cx).toFixed(7), +(times * (y - cy) + cy).toFixed(7)];
670 };
671
672 var dot = function dot(config) {
673
674 config = initConfig({
675 // 前进方向
676 d: [1, 1],
677 // 中心坐标
678 c: [0, 0],
679 // 当前位置
680 p: [0, 0]
681 }, config);
682
683 var dotObj = {
684
685 // 前进方向以当前位置为中心,旋转deg度
686 "rotate": function rotate(deg) {
687 var dPx = config.d[0] + config.p[0],
688 dPy = config.d[1] + config.p[1];
689 var dP = _rotate2(config.p[0], config.p[1], deg, dPx, dPy);
690 config.d = [dP[0] - config.p[0], dP[1] - config.p[1]];
691 return dotObj;
692 },
693
694 // 沿着当前前进方向前进d
695 "move": function move(d) {
696 config.p = _move2(config.d[0], config.d[1], d, config.p[0], config.p[1]);
697 return dotObj;
698 },
699
700 // 围绕中心坐标缩放
701 "scale": function scale(times) {
702 config.p = _scale2(config.c[0], config.c[1], times, config.p[0], config.p[1]);
703 return dotObj;
704 },
705
706 // 当前位置
707 "value": function value() {
708 return config.p;
709 }
710
711 };
712
713 return dotObj;
714 };
715
716 function treeLayout$1(config) {
717
718 config = initConfig({
719
720 // 类型:如果不是下面五种之一,就认为是原始类型
721 // type:LR|RL|BT|TB|circle
722
723 // 如果类型是LR|RL|BT|TB需要设置如下参数
724 // width,height:宽和高
725
726 // 如果类型是circle需要设置如下参数
727 // 1.cx,cy:圆心;2.radius:半径;3.begin-deg,deg:开始和跨越弧度(可选)
728 "begin-deg": 0,
729 "deg": Math.PI * 2
730
731 }, config);
732
733 var treeCalc = treeLayout()
734 // 配置数据格式
735 .root(config.root).child(config.child).id(config.id);
736
737 var treeObj = function treeObj(initData) {
738
739 // 计算初始坐标
740 var orgData = treeCalc(initData);
741
742 if (config.type === 'LR' || config.type === 'RL') {
743
744 // 每层间隔
745 var dis1 = config.width / orgData.deep;
746 if ("RL" === config.type) dis1 *= -1;
747 // 兄弟间隔
748 var dis2 = config.height / (orgData.size - -0.5);
749 for (var i in orgData.node) {
750 var node = orgData.node[i];
751 orgData.node[i].left = +(("RL" == config.type ? config.width : 0) - -node.left * dis1).toFixed(7);
752 orgData.node[i].top = +(node.top * dis2).toFixed(7);
753 }
754 } else if (config.type === 'TB' || config.type === 'BT') {
755
756 // 每层间隔
757 var _dis = config.height / orgData.deep;
758 if ("BT" == config.type) _dis *= -1;
759 // 兄弟间隔
760 var _dis2 = config.width / (orgData.size - -0.5);
761 var _left = void 0,
762 _top = void 0;
763 for (var _i3 in orgData.node) {
764 var _node2 = orgData.node[_i3];
765 _left = _node2.left;
766 _top = _node2.top;
767 orgData.node[_i3].top = +(("BT" == config.type ? config.height : 0) - -_left * _dis).toFixed(7);
768 orgData.node[_i3].left = +(_top * _dis2).toFixed(7);
769 }
770 } else if (config.type === 'circle') {
771
772 // 每层间距
773 var _dis3 = config.radius / (orgData.deep - 1);
774 // 兄弟间隔弧度
775 var _dis4 = config.deg / (orgData.size - -0.5);
776 for (var _i4 in orgData.node) {
777 var _node3 = orgData.node[_i4];
778 orgData.node[_i4].deg = (config['begin-deg'] - -_dis4 * _node3.top) % (Math.PI * 2);
779 var pos = _rotate2(config.cx, config.cy, orgData.node[_i4].deg, config.cx - -_dis3 * (_node3.left - 0.5), config.cy);
780 orgData.node[_i4].left = +pos[0];
781 orgData.node[_i4].top = +pos[1];
782 }
783 }
784
785 // 启动绘图
786 config.drawer(orgData);
787
788 return treeObj;
789 };
790
791 // 配置
792 treeObj.config = function (_config) {
793 config = initConfig(config, _config);
794 return treeObj;
795 };
796
797 // 设置绘图方法
798 treeObj.drawer = function (drawerback) {
799 config.drawer = drawerback;
800 return treeObj;
801 };
802
803 return treeObj;
804 }
805
806 /**
807 * 判断一个值是不是number。
808 *
809 * @since V0.1.3
810 * @public
811 * @param {*} value 需要判断类型的值
812 * @returns {boolean} 如果是number返回true,否则返回false
813 */
814 function isNumber(value) {
815 return typeof value === 'number' || value !== null && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' && getType(value) === '[object Number]';
816 }
817
818 function pieLayout(config) {
819
820 config = initConfig({
821
822 // 饼图的开始和跨域角度[可选]
823 "begin-deg": -Math.PI / 2,
824 "deg": Math.PI * 2,
825
826 // 饼图中一个瓣的中心参考半径,可以有多个[可选]
827 "radius": []
828 // "cx": "",
829 // "cy": "",
830
831 // 设置数据结构[必选]
832 // "value": function (data, key, index) { }
833
834 }, config);
835
836 if (!isFunction(config.value)) {
837 throw new Error('config.value must be a function!');
838 }
839
840 var pieObj = function pieObj(initData) {
841
842 var i = 0,
843 innerData = [],
844 allData = 0;
845 for (var key in initData) {
846 innerData.push({
847 "value": config.value(initData[key], key, i),
848 "data": initData[key],
849 "key": key,
850 "index": i,
851 "dots": []
852 });
853 allData += innerData[i].value;
854 i += 1;
855 }
856
857 for (i = 0; i < innerData.length; i++) {
858
859 // 起始弧度
860 innerData[i].beginDeg = i === 0 ? config['begin-deg'] : innerData[i - 1].beginDeg + innerData[i - 1].deg;
861
862 // 百分比
863 var percent = innerData[i].value / allData;
864
865 // 跨越弧度
866 innerData[i].deg = percent * config.deg;
867
868 innerData[i].percent = new Number(percent * 100).toFixed(2);
869 }
870
871 // 中心点(用于辅助绘制折线)
872 if (isNumber(config.cx) && isNumber(config.cy)) {
873 for (i = 0; i < config.radius.length; i++) {
874
875 for (var j = 0; j < innerData.length; j++) {
876 innerData[j].dots.push(_rotate2(config.cx, config.cy, innerData[j].beginDeg + innerData[j].deg * 0.5, config.cx + config.radius[i], config.cy));
877 }
878 }
879 }
880
881 // 启动绘图
882 if (isFunction(config.drawer)) {
883 config.drawer(innerData);
884 }
885 };
886
887 // 配置
888 pieObj.config = function (_config) {
889 config = initConfig(config, _config);
890 return pieObj;
891 };
892
893 // 设置绘图方法
894 pieObj.drawer = function (drawerback) {
895 config.drawer = drawerback;
896 return pieObj;
897 };
898
899 return pieObj;
900 }
901
902 /**
903 * 在(a,b,c)方向位移d
904 * @private
905 */
906 function _move(d, a, b, c) {
907 c = c || 0;
908 var sqrt = Math.sqrt(a * a + b * b + c * c);
909 return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, a * d / sqrt, b * d / sqrt, c * d / sqrt, 1];
910 }
911
912 /**
913 * 围绕0Z轴旋转
914 * 其它的旋转可以借助transform实现
915 * 旋转角度单位采用弧度制
916 *
917 * @private
918 */
919 function _rotate(deg) {
920 var sin = Math.sin(deg),
921 cos = Math.cos(deg);
922 return [cos, sin, 0, 0, -sin, cos, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
923 }
924
925 /**
926 * 围绕圆心x、y和z分别缩放xTimes, yTimes和zTimes倍
927 *
928 * @private
929 */
930 function _scale(xTimes, yTimes, zTimes, cx, cy, cz) {
931 cx = cx || 0;cy = cy || 0;cz = cz || 0;
932 return [xTimes, 0, 0, 0, 0, yTimes, 0, 0, 0, 0, zTimes, 0, cx - cx * xTimes, cy - cy * yTimes, cz - cz * zTimes, 1];
933 }
934
935 /**
936 * 针对任意射线(a1,b1,c1)->(a2,b2,c2)
937 * 计算出二个变换矩阵
938 * 分别为:任意射线变成OZ轴变换矩阵 + OZ轴变回原来的射线的变换矩阵
939 *
940 * @private
941 */
942 function _transform(a1, b1, c1, a2, b2, c2) {
943
944 if (typeof a1 === 'number' && typeof b1 === 'number') {
945
946 // 如果设置二个点
947 // 表示二维上围绕某个点旋转
948 if (typeof c1 !== 'number') {
949 c1 = 0;a2 = a1;b2 = b1;c2 = 1;
950 }
951 // 只设置三个点(设置不足六个点都认为只设置了三个点)
952 // 表示围绕从原点出发的射线旋转
953 else if (typeof a2 !== 'number' || typeof b2 !== 'number' || typeof c2 !== 'number') {
954 a2 = a1;b2 = b1;c2 = c1;a1 = 0;b1 = 0;c1 = 0;
955 }
956
957 if (a1 == a2 && b1 == b2 && c1 == c2) throw new Error('It\'s not a legitimate ray!');
958
959 var sqrt1 = Math.sqrt((a2 - a1) * (a2 - a1) + (b2 - b1) * (b2 - b1)),
960 cos1 = sqrt1 != 0 ? (b2 - b1) / sqrt1 : 1,
961 sin1 = sqrt1 != 0 ? (a2 - a1) / sqrt1 : 0,
962 b = (a2 - a1) * sin1 + (b2 - b1) * cos1,
963 c = c2 - c1,
964 sqrt2 = Math.sqrt(b * b + c * c),
965 cos2 = sqrt2 != 0 ? c / sqrt2 : 1,
966 sin2 = sqrt2 != 0 ? b / sqrt2 : 0;
967
968 return [
969
970 // 任意射线变成OZ轴变换矩阵
971 [cos1, cos2 * sin1, sin1 * sin2, 0, -sin1, cos1 * cos2, cos1 * sin2, 0, 0, -sin2, cos2, 0, b1 * sin1 - a1 * cos1, c1 * sin2 - a1 * sin1 * cos2 - b1 * cos1 * cos2, -a1 * sin1 * sin2 - b1 * cos1 * sin2 - c1 * cos2, 1],
972
973 // OZ轴变回原来的射线的变换矩阵
974 [cos1, -sin1, 0, 0, cos2 * sin1, cos2 * cos1, -sin2, 0, sin1 * sin2, cos1 * sin2, cos2, 0, a1, b1, c1, 1]];
975 } else {
976 throw new Error('a1 and b1 is required!');
977 }
978 }
979
980 // 二个4x4矩阵相乘
981 // 或矩阵和齐次坐标相乘
982 var _multiply = function _multiply(matrix4, param) {
983 var newParam = [];
984 for (var i = 0; i < 4; i++) {
985 for (var j = 0; j < param.length / 4; j++) {
986 newParam[j * 4 + i] = matrix4[i] * param[j * 4] + matrix4[i + 4] * param[j * 4 + 1] + matrix4[i + 8] * param[j * 4 + 2] + matrix4[i + 12] * param[j * 4 + 3];
987 }
988 }return newParam;
989 };
990
991 /**
992 * 4x4矩阵
993 * 列主序存储
994 * @since V0.2.0
995 * @public
996 */
997 function Matrix4(initMatrix4) {
998
999 var matrix4 = initMatrix4 || [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
1000
1001 var matrix4Obj = {
1002
1003 // 移动
1004 "move": function move(dis, a, b, c) {
1005 matrix4 = _multiply(_move(dis, a, b, c), matrix4);
1006 return matrix4Obj;
1007 },
1008
1009 // 旋转
1010 "rotate": function rotate(deg, a1, b1, c1, a2, b2, c2) {
1011 var matrix4s = _transform(a1, b1, c1, a2, b2, c2);
1012 matrix4 = _multiply(_multiply(_multiply(matrix4s[1], _rotate(deg)), matrix4s[0]), matrix4);
1013 return matrix4Obj;
1014 },
1015
1016 // 缩放
1017 "scale": function scale(xTimes, yTimes, zTimes, cx, cy, cz) {
1018 matrix4 = _multiply(_scale(xTimes, yTimes, zTimes, cx, cy, cz), matrix4);
1019 return matrix4Obj;
1020 },
1021
1022 // 乘法
1023 // 可以传入一个矩阵(matrix4,flag)
1024 "multiply": function multiply(newMatrix4, flag) {
1025 matrix4 = flag ? _multiply(matrix4, newMatrix4) : _multiply(newMatrix4, matrix4);
1026 return matrix4Obj;
1027 },
1028
1029 // 对一个坐标应用变换
1030 // 齐次坐标(x,y,z,w)
1031 "use": function use(x, y, z, w) {
1032 // w为0表示点位于无穷远处,忽略
1033 z = z || 0;w = w || 1;
1034 var temp = _multiply(matrix4, [x, y, z, w]);
1035 temp[0] = +temp[0].toFixed(7);
1036 temp[1] = +temp[1].toFixed(7);
1037 temp[2] = +temp[2].toFixed(7);
1038 temp[3] = +temp[3].toFixed(7);
1039 return temp;
1040 },
1041
1042 // 矩阵的值
1043 "value": function value() {
1044 return matrix4;
1045 }
1046
1047 };
1048
1049 return matrix4Obj;
1050 }
1051
1052 //当前正在运动的动画的tick函数堆栈
1053 var $timers = [];
1054 //唯一定时器的定时间隔
1055 var $interval = 13;
1056 //指定了动画时长duration默认值
1057 var $speeds = 400;
1058 //定时器ID
1059 var $timerId = null;
1060
1061 /**
1062 * 动画轮播
1063 * @since V0.2.0
1064 * @public
1065 * @param {function} doback 轮询函数,有一个形参deep,0-1,表示执行进度
1066 * @param {number} duration 动画时长,可选
1067 * @param {function} callback 动画结束回调,可选,有一个形参deep,0-1,表示执行进度
1068 *
1069 * @returns {function} 返回一个函数,调用该函数,可以提前结束动画
1070 */
1071 function animation(doback, duration, callback) {
1072
1073 var clock = {
1074 //把tick函数推入堆栈
1075 "timer": function timer(tick, duration, callback) {
1076 if (!tick) {
1077 throw new Error('Tick is required!');
1078 }
1079 duration = duration || $speeds;
1080 var id = new Date().valueOf() + "_" + (Math.random() * 1000).toFixed(0);
1081 $timers.push({
1082 "id": id,
1083 "createTime": new Date(),
1084 "tick": tick,
1085 "duration": duration,
1086 "callback": callback
1087 });
1088 clock.start();
1089 return id;
1090 },
1091
1092 //开启唯一的定时器timerId
1093 "start": function start() {
1094 if (!$timerId) {
1095 $timerId = window.setInterval(clock.tick, $interval);
1096 }
1097 },
1098
1099 //被定时器调用,遍历timers堆栈
1100 "tick": function tick() {
1101 var createTime = void 0,
1102 flag = void 0,
1103 tick = void 0,
1104 callback = void 0,
1105 timer = void 0,
1106 duration = void 0,
1107 passTime = void 0,
1108 timers = $timers;
1109 $timers = [];
1110 $timers.length = 0;
1111 for (flag = 0; flag < timers.length; flag++) {
1112 //初始化数据
1113 timer = timers[flag];
1114 createTime = timer.createTime;
1115 tick = timer.tick;
1116 duration = timer.duration;
1117 callback = timer.callback;
1118
1119 //执行
1120 passTime = (+new Date() - createTime) / duration;
1121 passTime = passTime > 1 ? 1 : passTime;
1122 tick(passTime);
1123 if (passTime < 1 && timer.id) {
1124 //动画没有结束再添加
1125 $timers.push(timer);
1126 } else if (callback) {
1127 callback(passTime);
1128 }
1129 }
1130 if ($timers.length <= 0) {
1131 clock.stop();
1132 }
1133 },
1134
1135 //停止定时器,重置timerId=null
1136 "stop": function stop() {
1137 if ($timerId) {
1138 window.clearInterval($timerId);
1139 $timerId = null;
1140 }
1141 }
1142 };
1143
1144 var id = clock.timer(function (deep) {
1145 //其中deep为0-1,表示改变的程度
1146 doback(deep);
1147 }, duration, callback);
1148
1149 // 返回一个函数
1150 // 用于在动画结束前结束动画
1151 return function () {
1152 var i = void 0;
1153 for (i in $timers) {
1154 if ($timers[i].id == id) {
1155 $timers[i].id = undefined;
1156 return;
1157 }
1158 }
1159 };
1160 }
1161
1162 /**
1163 * 初始化配置文件
1164 *
1165 * @private
1166 * @param {Json} init 默认值
1167 * @param {Json} data
1168 * @return {Json}
1169 */
1170 function initConfig$1(init, data) {
1171 for (var key in data) {
1172 try {
1173 init[key] = data[key];
1174 } catch (e) {
1175 throw new Error("Illegal property value!");
1176 }
1177 }return init;
1178 }
1179
1180 /**
1181 * Hermite三次插值
1182 * @since V0.2.0
1183 * @public
1184 * @param {Json} config 可选
1185 */
1186 function hermite(config) {
1187
1188 config = initConfig$1({
1189 // 张弛系数
1190 "u": 0.5
1191 }, config);
1192
1193 var MR = void 0,
1194 a = void 0,
1195 b = void 0;
1196
1197 /**
1198 * 根据x值返回y值
1199 * @param {Number} x
1200 */
1201 var hermite = function hermite(x) {
1202 if (MR) {
1203 var sx = (x - a) / (b - a),
1204 sx2 = sx * sx,
1205 sx3 = sx * sx2;
1206 var sResult = sx3 * MR[0] + sx2 * MR[1] + sx * MR[2] + MR[3];
1207 return sResult * (b - a);
1208 } else throw new Error('You shoud first set the position!');
1209 };
1210
1211 /**
1212 * 设置点的位置
1213 * @param {Number} x1 左边点的位置
1214 * @param {Number} y1
1215 * @param {Number} x2 右边点的位置
1216 * @param {Number} y2
1217 * @param {Number} s1 二个点的斜率
1218 * @param {Number} s2
1219 */
1220 hermite.setP = function (x1, y1, x2, y2, s1, s2) {
1221 if (x1 < x2) {
1222 // 记录原始尺寸
1223 a = x1;b = x2;
1224 var p3 = config.u * s1,
1225 p4 = config.u * s2;
1226 // 缩放到[0,1]定义域
1227 y1 /= x2 - x1;
1228 y2 /= x2 - x1;
1229 // MR是提前计算好的多项式通解矩阵
1230 // 为了加速计算
1231 // 如上面说的
1232 // 统一在[0,1]上计算后再通过缩放和移动恢复
1233 // 避免了动态求解矩阵的麻烦
1234 MR = [2 * y1 - 2 * y2 + p3 + p4, 3 * y2 - 3 * y1 - 2 * p3 - p4, p3, y1];
1235 } else throw new Error('The point x-position should be increamented!');
1236 return hermite;
1237 };
1238
1239 return hermite;
1240 }
1241
1242 /**
1243 * Cardinal三次插值
1244 * ----------------------------
1245 * Hermite拟合的计算是,确定二个点和二个点的斜率
1246 * 用一个y=ax(3)+bx(2)+cx+d的三次多项式来求解
1247 * 而Cardinal是建立在此基础上
1248 * 给定需要拟合的二个点和第一个点的前一个点+最后一个点的后一个点
1249 * 第一个点的斜率由第一个点的前一个点和第二个点的斜率确定
1250 * 第二个点的斜率由第一个点和第二个点的后一个点的斜率确定
1251 */
1252
1253 function cardinal(config) {
1254
1255 config = initConfig({
1256 // 该参数用于调整曲线走势,默认数值t=0,分水岭t=-1,|t-(-1)|的值越大,曲线走势调整的越严重
1257 "t": 0
1258 }, config);
1259
1260 var HS = void 0,
1261 i = void 0;
1262
1263 // 根据x值返回y值
1264 var cardinal = function cardinal(x) {
1265
1266 if (HS) {
1267 i = -1;
1268 // 寻找记录x实在位置的区间
1269 // 这里就是寻找对应的拟合函数
1270 while (i + 1 < HS.x.length && (x > HS.x[i + 1] || i == -1 && x >= HS.x[i + 1])) {
1271 i += 1;
1272 }
1273 if (i == -1 || i >= HS.h.length) throw new Error('Coordinate crossing!');
1274 return HS.h[i](x);
1275 } else {
1276 throw new Error('You shoud first set the position!');
1277 }
1278 };
1279
1280 // 设置张弛系数【应该在点的位置设置前设置】
1281 cardinal.setT = function (t) {
1282
1283 if (typeof t === 'number') {
1284 config.t = t;
1285 } else {
1286 throw new Error('Expecting a figure!');
1287 }
1288 return cardinal;
1289 };
1290
1291 // 设置点的位置
1292 // 参数格式:[[x,y],[x,y],...]
1293 // 至少二个点
1294 cardinal.setP = function (points) {
1295
1296 HS = {
1297 "x": [],
1298 "h": []
1299 };
1300 var flag = void 0,
1301 slope = (points[1][1] - points[0][1]) / (points[1][0] - points[0][0]),
1302 temp = void 0;
1303 HS.x[0] = points[0][0];
1304 for (flag = 1; flag < points.length; flag++) {
1305 if (points[flag][0] <= points[flag - 1][0]) throw new Error('The point position should be increamented!');
1306 HS.x[flag] = points[flag][0];
1307 // 求点斜率
1308 temp = flag < points.length - 1 ? (points[flag + 1][1] - points[flag - 1][1]) / (points[flag + 1][0] - points[flag - 1][0]) : (points[flag][1] - points[flag - 1][1]) / (points[flag][0] - points[flag - 1][0]);
1309 // 求解二个点直接的拟合方程
1310 // 第一个点的前一个点直接取第一个点
1311 // 最后一个点的后一个点直接取最后一个点
1312 HS.h[flag - 1] = hermite({
1313 "u": (1 - config.t) * 0.5
1314 }).setP(points[flag - 1][0], points[flag - 1][1], points[flag][0], points[flag][1], slope, temp);
1315 slope = temp;
1316 }
1317 return cardinal;
1318 };
1319
1320 return cardinal;
1321 }
1322
1323 /**
1324 * 把当前维护的结点加到目标结点内部的结尾
1325 * @param {selector} target
1326 * @return {image2D}
1327 */
1328 var appendTo = function appendTo(target, context) {
1329 var nodes = sizzle(target, context || document);
1330 if (nodes.length > 0) {
1331 for (var i = 0; i < this.length; i++) {
1332 nodes[0].appendChild(this[i]);
1333 }
1334 } else {
1335 throw new Error('Target empty!');
1336 }
1337 return this;
1338 };
1339
1340 /**
1341 * 把当前维护的结点加到目标结点内部的开头
1342 * @param {selector} target
1343 * @return {image2D}
1344 */
1345 var prependTo = function prependTo(target, context) {
1346 var nodes = sizzle(target, context || document);
1347 if (nodes.length > 0) {
1348 for (var i = 0; i < this.length; i++) {
1349 nodes[0].insertBefore(this[i], nodes[0].childNodes[0]);
1350 }
1351 } else {
1352 throw new Error('Target empty!');
1353 }
1354 return this;
1355 };
1356
1357 /**
1358 * 把当前维护的结点加到目标结点之后
1359 * @param {selector} target
1360 * @return {image2D}
1361 */
1362 var afterTo = function afterTo(target, context) {
1363 var nodes = sizzle(target, context || document);
1364 if (nodes.length > 0) {
1365 for (var i = 0; i < this.length; i++) {
1366 //如果第二个参数undefined,在结尾追加,目的一样达到
1367 nodes[0].parentNode.insertBefore(this[i], nodes[0].nextSibling);
1368 }
1369 } else {
1370 throw new Error('Target empty!');
1371 }
1372 return this;
1373 };
1374
1375 /**
1376 * 把当前维护的结点加到目标结点之前
1377 * @param {selector} target
1378 * @return {image2D}
1379 */
1380 var beforeTo = function beforeTo(target, context) {
1381 var nodes = sizzle(target, context || document);
1382 if (nodes.length > 0) {
1383 for (var i = 0; i < this.length; i++) {
1384 nodes[0].parentNode.insertBefore(this[i], nodes[0]);
1385 }
1386 } else {
1387 throw new Error('Target empty!');
1388 }
1389 return this;
1390 };
1391
1392 // 删除当前维护的结点
1393 var remove = function remove() {
1394 for (var i = 0; i < this.length; i++) {
1395 this[i].parentNode.removeChild(this[i]);
1396 }return this;
1397 };
1398
1399 // 筛选当前结点
1400 var filter = function filter(filterback) {
1401 var temp = [];
1402 for (var i = 0; i < this.length; i++) {
1403 if (filterback(i, image2D(this[i]))) temp.push(this[i]);
1404 }
1405 return image2D(temp);
1406 };
1407
1408 // 修改文本或获取结点文本
1409 var text = function text(content) {
1410 if (content) {
1411 for (var i = 0; i < this.length; i++) {
1412 this[i].textContent = content;
1413 }return this;
1414 }
1415 if (this.length <= 0) throw new Error('Target empty!');
1416 return this[0].textContent;
1417 };
1418
1419 /**
1420 * 返回渲染后的CSS样式值
1421 * @param {DOM} dom 目标结点
1422 * @param {String} name 属性名称(可选)
1423 * @return {String}
1424 */
1425 function getStyle(dom, name) {
1426
1427 // 获取结点的全部样式
1428 var allStyle = document.defaultView && document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(dom, null) : dom.currentStyle;
1429
1430 // 如果没有指定属性名称,返回全部样式
1431 return typeof name === 'string' ? allStyle.getPropertyValue(name) : allStyle;
1432 }
1433
1434 /**
1435 * 设置或获取样式
1436 * @arguments(key):获取指定样式
1437 * @arguments(key,value):设置指定样式
1438 * @arguments():获取全部样式
1439 * @arguments(json):设置大量样式
1440 */
1441 function style() {
1442
1443 // 获取样式
1444 if (arguments.length <= 1 && (arguments.length <= 0 || _typeof(arguments[0]) !== 'object')) {
1445 if (this.length <= 0) throw new Error('Target empty!');
1446
1447 // 为了获取非style定义的样式,需要使用特殊的方法获取
1448 return getStyle(this[0], arguments[0]);
1449 }
1450
1451 // 设置样式
1452 for (var i = 0; i < this.length; i++) {
1453 if (arguments.length === 1) {
1454 for (var key in arguments[0]) {
1455 this[i].style[key] = arguments[0][key];
1456 }
1457 } else this[i].style[arguments[0]] = arguments[1];
1458 }
1459
1460 return this;
1461 }
1462
1463 var setAttribute = function setAttribute(dom, attr, val) {
1464 if (/[a-z]/.test(dom.tagName) && XLINK_ATTRIBUTE.indexOf(attr) >= 0) {
1465 // 如果是xml元素
1466 // 针对xlink使用特殊方法赋值
1467 dom.setAttributeNS(NAMESPACE.xlink, 'xlink:' + attr, val);
1468 } else dom.setAttribute(attr, val);
1469 };
1470
1471 /**
1472 * 设置或获取属性
1473 * @arguments(attr):获取属性
1474 * @arguments(attr,value):设置指定属性值
1475 * @arguments(json):设置大量属性
1476 */
1477 function attribute() {
1478
1479 // 获取属性值
1480 if (arguments.length === 1 && _typeof(arguments[0]) !== 'object') {
1481 if (this.length <= 0) throw new Error('Target empty!');
1482 return this[0].getAttribute(arguments[0]);
1483 }
1484
1485 // 设置属性值
1486 else if (arguments.length > 0) {
1487 for (var i = 0; i < this.length; i++) {
1488 if (arguments.length === 1) {
1489 for (var key in arguments[0]) {
1490 setAttribute(this[i], key, arguments[0][key]);
1491 }
1492 } else setAttribute(this[i], arguments[0], arguments[1]);
1493 }
1494 }
1495
1496 return this;
1497 }
1498
1499 // 用于把数据绑定到一组结点或返回第一个结点数据
1500 // 可以传递函数对数据处理
1501 var datum = function datum(data, calcback) {
1502
1503 // 获取数据
1504 if (arguments.length <= 0) {
1505 if (this.length <= 0) throw new Error('Target empty!');
1506 return this[0].__data__;
1507 }
1508
1509 // 设置数据
1510 for (var i = 0; i < this.length; i++) {
1511 this[i].__data__ = typeof calcback === 'function' ? calcback(data, i) : data;
1512 }return this;
1513 };
1514
1515 // 用于把一组数据绑定到一组结点或返回一组结点数据
1516 // 可以传递函数对数据处理
1517 var data = function data(datas, calcback) {
1518
1519 // 获取数据
1520 if (arguments.length <= 0) {
1521 var _temp3 = [];
1522 for (var _i5 = 0; _i5 < this.length; _i5++) {
1523 _temp3[_i5] = this[_i5].__data__;
1524 }return _temp3;
1525 }
1526
1527 // 设置数据
1528 var temp = [],
1529 i = void 0;
1530 for (i = 0; i < this.length && i < datas.length; i++) {
1531 this[i].__data__ = typeof calcback === 'function' ? calcback(datas[i], i) : datas[i];
1532 temp.push(this[i]);
1533 }
1534 var newImage2D = image2D(temp);
1535
1536 // 记录需要去平衡的数据
1537 newImage2D.__enter__ = [];
1538 for (; i < datas.length; i++) {
1539 newImage2D.__enter__.push(typeof calcback === 'function' ? calcback(datas[i], i) : datas[i]);
1540 } // 记录需要去平衡的结点
1541 newImage2D.__exit__ = [];
1542 for (; i < this.length; i++) {
1543 newImage2D.__exit__.push(this[i]);
1544 }return newImage2D;
1545 };
1546
1547 // 把过滤出来多于结点的数据部分变成结点返回
1548 // 需要传递一个字符串来标明新创建元素是什么
1549 var enter = function enter(template, type) {
1550
1551 if (!this.__enter__ || this.__enter__.constructor !== Array) throw new Error('Not a data node object to be balanced!');
1552
1553 var temp = [];
1554 for (var i = 0; i < this.__enter__.length; i++) {
1555 temp[i] = toNode$1(template, type);
1556 temp[i].__data__ = this.__enter__[i];
1557 }
1558
1559 delete this.__enter__;
1560 return image2D(temp);
1561 };
1562
1563 // 把过滤出来多于数据的结点部分返回
1564 var exit = function exit() {
1565
1566 if (!this.__exit__ || this.__exit__.constructor !== Array) throw new Error('Not a data node object to be balanced!');
1567
1568 var exitImage2D = image2D(this.__exit__);
1569 delete this.__exit__;
1570 return exitImage2D;
1571 };
1572
1573 // 在维护的结点上轮询执行传入的方法
1574 // doback(data,index,image2D)
1575 var loop = function loop(doback) {
1576
1577 for (var i = 0; i < this.length; i++) {
1578 doback(this[i].__data__, i, image2D(this[i]));
1579 }return this;
1580 };
1581
1582 /**
1583 * 绑定事件
1584 * @param {string} eventType
1585 * @param {function} callback
1586 */
1587 var bind = function bind(eventType, callback) {
1588
1589 if (window.attachEvent) {
1590 for (var flag = 0; flag < this.length; flag++) {
1591 this[flag].attachEvent("on" + eventType, callback);
1592 } // 后绑定的先执行
1593 } else {
1594 for (var _flag2 = 0; _flag2 < this.length; _flag2++) {
1595 this[_flag2].addEventListener(eventType, callback, false);
1596 } // 捕获
1597 }
1598
1599 return this;
1600 };
1601
1602 /**
1603 * 获取鼠标相对特定元素左上角位置
1604 * @param {Event} event
1605 */
1606 var position = function position(event) {
1607
1608 // 返回元素的大小及其相对于视口的位置
1609 var bounding = this[0].getBoundingClientRect();
1610
1611 if (!event || !event.clientX) throw new Error('Event is necessary!');
1612 return {
1613
1614 // 鼠标相对元素位置 = 鼠标相对窗口坐标 - 元素相对窗口坐标
1615 "x": event.clientX - bounding.left,
1616 "y": event.clientY - bounding.top
1617 };
1618 };
1619
1620 // r1和r2,内半径和外半径
1621 // beginA起点弧度,rotateA旋转弧度式
1622 function arc(beginA, rotateA, cx, cy, r1, r2, doback) {
1623
1624 if (rotateA > Math.PI * 2) rotateA = Math.PI * 2;
1625 if (rotateA < -Math.PI * 2) rotateA = -Math.PI * 2;
1626
1627 // 保证逆时针也是可以的
1628 if (rotateA < 0) {
1629 beginA += rotateA;
1630 rotateA *= -1;
1631 }
1632
1633 var temp = [],
1634 p = void 0;
1635
1636 // 内部
1637 p = _rotate2(0, 0, beginA, r1, 0);
1638 temp[0] = p[0];
1639 temp[1] = p[1];
1640 p = _rotate2(0, 0, rotateA, p[0], p[1]);
1641 temp[2] = p[0];
1642 temp[3] = p[1];
1643
1644 // 外部
1645 p = _rotate2(0, 0, beginA, r2, 0);
1646 temp[4] = p[0];
1647 temp[5] = p[1];
1648 p = _rotate2(0, 0, rotateA, p[0], p[1]);
1649 temp[6] = p[0];
1650 temp[7] = p[1];
1651
1652 doback(beginA, beginA + rotateA, temp[0] + cx, temp[1] + cy, temp[4] + cx, temp[5] + cy, temp[2] + cx, temp[3] + cy, temp[6] + cx, temp[7] + cy, (r2 - r1) * 0.5);
1653 }
1654
1655 // 文字统一设置方法
1656 var initText = function initText(painter, config, x, y, deg) {
1657 painter.beginPath();
1658 painter.translate(x, y);
1659 painter.rotate(deg);
1660 painter.font = config['font-size'] + "px " + config['font-family'];
1661 return painter;
1662 };
1663
1664 // 画弧统一设置方法
1665 var initArc = function initArc(painter, config, cx, cy, r1, r2, beginDeg, deg) {
1666 arc(beginDeg, deg, cx, cy, r1, r2, function (beginA, endA, begInnerX, begInnerY, begOuterX, begOuterY, endInnerX, endInnerY, endOuterX, endOuterY, r) {
1667 if (r < 0) r = -r;
1668 painter.beginPath();
1669 painter.moveTo(begInnerX, begInnerY);
1670 painter.arc(
1671 // (圆心x,圆心y,半径,开始角度,结束角度,true逆时针/false顺时针)
1672 cx, cy, r1, beginA, endA, false);
1673 // 结尾
1674 if (config["arc-end-cap"] != 'round') painter.lineTo(endOuterX, endOuterY);else painter.arc((endInnerX + endOuterX) * 0.5, (endInnerY + endOuterY) * 0.5, r, endA - Math.PI, endA, true);
1675 painter.arc(cx, cy, r2, endA, beginA, true);
1676 // 开头
1677 if (config["arc-start-cap"] != 'round') painter.lineTo(begInnerX, begInnerY);else painter.arc((begInnerX + begOuterX) * 0.5, (begInnerY + begOuterY) * 0.5, r, beginA, beginA - Math.PI, true);
1678 });
1679 return painter;
1680 };
1681
1682 // 画圆统一设置方法
1683 var initCircle = function initCircle(painter, cx, cy, r) {
1684 painter.beginPath();
1685 painter.moveTo(cx + r, cy);
1686 painter.arc(cx, cy, r, 0, Math.PI * 2);
1687 return painter;
1688 };
1689
1690 // 画矩形统一设置方法
1691 var initRect = function initRect(painter, x, y, width, height) {
1692 painter.beginPath();
1693 painter.rect(x, y, width, height);
1694 return painter;
1695 };
1696
1697 var linearGradient = function linearGradient(painter, x0, y0, x1, y1) {
1698 var gradient = painter.createLinearGradient(x0, y0, x1, y1);
1699 var enhanceGradient = {
1700 "value": function value() {
1701 return gradient;
1702 },
1703 "addColorStop": function addColorStop(stop, color) {
1704 gradient.addColorStop(stop, color);
1705 return enhanceGradient;
1706 }
1707 };
1708 return enhanceGradient;
1709 };
1710
1711 // 加强版本的画笔
1712 function painter_canvas2D(canvas) {
1713
1714 // 获取canvas2D画笔
1715 var painter = canvas.getContext("2d");
1716
1717 // 如果没有针对模糊问题处理
1718 if (canvas.__had_scale2_canvas__ !== 'YES') {
1719 canvas.__had_scale2_canvas__ = 'YES';
1720
1721 var width = canvas.clientWidth || canvas.getAttribute('width'),
1722 //内容+内边距
1723 height = canvas.clientHeight || canvas.getAttribute('height');
1724
1725 // 设置显示大小
1726 canvas.style.width = width + "px";
1727 canvas.style.height = height + "px";
1728
1729 // 设置画布大小(画布大小设置为显示的二倍,使得显示的时候更加清晰)
1730 canvas.setAttribute('width', width * 2);
1731 canvas.setAttribute('height', height * 2);
1732
1733 // 通过缩放实现模糊问题
1734 painter.scale(2, 2);
1735 }
1736
1737 // 默认配置canvas2D对象已经存在的属性
1738 painter.textBaseline = 'middle';
1739 painter.textAlign = 'left';
1740
1741 // 默认配置不应该有canvas2D对象已经存在的属性
1742 // 这里是为了简化或和svg统一接口而自定义的属性
1743 var _config2 = {
1744 "font-size": "16", // 文字大小
1745 "font-family": "sans-serif", // 字体
1746 "arc-start-cap": "butt", // 弧开始闭合方式
1747 "arc-end-cap": "butt" // 弧结束闭合方式
1748 };
1749
1750 // 画笔
1751 var enhancePainter = {
1752
1753 // 属性设置或获取
1754 "config": function config() {
1755 if (arguments.length === 1) {
1756 if (_typeof(arguments[0]) !== 'object') return painter[arguments[0]];
1757 for (var key in arguments[0]) {
1758 if (_config2[key]) _config2[key] = arguments[0][key];else painter[key] = arguments[0][key];
1759 }
1760 } else if (arguments.length === 2) {
1761 if (_config2[arguments[0]]) _config2[arguments[0]] = arguments[1];else painter[arguments[0]] = arguments[1];
1762 }
1763 return enhancePainter;
1764 },
1765
1766 // 文字
1767 "fillText": function fillText(text, x, y, deg) {
1768 painter.save();
1769 initText(painter, _config2, x, y, deg || 0).fillText(text, 0, 0);
1770 painter.restore();
1771 return enhancePainter;
1772 },
1773 "strokeText": function strokeText(text, x, y, deg) {
1774 painter.save();
1775 initText(painter, _config2, x, y, deg || 0).strokeText(text, 0, 0);
1776 painter.restore();
1777 return enhancePainter;
1778 },
1779
1780 // 路径
1781 "beginPath": function beginPath() {
1782 painter.beginPath();return enhancePainter;
1783 },
1784 "closePath": function closePath() {
1785 painter.closePath();return enhancePainter;
1786 },
1787 "moveTo": function moveTo(x, y) {
1788 painter.moveTo(x, y);return enhancePainter;
1789 },
1790 "lineTo": function lineTo(x, y) {
1791 painter.lineTo(x, y);return enhancePainter;
1792 },
1793 "arc": function arc(x, y, r, beginDeg, deg) {
1794 painter.arc(x, y, r, beginDeg, beginDeg + deg);
1795 return enhancePainter;
1796 },
1797 "fill": function fill() {
1798 painter.fill();return enhancePainter;
1799 },
1800 "stroke": function stroke() {
1801 painter.stroke();return enhancePainter;
1802 },
1803
1804 "save": function save() {
1805 painter.save();return enhancePainter;
1806 },
1807 "restore": function restore() {
1808 painter.restore();return enhancePainter;
1809 },
1810
1811 // 路径 - 贝塞尔曲线
1812 "quadraticCurveTo": function quadraticCurveTo(cpx, cpy, x, y) {
1813 painter.quadraticCurveTo(cpx, cpy, x, y);return enhancePainter;
1814 },
1815 "bezierCurveTo": function bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
1816 painter.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);return enhancePainter;
1817 },
1818
1819 // 擦除画面
1820 "clearRect": function clearRect(x, y, width, height) {
1821 painter.clearRect(x || 0, y || 0, width || canvas.getAttribute('width') / 2, height || canvas.getAttribute('height') / 2);return enhancePainter;
1822 },
1823
1824 // 地址图片
1825 "toDataURL": function toDataURL() {
1826 return canvas.toDataURL();
1827 },
1828
1829 // image
1830 "drawImage": function drawImage(img, sx, sy, sw, sh, x, y, w, h) {
1831 painter.drawImage(img, sx || 0, sy || 0, sw ? sw * 2 : canvas.getAttribute('width'), sh ? sh * 2 : canvas.getAttribute('height'), x || 0, y || 0, w || canvas.getAttribute('width') / 2, h || canvas.getAttribute('height') / 2);
1832 return enhancePainter;
1833 },
1834
1835 // 弧
1836 "fillArc": function fillArc(cx, cy, r1, r2, beginDeg, deg) {
1837 initArc(painter, _config2, cx, cy, r1, r2, beginDeg, deg).fill();return enhancePainter;
1838 },
1839 "strokeArc": function strokeArc(cx, cy, r1, r2, beginDeg, deg) {
1840 initArc(painter, _config2, cx, cy, r1, r2, beginDeg, deg).stroke();return enhancePainter;
1841 },
1842
1843 // 圆形
1844 "fillCircle": function fillCircle(cx, cy, r) {
1845 initCircle(painter, cx, cy, r).fill();return enhancePainter;
1846 },
1847 "strokeCircle": function strokeCircle(cx, cy, r) {
1848 initCircle(painter, cx, cy, r).stroke();return enhancePainter;
1849 },
1850
1851 // 矩形
1852 "fillRect": function fillRect(x, y, width, height) {
1853 initRect(painter, x, y, width, height).fill();return enhancePainter;
1854 },
1855 "strokeRect": function strokeRect(x, y, width, height) {
1856 initRect(painter, x, y, width, height).stroke();return enhancePainter;
1857 },
1858
1859 /**
1860 * 渐变
1861 * -------------
1862 */
1863
1864 // 线性渐变
1865 "createLinearGradient": function createLinearGradient(x0, y0, x1, y1) {
1866 return linearGradient(painter, x0, y0, x1, y1);
1867 },
1868
1869 /**
1870 * 变换
1871 * --------------
1872 */
1873
1874 // 移动
1875 // 用来移动 canvas 的原点到指定的位置
1876 "translate": function translate(x, y) {
1877 painter.translate(x, y);return enhancePainter;
1878 },
1879
1880 // 旋转
1881 "rotate": function rotate(deg) {
1882 painter.rotate(deg);return enhancePainter;
1883 },
1884
1885 // 缩放
1886 "scale": function scale(x, y) {
1887 y = y || x;painter.scale(x, y);return enhancePainter;
1888 }
1889 };
1890
1891 return enhancePainter;
1892 }
1893
1894 function normalConfig(key, value) {
1895
1896 // 文字水平对齐方式
1897 if (key === 'textAlign') {
1898 return {
1899 "left": "start",
1900 "right": "end",
1901 "center": "middle"
1902 }[value] || value;
1903 }
1904
1905 return value;
1906 }
1907 // 文字统一设置方法
1908 var initText$1 = function initText$1(painter, config, x, y, deg) {
1909 if (!isElement(painter[0])) throw new Error('Target empty!');
1910 if (painter[0].nodeName.toLowerCase() !== 'text') throw new Error('Need a <text> !');
1911
1912 // 垂直对齐采用dy实现
1913 painter.attr('dy', {
1914 "top": config['font-size'] * 0.5,
1915 "middle": 0,
1916 "bottom": -config['font-size'] * 0.5
1917 }[config.textBaseline]).css({
1918
1919 // 文字对齐方式
1920 "text-anchor": config.textAlign,
1921 "dominant-baseline": "central",
1922
1923 // 文字大小和字体设置
1924 "font-size": config['font-size'] + "px",
1925 "font-family": config['font-family']
1926 }).attr({ "x": x, "y": y });
1927
1928 return {
1929 "transform": "rotate(" + deg * 180 / Math.PI + "," + x + "," + y + ")"
1930 };
1931 };
1932
1933 // 画弧统一设置方法
1934 var initArc$1 = function initArc$1(painter, config, cx, cy, r1, r2, beginDeg, deg) {
1935 if (painter[0].nodeName.toLowerCase() !== 'path') throw new Error('Need a <path> !');
1936 arc(beginDeg, deg, cx, cy, r1, r2, function (beginA, endA, begInnerX, begInnerY, begOuterX, begOuterY, endInnerX, endInnerY, endOuterX, endOuterY, r) {
1937 var f = endA - beginA > Math.PI ? 1 : 0,
1938 d = "M" + begInnerX + " " + begInnerY;
1939 if (r < 0) r = -r;
1940 d +=
1941 // 横半径 竖半径 x轴偏移角度 0小弧/1大弧 0逆时针/1顺时针 终点x 终点y
1942 "A" + r1 + " " + r1 + " 0 " + f + " 1 " + endInnerX + " " + endInnerY;
1943 // 结尾
1944 if (config["arc-end-cap"] != 'round') d += "L" + endOuterX + " " + endOuterY;else d += "A" + r + " " + r + " " + " 0 1 0 " + endOuterX + " " + endOuterY;
1945 d += "A" + r2 + " " + r2 + " 0 " + f + " 0 " + begOuterX + " " + begOuterY;
1946 // 开头
1947 if (config["arc-start-cap"] != 'round') d += "L" + begInnerX + " " + begInnerY;else d += "A" + r + " " + r + " " + " 0 1 0 " + begInnerX + " " + begInnerY;
1948 painter.attr('d', d);
1949 });
1950 return painter;
1951 };
1952
1953 // 画圆统一设置方法
1954 var initCircle$1 = function initCircle$1(painter, cx, cy, r) {
1955 if (painter[0].nodeName.toLowerCase() !== 'circle') throw new Error('Need a <circle> !');
1956 painter.attr({
1957 "cx": cx,
1958 "cy": cy,
1959 "r": r
1960 });
1961 return painter;
1962 };
1963
1964 // 路径统一设置方法
1965 var initPath = function initPath(painter, path) {
1966 if (painter[0].nodeName.toLowerCase() !== 'path') throw new Error('Need a <path> !');
1967 painter.attr('d', path);
1968 return painter;
1969 };
1970
1971 // 画矩形统一设置方法
1972 var initRect$1 = function initRect$1(painter, x, y, width, height) {
1973 if (painter[0].nodeName.toLowerCase() !== 'rect') throw new Error('Need a <rect> !');
1974 painter.attr({
1975 "x": x,
1976 "y": y,
1977 "width": width,
1978 "height": height
1979 });
1980 return painter;
1981 };
1982
1983 var initDefs = function initDefs(target) {
1984 var defs = target.getElementsByTagName('defs');
1985 if (defs.length <= 0) {
1986 defs = [toNode$1("<defs>", "SVG")];
1987 target.appendChild(defs[0]);
1988 }
1989 return defs[0];
1990 };
1991
1992 var linearGradient$1 = function linearGradient$1(painter, target, x0, y0, x1, y1) {
1993 var defs = initDefs(target);
1994 var gradientId = "image2D-lg-" + new Date().valueOf() + "-" + Math.random();
1995 var gradientDom = toNode$1('<linearGradient id="' + gradientId + '" x1="' + x0 + '%" y1="' + y0 + '%" x2="' + x1 + '%" y2="' + y1 + '%"></linearGradient>');
1996 target.appendChild(gradientDom);
1997 var enhanceGradient = {
1998 "value": function value() {
1999 return "url(#" + gradientId + ")";
2000 },
2001 "addColorStop": function addColorStop(stop, color) {
2002 gradientDom.appendChild(toNode$1('<stop offset="' + stop * 100 + '%" style="stop-color:' + color + ';" />'));
2003 return enhanceGradient;
2004 }
2005 };
2006 return enhanceGradient;
2007 };
2008
2009 function painter_svg(target, selector) {
2010
2011 var painter = void 0;
2012 if (selector) painter = image2D(selector, target);
2013
2014 // 类似canvas画笔的属性
2015 var _config3 = {
2016
2017 // 基本设置
2018 "fillStyle": "#000",
2019 "strokeStyle": "#000",
2020 "lineWidth": 1,
2021
2022 // 文字对齐方式
2023 "textAlign": "start",
2024 "textBaseline": "middle",
2025
2026 // 文字设置
2027 "font-size": "16",
2028 "font-family": "sans-serif",
2029
2030 // arc二端闭合方式['butt':直线闭合,'round':圆帽闭合]
2031 "arc-start-cap": "butt",
2032 "arc-end-cap": "butt"
2033
2034 };
2035
2036 // 路径(和canvas2D的类似)
2037 var path = "",
2038 currentPosition = [];
2039
2040 // 变换(和canvas2D的类似,内部维护了用于记录)
2041 var transform_history = [],
2042 transform_current = "";
2043
2044 // 画笔
2045 var enhancePainter = {
2046
2047 // 属性设置或获取
2048 "config": function config() {
2049 if (arguments.length === 1) {
2050 if (_typeof(arguments[0]) !== 'object') return _config3[arguments[0]];
2051 for (var key in arguments[0]) {
2052 _config3[key] = normalConfig(key, arguments[0][key]);
2053 }
2054 } else if (arguments.length === 2) _config3[arguments[0]] = normalConfig(arguments[0], arguments[1]);
2055 return enhancePainter;
2056 },
2057
2058 // 基础方法
2059 "bind": function bind(selector) {
2060 painter = image2D(selector, target);return enhancePainter;
2061 },
2062 "appendTo": function appendTo(selector) {
2063 painter.appendTo(selector || target, target);return enhancePainter;
2064 },
2065 "prependTo": function prependTo(selector) {
2066 painter.prependTo(selector || target, target);return enhancePainter;
2067 },
2068 "afterTo": function afterTo(selector) {
2069 painter.afterTo(selector || target, target);return enhancePainter;
2070 },
2071 "beforeTo": function beforeTo(selector) {
2072 painter.beforeTo(selector || target, target);return enhancePainter;
2073 },
2074
2075 // 路径
2076 "beginPath": function beginPath() {
2077 path = "";currentPosition = [];return enhancePainter;
2078 },
2079 "closePath": function closePath() {
2080 path += "Z";return enhancePainter;
2081 },
2082 "moveTo": function moveTo(x, y) {
2083 path += "M" + x + " " + y;currentPosition = [x, y];return enhancePainter;
2084 },
2085 "lineTo": function lineTo(x, y) {
2086 path += (path == "" ? "M" : "L") + x + " " + y;currentPosition = [x, y];return enhancePainter;
2087 },
2088 "arc": function arc(x, y, r, beginDeg, deg) {
2089 var begPosition = _rotate2(x, y, beginDeg, x + r, y);
2090 var endPosition = _rotate2(x, y, beginDeg + deg, x + r, y);
2091 beginDeg = beginDeg / Math.PI * 180;
2092 deg = deg / Math.PI * 180;
2093 // 如果当前没有路径,说明是开始的,就移动到正确位置
2094 if (path == '') {
2095 path += "M" + begPosition[0] + "," + begPosition[1];
2096 }
2097 // 如果当前有路径,位置不正确,应该画到正确位置(和canvas保持一致)
2098 else if (begPosition[0] != currentPosition[0] || begPosition[1] != currentPosition[1]) {
2099 path += "L" + begPosition[0] + "," + begPosition[1];
2100 }
2101 path += "A" + r + "," + r + " 0 " + (deg > 180 || deg < -180 ? 1 : 0) + "," + (deg > 0 ? 1 : 0) + " " + endPosition[0] + "," + endPosition[1];
2102 return enhancePainter;
2103 },
2104 "fill": function fill() {
2105 initPath(painter, path).attr('transform', transform_current).attr("fill", _config3.fillStyle);
2106 return enhancePainter;
2107 },
2108 "stroke": function stroke() {
2109 initPath(painter, path).attr('transform', transform_current).attr({ "stroke-width": _config3.lineWidth, "stroke": _config3.strokeStyle, "fill": "none" });
2110 return enhancePainter;
2111 },
2112
2113 "save": function save() {
2114 transform_history.push(transform_current);
2115 return enhancePainter;
2116 },
2117 "restore": function restore() {
2118 if (transform_history.length > 0) transform_current = transform_history.pop();
2119 return enhancePainter;
2120 },
2121
2122 // 路径 - 贝塞尔曲线
2123 "quadraticCurveTo": function quadraticCurveTo(cpx, cpy, x, y) {
2124 path += "Q" + cpx + " " + cpy + "," + x + " " + y;return enhancePainter;
2125 },
2126 "bezierCurveTo": function bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
2127 path += "C" + cp1x + " " + cp1y + "," + cp2x + " " + cp2y + "," + x + " " + y;return enhancePainter;
2128 },
2129
2130 // 文字
2131 "fillText": function fillText(text, x, y, deg) {
2132 var returnJSon = initText$1(painter, _config3, x, y, deg || 0);
2133 painter.attr('transform', transform_current + returnJSon.transform).attr("fill", _config3.fillStyle)[0].textContent = text;
2134 return enhancePainter;
2135 },
2136 "strokeText": function strokeText(text, x, y, deg) {
2137 var returnJSon = initText$1(painter, _config3, x, y, deg || 0);
2138 painter.attr('transform', transform_current + returnJSon.transform).attr({ "stroke": _config3.strokeStyle, "fill": "none" })[0].textContent = text;
2139 return enhancePainter;
2140 },
2141
2142 // 弧
2143 "fillArc": function fillArc(cx, cy, r1, r2, beginDeg, deg) {
2144 initArc$1(painter, _config3, cx, cy, r1, r2, beginDeg, deg).attr('transform', transform_current).attr("fill", _config3.fillStyle);
2145 return enhancePainter;
2146 },
2147 "strokeArc": function strokeArc(cx, cy, r1, r2, beginDeg, deg) {
2148 initArc$1(painter, _config3, cx, cy, r1, r2, beginDeg, deg).attr('transform', transform_current).attr({ "stroke-width": _config3.lineWidth, "stroke": _config3.strokeStyle, "fill": "none" });
2149 return enhancePainter;
2150 },
2151
2152 // 圆形
2153 "fillCircle": function fillCircle(cx, cy, r) {
2154 initCircle$1(painter, cx, cy, r).attr('transform', transform_current).attr("fill", _config3.fillStyle);return enhancePainter;
2155 },
2156 "strokeCircle": function strokeCircle(cx, cy, r) {
2157 initCircle$1(painter, cx, cy, r).attr('transform', transform_current).attr({ "stroke-width": _config3.lineWidth, "stroke": _config3.strokeStyle, "fill": "none" });return enhancePainter;
2158 },
2159
2160 // 矩形
2161 "fillRect": function fillRect(x, y, width, height) {
2162 initRect$1(painter, x, y, width, height).attr('transform', transform_current).attr("fill", _config3.fillStyle);return enhancePainter;
2163 },
2164 "strokeRect": function strokeRect(x, y, width, height) {
2165 initRect$1(painter, x, y, width, height).attr('transform', transform_current).attr({ "stroke-width": _config3.lineWidth, "stroke": _config3.strokeStyle, "fill": "none" });return enhancePainter;
2166 },
2167
2168 /**
2169 * 渐变
2170 * -------------
2171 */
2172
2173 // 线性渐变
2174 "createLinearGradient": function createLinearGradient(x0, y0, x1, y1) {
2175 return linearGradient$1(painter, target, x0, y0, x1, y1);
2176 },
2177
2178 /**
2179 * 变换
2180 * --------------
2181 */
2182
2183 // 移动
2184 "translate": function translate(x, y) {
2185 transform_current += ' translate(' + x + ',' + y + ')';
2186 return enhancePainter;
2187 },
2188
2189 // 旋转
2190 "rotate": function rotate(deg) {
2191 transform_current += ' rotate(' + deg / Math.PI * 180 + ')';
2192 return enhancePainter;
2193 },
2194
2195 // 缩放
2196 "scale": function scale(x, y) {
2197 y = y || x;
2198 transform_current += ' scale(' + x + ',' + y + ')';
2199 return enhancePainter;
2200 }
2201
2202 };
2203
2204 return enhancePainter;
2205 }
2206
2207 // 统一画笔
2208 // 负责启动具体的绘图对象
2209 function painter() {
2210
2211 // 因为绘图画布是必须的,因此在判断画布类型前,如果压根没有结点,肯定是非法的
2212 if (!isElement(this[0])) throw new Error('Target empty!');
2213
2214 var target = this[0],
2215 nodeName = target.nodeName.toLowerCase();
2216
2217 // canvas2D
2218 if (nodeName === 'canvas') return painter_canvas2D(target);
2219
2220 // svg
2221 if (nodeName === 'svg') return painter_svg(target, arguments[0]);
2222
2223 throw new Error('Painter is not a function!');
2224 }
2225
2226 /**
2227 * 判断传入的元素是不是canvas2D画笔
2228 * @param {Any} param
2229 * @return {Boolean} true:画笔,false:不是画笔
2230 */
2231 var isCanvas2D = function isCanvas2D(param) {
2232 return param && param.constructor === CanvasRenderingContext2D;
2233 };
2234
2235 function layer() {
2236
2237 if (!isElement(this[0])) throw new Error('Target empty!');
2238
2239 if (this[0].nodeName.toLowerCase() !== 'canvas') throw new Error('Layer is not a function!');
2240
2241 // 画笔
2242 var painter = this.painter(),
2243
2244 // 图层集合
2245 layer = {},
2246 layer_index = [];
2247 var width = this[0].clientWidth,
2248 //内容+内边距
2249 height = this[0].clientHeight;
2250
2251 var layerManager = {
2252
2253 // 获取指定图层画笔
2254 "painter": function painter(id) {
2255 if (!layer[id] || !isCanvas2D(layer[id].painter)) {
2256 // 初始化的图层都可见
2257 layer[id] = { "visible": true };
2258
2259 // 后期可以考虑使用离线画布offScreenCanvas提高效率
2260 layer[id].canvas = document.createElement('canvas');
2261 // 设置大小才会避免莫名其妙的错误
2262 layer[id].canvas.setAttribute('width', width);
2263 layer[id].canvas.setAttribute('height', height);
2264
2265 layer[id].painter = image2D(layer[id].canvas).painter();
2266
2267 layer_index.push(id);
2268 }
2269 return layer[id].painter;
2270 },
2271
2272 // 删除图层
2273 "delete": function _delete(id) {
2274 // 删除索引
2275 for (var i = 0; i < layer_index.length; i++) {
2276 if (layer_index[i] === id) {
2277 layer_index.splice(i, 1);
2278 break;
2279 }
2280 } // 删除图层
2281 delete layer[id];
2282 return layerManager;
2283 },
2284
2285 // 更新内容到画布
2286 "update": function update() {
2287 painter.clearRect(0, 0, width, height);
2288 painter.save();
2289
2290 for (var i = 0; i < layer_index.length; i++) {
2291 if (layer[layer_index[i]].visible) painter.drawImage(layer[layer_index[i]].canvas, 0, 0, width, height, 0, 0, width, height);
2292 }
2293 painter.restore();
2294 return layerManager;
2295 },
2296
2297 // 隐藏图层
2298 "hidden": function hidden(id) {
2299 layer[id].visible = false;
2300 return layerManager;
2301 },
2302
2303 // 显示图层
2304 "show": function show(id) {
2305 layer[id].visible = true;
2306 return layerManager;
2307 }
2308 };
2309
2310 return layerManager;
2311 }
2312
2313 image2D.extend({
2314
2315 // 布局
2316 treeLayout: treeLayout$1, pieLayout: pieLayout,
2317
2318 // 矩阵变换
2319 Matrix4: Matrix4,
2320
2321 // 二维简单变换
2322 rotate: _rotate2, move: _move2, scale: _scale2, dot: dot,
2323
2324 // 工具类
2325 animation: animation,
2326
2327 // 插值类计算
2328 cardinal: cardinal
2329
2330 });
2331 image2D.prototype.extend({
2332
2333 // 结点操作
2334 appendTo: appendTo, prependTo: prependTo, afterTo: afterTo, beforeTo: beforeTo, remove: remove, filter: filter, text: text,
2335
2336 // 结点属性或样式操作
2337 css: style, attr: attribute,
2338
2339 // 结点和数据绑定
2340 datum: datum, data: data, enter: enter, exit: exit, loop: loop,
2341
2342 // 结点事件
2343 bind: bind, position: position,
2344
2345 // 自定义画笔
2346 painter: painter,
2347
2348 // 图层
2349 layer: layer
2350
2351 });
2352
2353 image2D.fn = image2D.prototype;
2354
2355 // 判断当前环境,如果不是浏览器环境
2356 if ((typeof module === 'undefined' ? 'undefined' : _typeof(module)) === "object" && _typeof(module.exports) === "object") {
2357 module.exports = image2D;
2358 }
2359 // 浏览器环境下
2360 // 因为浏览器下挂载到window对象上
2361 // 为了防止覆盖,额外提供一个noConflict方法,用以在覆盖的时候恢复
2362 else {
2363 var
2364 // 保存之前的image2D,防止直接覆盖
2365 _image2D = window.image2D,
2366
2367
2368 // 保存之前的$$,防止直接覆盖
2369 _$$ = window.$$;
2370
2371 image2D.noConflict = function (deep) {
2372
2373 // 如果当前的$$是被最新的image2D覆盖的
2374 // 恢复之前的
2375 if (window.$$ === image2D) {
2376 window.$$ = _$$;
2377 }
2378
2379 // 如果当前的image2D是被最新的image2D覆盖的
2380 // 且标记需要恢复
2381 // 恢复之前的
2382 if (deep && window.image2D === image2D) {
2383 window.image2D = _image2D;
2384 }
2385
2386 // 返回当前image2D
2387 // 因为调用这个方法以后
2388 // 全局window下的image2D和$$是什么
2389 // 已经不一定了
2390 return image2D;
2391 };
2392 // 挂载库对象到根
2393 window.image2D = window.$$ = image2D;
2394 }
2395})();
\No newline at end of file