UNPKG

120 kBJavaScriptView Raw
1/*!
2* image2D - 🍇 使用ECMAScript绘制二维图片。Drawing Two-Dimensional Pictures Using ECMAScript.
3* git+https://github.com/hai2007/image2D.git
4*
5* For online documents, please visit
6* https://hai2007.gitee.io/image2d/index.html
7*
8* author 你好2007
9*
10* version 1.16.4
11*
12* build Thu Apr 11 2019
13*
14* Copyright hai2007 < https://hai2007.gitee.io/sweethome/ >
15* Released under the MIT license
16*
17* Date:Sat Apr 16 2022 19:16:07 GMT+0800 (GMT+08:00)
18*/
19
20"use strict";
21
22var _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; };
23
24(function () {
25 'use strict';
26
27 /**
28 * 初始化配置文件
29 * @param {Json} init 默认值
30 * @param {Json} data
31 * @return {Json}
32 */
33
34 var initConfig = function initConfig(init, data) {
35 for (var key in data) {
36 try {
37 init[key] = data[key];
38 } catch (e) {
39 throw new Error("Illegal property value!");
40 }
41 }return init;
42 };
43
44 // 命名空间路径
45 var NAMESPACE = {
46 "svg": "http://www.w3.org/2000/svg",
47 "xhtml": "http://www.w3.org/1999/xhtml",
48 "xlink": "http://www.w3.org/1999/xlink",
49 "xml": "http://www.w3.org/XML/1998/namespace",
50 "xmlns": "http://www.w3.org/2000/xmlns/"
51 };
52
53 // 正则表达式
54 var REGEXP = {
55
56 // 空白字符:http://www.w3.org/TR/css3-selectors/#whitespace
57 "whitespace": "[\\x20\\t\\r\\n\\f]",
58
59 // 空格外的空白字符
60 "blank": "[\\n\\f\\r]",
61
62 // 标志符:http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
63 "identifier": "(?:\\\\.|[\\w-]|[^\0-\\xa0])+"
64 };
65
66 // 记录需要使用xlink命名空间常见的xml属性
67 var XLINK_ATTRIBUTE = ["href", "title", "show", "type", "role", "actuate"];
68
69 /**
70 * 判断一个值是不是Object。
71 *
72 * @param {*} value 需要判断类型的值
73 * @returns {boolean} 如果是Object返回true,否则返回false
74 */
75 function _isObject(value) {
76 var type = typeof value === "undefined" ? "undefined" : _typeof(value);
77 return value != null && (type === 'object' || type === 'function');
78 }
79
80 var toString = Object.prototype.toString;
81
82 /**
83 * 获取一个值的类型字符串[object type]
84 *
85 * @param {*} value 需要返回类型的值
86 * @returns {string} 返回类型字符串
87 */
88 function getType(value) {
89 if (value == null) {
90 return value === undefined ? '[object Undefined]' : '[object Null]';
91 }
92 return toString.call(value);
93 }
94
95 /**
96 * 判断一个值是不是number。
97 *
98 * @param {*} value 需要判断类型的值
99 * @returns {boolean} 如果是number返回true,否则返回false
100 */
101 function _isNumber(value) {
102 return typeof value === 'number' || value !== null && (typeof value === "undefined" ? "undefined" : _typeof(value)) === 'object' && getType(value) === '[object Number]';
103 }
104
105 /**
106 * 判断一个值是不是String。
107 *
108 * @param {*} value 需要判断类型的值
109 * @returns {boolean} 如果是String返回true,否则返回false
110 */
111 function _isString(value) {
112 var type = typeof value === "undefined" ? "undefined" : _typeof(value);
113 return type === 'string' || type === 'object' && value != null && !Array.isArray(value) && getType(value) === '[object String]';
114 }
115
116 /**
117 * 判断一个值是不是Function。
118 *
119 * @param {*} value 需要判断类型的值
120 * @returns {boolean} 如果是Function返回true,否则返回false
121 */
122 function _isFunction(value) {
123 if (!_isObject(value)) {
124 return false;
125 }
126
127 var type = getType(value);
128 return type === '[object Function]' || type === '[object AsyncFunction]' || type === '[object GeneratorFunction]' || type === '[object Proxy]';
129 }
130
131 /**
132 * 判断一个值是不是一个朴素的'对象'
133 * 所谓"纯粹的对象",就是该对象是通过"{}"或"new Object"创建的
134 *
135 * @param {*} value 需要判断类型的值
136 * @returns {boolean} 如果是朴素的'对象'返回true,否则返回false
137 */
138
139 function _isPlainObject(value) {
140 if (value === null || (typeof value === "undefined" ? "undefined" : _typeof(value)) !== 'object' || getType(value) != '[object Object]') {
141 return false;
142 }
143
144 // 如果原型为null
145 if (Object.getPrototypeOf(value) === null) {
146 return true;
147 }
148
149 var proto = value;
150 while (Object.getPrototypeOf(proto) !== null) {
151 proto = Object.getPrototypeOf(proto);
152 }
153 return Object.getPrototypeOf(value) === proto;
154 }
155
156 var domTypeHelp = function domTypeHelp(types, value) {
157 return value !== null && (typeof value === "undefined" ? "undefined" : _typeof(value)) === 'object' && types.indexOf(value.nodeType) > -1 && !_isPlainObject(value);
158 };
159
160 /*!
161 * 💡 - 值类型判断方法
162 * https://github.com/hai2007/tool.js/blob/master/type.js
163 *
164 * author hai2007 < https://hai2007.gitee.io/sweethome >
165 *
166 * Copyright (c) 2020-present hai2007 走一步,再走一步。
167 * Released under the MIT license
168 */
169
170 var isObject = _isObject;
171 var isNumber = _isNumber;
172 var isString = _isString;
173
174 // 引用类型
175 var isFunction = _isFunction;
176 var isArray = function isArray(input) {
177 return Array.isArray(input);
178 };
179
180 // 结点类型
181 var isElement = function isElement(input) {
182 return domTypeHelp([1, 9, 11], input);
183 };
184 var isText = function isText(input) {
185 return domTypeHelp([3], input);
186 };
187
188 /**
189 * 设置svg字符串
190 * @param {dom} target
191 * @param {string} svgstring
192 */
193 var setSVG = function setSVG(target, svgstring) {
194 if ('innerHTML' in SVGElement.prototype === false || 'innerHTML' in SVGSVGElement.prototype === false) {
195
196 // 创建一个非svg结点,用例帮助解析
197 // 这样比直接解析字符串简单
198 var frame = document.createElement("div");
199 frame.innerHTML = svgstring;
200
201 var toSvgNode = function toSvgNode(htmlNode) {
202
203 // 创建svg结点,并挂载属性
204 var svgNode = document.createElementNS(NAMESPACE.svg, htmlNode.tagName.toLowerCase());
205 var attrs = htmlNode.attributes;
206
207 for (var i = 0; attrs && i < attrs.length; i++) {
208
209 // 是否是特殊属性目前靠手工登记
210 if (XLINK_ATTRIBUTE.indexOf(attrs[i].nodeName) >= 0) {
211
212 // 针对特殊的svg属性,追加命名空间
213 svgNode.setAttributeNS(NAMESPACE.xlink, 'xlink:' + attrs[i].nodeName, htmlNode.getAttribute(attrs[i].nodeName));
214 } else {
215 svgNode.setAttribute(attrs[i].nodeName, htmlNode.getAttribute(attrs[i].nodeName));
216 }
217 }
218 return svgNode;
219 };
220
221 var rslNode = toSvgNode(frame.firstChild);
222
223 (function toSVG(pnode, svgPnode) {
224 var node = pnode.firstChild;
225
226 // 如果是文本结点
227 if (isText(node)) {
228 svgPnode.textContent = pnode.innerText;
229 return;
230 }
231
232 // 不是文本结点,就拼接
233 while (node) {
234 var svgNode = toSvgNode(node);
235 svgPnode.appendChild(svgNode);
236 if (node.firstChild) toSVG(node, svgNode);
237 node = node.nextSibling;
238 }
239 })(frame.firstChild, rslNode);
240
241 // 拼接
242 target.appendChild(rslNode);
243 } else {
244
245 // 如果当前浏览器提供了svg类型结点的innerHTML,我们还是使用浏览器提供的
246 target.innerHTML = svgstring;
247 }
248 };
249
250 // 变成指定类型的结点
251 // type可以取:
252 // 1.'HTML',html结点
253 // 2.'SVG',svg结点(默认值)
254 var toNode = function toNode(template, type) {
255 var frame = void 0,
256 childNodes = void 0,
257 frameTagName = 'div';
258 if (type === 'html' || type === 'HTML') {
259
260 // 大部分的标签可以直接使用div作为容器
261 // 部分特殊的需要特殊的容器标签
262
263 if (/^<tr[> ]/.test(template)) {
264
265 frameTagName = "tbody";
266 } else if (/^<th[> ]/.test(template) || /^<td[> ]/.test(template)) {
267
268 frameTagName = "tr";
269 } else if (/^<thead[> ]/.test(template) || /^<tbody[> ]/.test(template)) {
270
271 frameTagName = "table";
272 }
273
274 frame = document.createElement(frameTagName);
275 frame.innerHTML = template;
276
277 // 比如tr标签,它应该被tbody或thead包含
278 // 如果采用别的标签,比如div,这类标签无法生成
279 // 为了方便校对,这里给出提示
280 if (!/</.test(frame.innerHTML)) {
281 throw new Error('This template cannot be generated using div as a container:' + template + "\nPlease contact us: https://github.com/hai2007/image2D/issues");
282 }
283 } else {
284 frame = document.createElementNS(NAMESPACE.svg, 'svg');
285 // 部分浏览器svg元素没有innerHTML
286 setSVG(frame, template);
287 }
288 childNodes = frame.childNodes;
289 for (var i = 0; i < childNodes.length; i++) {
290 if (isElement(childNodes[i])) return childNodes[i];
291 }
292 };
293
294 /**
295 * 变成结点
296 * @param {string} template
297 * @param {string} type
298 * @return {dom} 返回结点
299 */
300 function toNode$1(template, type) {
301
302 // 把传递元素类型和标记进行统一处理
303 if (new RegExp("^" + REGEXP.identifier + "$").test(template)) template = "<" + template + "></" + template + ">";
304
305 var mark = /^<([^(>| )]+)/.exec(template)[1];
306
307 // 画布canvas特殊知道,一定是html
308 if ("canvas" === mark.toLowerCase()) type = 'HTML';
309
310 // 此外,如果没有特殊设定,规定一些标签是html标签
311 if (!isString(type) && [
312
313 // 三大display元素
314 "div", "span", "p",
315
316 // 小元素
317 "em", "i",
318
319 // 关系元素
320 "table", "ul", "ol", "dl", "dt", "li", "dd",
321
322 // 表单相关
323 "form", "input", "button", "textarea",
324
325 // H5结构元素
326 "header", "footer", "article", "section",
327
328 // 标题元素
329 "h1", "h2", "h3", "h4", "h5", "h6",
330
331 // 替换元素
332 "image", "video", "iframe", "object",
333
334 // 资源元素
335 "style", "script", "link",
336
337 // table系列
338 "tr", "td", "th", "tbody", "thead"].indexOf(mark.toLowerCase()) >= 0) type = 'HTML';
339
340 return toNode(template, type);
341 }
342
343 /**
344 * 在指定上下文查找结点
345 * @param {string|dom|array|function|image2D} selector 选择器,必输
346 * @param {dom|'html'|'svg'} context 查找上下文,或标签类型,必输
347 * @return {array|image2D} 结点数组
348 *
349 * 特别注意:
350 * 1.id选择器或者传入的是维护的结点,查找上下文会被忽略
351 * 2.如果selector传入的是一个字符串模板,context可选,其表示模板类型
352 */
353 function sizzle(selector, context) {
354
355 // 如果是字符串
356 // context如果是字符串(应该是'html'或'svg')表示这是生成结点,也走这条路线
357 if (isString(context) || isString(selector)) {
358 selector = selector.trim().replace(new RegExp(REGEXP.blank, 'g'), '');
359
360 // 如果以'<'开头表示是字符串模板
361 if (typeof context == 'string' || /^</.test(selector)) {
362 var node = toNode$1(selector, context);
363 if (isElement(node)) return [node];else return [];
364 }
365
366 // *表示查找全部
367 else if (selector === '*') {
368 return context.getElementsByTagName('*');
369 }
370
371 var id = selector.match(new RegExp('#' + REGEXP.identifier, 'g'));
372 // ID选择器
373 // 此选择器会忽略上下文
374 if (id) {
375 var _node = document.getElementById(id[0].replace('#', ''));
376 if (isElement(_node)) return [_node];else return [];
377 }
378
379 var cls = selector.match(new RegExp('\\.' + REGEXP.identifier, 'g')),
380 tag = selector.match(new RegExp('^' + REGEXP.identifier));
381
382 // 结点和class混合选择器
383 if (tag || cls) {
384 var allNodes = context.getElementsByTagName(tag ? tag[0] : "*"),
385 temp = [];
386 for (var i = 0; i < allNodes.length; i++) {
387 var clazz = " " + allNodes[i].getAttribute('class') + " ",
388 flag = true;
389 for (var j = 0; cls && j < cls.length; j++) {
390 if (!clazz.match(" " + cls[j].replace('.', '') + " ")) {
391 flag = false;
392 break;
393 }
394 }
395 if (flag) temp.push(allNodes[i]);
396 }
397 return temp;
398 }
399
400 // 未知情况,报错
401 else {
402 throw new Error('Unsupported selector:' + selector);
403 }
404 }
405
406 // 如果是结点
407 else if (isElement(selector)) {
408 return [selector];
409 }
410
411 // 如果是数组
412 // 数组中的内容如果不是结点会直接被忽略
413 else if (selector && (selector.constructor === Array || selector.constructor === HTMLCollection || selector.constructor === NodeList)) {
414 var _temp = [];
415 for (var _i = 0; _i < selector.length; _i++) {
416
417 // 如果是结点
418 if (isElement(selector[_i])) _temp.push(selector[_i]);
419
420 // 如果是image2D对象
421 else if (selector[_i] && selector[_i].constructor === image2D) {
422 for (var _j = 0; _j < selector[_i].length; _j++) {
423 _temp.push(selector[_i][_j]);
424 }
425 }
426 }
427 return _temp;
428 }
429
430 // 如果是image2D对象
431 else if (selector && selector.constructor === image2D) {
432 return selector;
433 }
434
435 // 如果是函数
436 else if (isFunction(selector)) {
437 var _allNodes = context.getElementsByTagName('*'),
438 _temp2 = [];
439 for (var _i2 = 0; _i2 < _allNodes.length; _i2++) {
440 // 如果选择器函数返回true,表示当前面对的结点被接受
441 if (selector(_allNodes[_i2])) _temp2.push(_allNodes[_i2]);
442 }
443 return _temp2;
444 }
445
446 // 未知情况,报错
447 else {
448 throw new Error('Unknown selector:' + selector);
449 }
450 }
451
452 /**
453 * 设计需求是:
454 * image2D和image2D(selector[, context])
455 * 分别表示绘图类和绘图对象
456 *
457 * 题外:为什么不选择image2D和new image2D(selector[, context])?
458 * 只是感觉没有前面的写法用起来简洁
459 *
460 * 为了实现需求,第一反应是:
461 * let image2D=function(selector,context){
462 * return new image2D();
463 * };
464 *
465 * 在image2D上挂载静态方法,在image2D.prototype上挂载对象方法,
466 * 看起来稳的很,其实这明显是一个死循环。
467 *
468 * 为了解决这个问题,我们在image2D的原型上定义了一个方法:
469 * image2D.prototype.init=function(selector,context){
470 * return this;
471 * };
472 *
473 * 执行下面的方法:
474 * let temp=image2D.prototype.init(selector, context);
475 * 上面返回的temp很明显就是image2D.prototype,其实就是image2D对象
476 * (例如:new A(),其实就是取A.prototype,这样对比就很好理解了)
477 *
478 * 因此可以改造代码如下:
479 *
480 * 这样image2D和new image2D(selector[, context])就分别表示类和对象。
481 *
482 * 问:看起来是不是实现了?
483 * 答:是的,实现了。
484 * 问:可是总感觉有点不好,说不出为什么。
485 * 答:是不是感觉image2D()打印出来的东西有点多?
486 * 问:是的。
487 *
488 * 事实上,因为直接取image2D.prototype作为new image2D(),
489 * 理论上说,使用上区别不大,唯一不足的是,
490 * 挂载在image2D.prototype上的方法会在打印image2D对象的时候看见,不舒服。
491 *
492 * 为了看起来好看些,代码再次改造:
493 * let image2D = function (selector, context) {
494 * return new image2D.prototype.init(selector, context);
495 * };
496 *
497 * 为了让image2D(selector, context)返回的是image2D对象,需要修改image2D.prototype.init的原型:
498 * image2D.prototype.init.prototype = image2D.prototype;
499 *
500 * 这样:
501 * image2D(selector, context) ==
502 * return new image2D.prototype.init(selector, context) ==
503 * image2D.prototype.init.prototype ==
504 * image2D.prototype ==
505 * new image2D(selector, context)
506 *
507 * 此时需求就实现了,
508 * 而且打印image2D(selector, context)的时候,
509 * 对象上的方法都在原型上,看起来就比较舒服了。
510 */
511
512 var image2D = function image2D(selector, context) {
513 return new image2D.prototype.init(selector, context);
514 };
515
516 image2D.prototype.init = function (selector, context) {
517
518 // 如果没有传递,默认使用document作为上下文
519 this.context = context = context || document;
520
521 // 使用sizzle获取需要维护的结点,并把结点维护到image2D对象中
522 var nodes = sizzle(selector, context),
523 flag = void 0;
524 for (flag = 0; flag < nodes.length; flag++) {
525 this[flag] = nodes[flag];
526 }
527
528 // 设置结点个数
529 this.length = nodes.length;
530 return this;
531 };
532
533 // 扩展方法
534 // 在image2D和image2D.prototype上分别调用extend方法就可以在类和对象上扩展方法了
535 image2D.prototype.extend = image2D.extend = function () {
536
537 var target = arguments[0] || {};
538 var source = arguments[1] || {};
539 var length = arguments.length;
540
541 /*
542 * 确定复制目标和源
543 */
544 if (length === 1) {
545 //如果只有一个参数,目标对象是自己
546 source = target;
547 target = this;
548 }
549 if (!isObject(target)) {
550 //如果目标不是对象或函数,则初始化为空对象
551 target = {};
552 }
553
554 /*
555 * 复制属性到对象上面
556 */
557 for (var key in source) {
558 try {
559 target[key] = source[key];
560 } catch (e) {
561
562 // 为什么需要try{}catch(e){}?
563 // 一些对象的特殊属性不允许覆盖,比如name
564 // 执行:image2D.extend({'name':'新名称'})
565 // 会抛出TypeError
566 throw new Error("Illegal property key:" + key + "!");
567 }
568 }
569
570 return target;
571 };
572
573 image2D.prototype.init.prototype = image2D.prototype;
574
575 /*!
576 * 🔪 - 基本的树结构位置生成算法
577 * https://github.com/hai2007/algorithm.js/blob/master/tree.js
578 *
579 * author hai2007 < https://hai2007.gitee.io/sweethome >
580 *
581 * Copyright (c) 2020-present hai2007 走一步,再走一步。
582 * Released under the MIT license
583 */
584
585 function treeLayout(_config) {
586
587 /**
588 * 无论绘制的树结构是什么样子的
589 * 计算时都假想目标树的样子如下:
590 * 1.根结点在最左边,且上下居中
591 * 2.树是从左往右生长的结构
592 * 3.每个结点都是一块1*1的正方形,top和left分别表示正方形中心的位置
593 */
594
595 var config = _config || {},
596
597 // 维护的树
598 alltreedata,
599
600 // 根结点ID
601 rootid;
602
603 /**
604 * 把内部保存的树结点数据
605 * 计算结束后会调用配置的绘图方法
606 */
607 var update = function update() {
608
609 var beforeDis = [],
610 size = 0,
611 maxDeep = 0;
612 (function positionCalc(pNode, deep) {
613
614 if (deep > maxDeep) maxDeep = deep;
615 var flag;
616 for (flag = 0; flag < pNode.children.length; flag++) {
617 // 因为全部的子结点的位置确定了,父结点的y位置就是子结点的中间位置
618 // 因此有子结点的,先计算子结点
619 positionCalc(alltreedata[pNode.children[flag]], deep + 1);
620 } // left的位置比较简单,deep从0开始编号
621 // 比如deep=0,第一层,left=0+0.5=0.5,也就是根结点
622 alltreedata[pNode.id].left = deep + 0.5;
623 if (flag == 0) {
624
625 // beforeDis是一个数组,用以记录每一层此刻top下边缘(每一层是从上到下)
626 // 比如一层的第一个,top值最小可以取top=0.5
627 // 为了方便计算,beforeDis[deep] == undefined的时候表示现在准备计算的是这层的第一个结点
628 // 因此设置最低上边缘为-0.5
629 if (beforeDis[deep] == undefined) beforeDis[deep] = -0.5;
630 // 父边缘同意的进行初始化
631 if (beforeDis[deep - 1] == undefined) beforeDis[deep - 1] = -0.5;
632
633 // 添加的新结点top值第一种求法:本层上边缘+1(比如上边缘是-0.5,那么top最小是top=-0.5+1=0.5)
634 alltreedata[pNode.id].top = beforeDis[deep] + 1;
635
636 var pTop = beforeDis[deep] + 1 + (alltreedata[pNode.pid].children.length - 1) * 0.5;
637 // 计算的原则是:如果第一种可行,选择第一种,否则必须选择第二种
638 // 判断第一种是否可行的方法就是:如果第一种计算后确定的孩子上边缘不对导致孩子和孩子的前兄弟重合就是可行的
639 if (pTop - 1 < beforeDis[deep - 1])
640 // 必须保证父亲结点和父亲的前一个兄弟保存1的距离,至少
641 // 添加的新结点top值的第二种求法:根据孩子取孩子结点的中心top
642 alltreedata[pNode.id].top = beforeDis[deep - 1] + 1 - (alltreedata[pNode.pid].children.length - 1) * 0.5;
643 } else {
644
645 // 此刻flag!=0
646 // 意味着结点有孩子,那么问题就解决了,直接取孩子的中间即可
647 // 其实,flag==0的分支计算的就是孩子,是没有孩子的叶结点,那是关键
648 alltreedata[pNode.id].top = (alltreedata[pNode.children[0]].top + alltreedata[pNode.children[flag - 1]].top) * 0.5;
649 }
650
651 // 因为计算孩子的时候
652 // 无法掌握父辈兄弟的情况
653 // 可能会出现父亲和兄弟重叠问题
654 if (alltreedata[pNode.id].top <= beforeDis[deep]) {
655 var needUp = beforeDis[deep] + 1 - alltreedata[pNode.id].top;
656 (function doUp(_pid, _deep) {
657 alltreedata[_pid].top += needUp;
658 if (beforeDis[_deep] < alltreedata[_pid].top) beforeDis[_deep] = alltreedata[_pid].top;
659 var _flag;
660 for (_flag = 0; _flag < alltreedata[_pid].children.length; _flag++) {
661 doUp(alltreedata[_pid].children[_flag], _deep + 1);
662 }
663 })(pNode.id, deep);
664 }
665
666 // 计算好一个结点后,需要更新此刻该层的上边缘
667 beforeDis[deep] = alltreedata[pNode.id].top;
668
669 // size在每次计算一个结点后更新,是为了最终绘图的时候知道树有多宽(此处应该叫高)
670 if (alltreedata[pNode.id].top + 0.5 > size) size = alltreedata[pNode.id].top + 0.5;
671 })(alltreedata[rootid], 0);
672
673 // 传递的参数分别表示:记录了位置信息的树结点集合、根结点ID和树的宽
674 return {
675 "node": alltreedata,
676 "root": rootid,
677 "size": size,
678 "deep": maxDeep + 1
679 };
680 };
681
682 /**
683 * 根据配置的层次关系(配置的id,child,root)把原始数据变成内部结构,方便后期位置计算
684 * @param {any} initTree
685 *
686 * tempTree[id]={
687 * "data":原始数据,
688 * "pid":父亲ID,
689 * "id":唯一标识ID,
690 * "children":[cid1、cid2、...]
691 * }
692 */
693 var toInnerTree = function toInnerTree(initTree) {
694
695 var tempTree = {};
696 // 根结点
697 var temp = config.root(initTree),
698 id,
699 rid;
700 id = rid = config.id(temp);
701 tempTree[id] = {
702 "data": temp,
703 "pid": null,
704 "id": id,
705 "children": []
706 };
707
708 var num = 1;
709 // 根据传递的原始数据,生成内部统一结构
710 (function createTree(pdata, pid) {
711 var children = config.child(pdata, initTree),
712 flag;
713 num += children ? children.length : 0;
714 for (flag = 0; children && flag < children.length; flag++) {
715 id = config.id(children[flag]);
716 tempTree[pid].children.push(id);
717 tempTree[id] = {
718 "data": children[flag],
719 "pid": pid,
720 "id": id,
721 "children": []
722 };
723 createTree(children[flag], id);
724 }
725 })(temp, id);
726
727 return {
728 value: [rid, tempTree],
729 num: num
730 };
731 };
732
733 // 可以传递任意格式的树原始数据
734 // 只要配置对应的解析方法即可
735 var tree = function tree(initTree) {
736
737 var treeData = toInnerTree(initTree);
738 alltreedata = treeData.value[1];
739 rootid = treeData.value[0];
740
741 if (treeData.num == 1) {
742 alltreedata[rootid].left = 0.5;
743 alltreedata[rootid].top = 0.5;
744 return {
745 deep: 1,
746 node: alltreedata,
747 root: rootid,
748 size: 1
749 };
750 }
751
752 return update();
753 };
754
755 // 获取根结点的方法:root(initTree)
756 tree.root = function (rootback) {
757 config.root = rootback;
758 return tree;
759 };
760
761 // 获取子结点的方法:child(parentTree,initTree)
762 tree.child = function (childback) {
763 config.child = childback;
764 return tree;
765 };
766
767 // 获取结点ID方法:id(treedata)
768 tree.id = function (idback) {
769 config.id = idback;
770 return tree;
771 };
772
773 return tree;
774 }
775
776 /**
777 * 点(x,y)围绕中心(cx,cy)旋转deg度
778 */
779 var _rotate2 = function _rotate2(cx, cy, deg, x, y) {
780 var cos = Math.cos(deg),
781 sin = Math.sin(deg);
782 return [+((x - cx) * cos - (y - cy) * sin + cx).toFixed(7), +((x - cx) * sin + (y - cy) * cos + cy).toFixed(7)];
783 };
784
785 /**
786 * 点(x,y)沿着向量(ax,ay)方向移动距离d
787 */
788 var _move2 = function _move2(ax, ay, d, x, y) {
789 var sqrt = Math.sqrt(ax * ax + ay * ay);
790 return [+(ax * d / sqrt + x).toFixed(7), +(ay * d / sqrt + y).toFixed(7)];
791 };
792
793 /**
794 * 点(x,y)围绕中心(cx,cy)缩放times倍
795 */
796 var _scale2 = function _scale2(cx, cy, times, x, y) {
797 return [+(times * (x - cx) + cx).toFixed(7), +(times * (y - cy) + cy).toFixed(7)];
798 };
799
800 var dot = function dot(config) {
801
802 config = initConfig({
803 // 前进方向
804 d: [1, 1],
805 // 中心坐标
806 c: [0, 0],
807 // 当前位置
808 p: [0, 0]
809 }, config);
810
811 var dotObj = {
812
813 // 前进方向以当前位置为中心,旋转deg度
814 "rotate": function rotate(deg) {
815 var dPx = config.d[0] + config.p[0],
816 dPy = config.d[1] + config.p[1];
817 var dP = _rotate2(config.p[0], config.p[1], deg, dPx, dPy);
818 config.d = [dP[0] - config.p[0], dP[1] - config.p[1]];
819 return dotObj;
820 },
821
822 // 沿着当前前进方向前进d
823 "move": function move(d) {
824 config.p = _move2(config.d[0], config.d[1], d, config.p[0], config.p[1]);
825 return dotObj;
826 },
827
828 // 围绕中心坐标缩放
829 "scale": function scale(times) {
830 config.p = _scale2(config.c[0], config.c[1], times, config.p[0], config.p[1]);
831 return dotObj;
832 },
833
834 // 当前位置
835 "value": function value() {
836 return config.p;
837 }
838
839 };
840
841 return dotObj;
842 };
843
844 function treeLayout$1(config) {
845
846 config = initConfig({
847
848 // 类型:如果不是下面五种之一,就认为是原始类型
849 // type:LR|RL|BT|TB|circle
850
851 // 如果类型是LR|RL|BT|TB需要设置如下参数
852 // width,height:宽和高
853
854 // 如果类型是circle需要设置如下参数
855 // 1.cx,cy:圆心;2.radius:半径;3.begin-deg,deg:开始和跨越弧度(可选)
856 "begin-deg": 0,
857 "deg": Math.PI * 2
858
859 }, config);
860
861 var treeCalc = treeLayout()
862 // 配置数据格式
863 .root(config.root).child(config.child).id(config.id);
864
865 var treeObj = function treeObj(initData) {
866
867 // 计算初始坐标
868 var orgData = treeCalc(initData);
869
870 // 计算deep
871 for (var key in orgData.node) {
872 orgData.node[key].deep = orgData.node[key].left - 0.5;
873 }
874
875 if (config.type === 'LR' || config.type === 'RL') {
876
877 // 每层间隔
878 var dis1 = config.width / orgData.deep;
879 if ("RL" === config.type) dis1 *= -1;
880 // 兄弟间隔
881 var dis2 = config.height / (orgData.size - -0.5);
882 for (var i in orgData.node) {
883 var node = orgData.node[i];
884 orgData.node[i].left = +(("RL" == config.type ? config.width : 0) - -node.left * dis1).toFixed(7);
885 orgData.node[i].top = +(node.top * dis2).toFixed(7);
886 }
887 } else if (config.type === 'TB' || config.type === 'BT') {
888
889 // 每层间隔
890 var _dis = config.height / orgData.deep;
891 if ("BT" == config.type) _dis *= -1;
892 // 兄弟间隔
893 var _dis2 = config.width / (orgData.size - -0.5);
894 var _left = void 0,
895 _top = void 0;
896 for (var _i3 in orgData.node) {
897 var _node2 = orgData.node[_i3];
898 _left = _node2.left;
899 _top = _node2.top;
900 orgData.node[_i3].top = +(("BT" == config.type ? config.height : 0) - -_left * _dis).toFixed(7);
901 orgData.node[_i3].left = +(_top * _dis2).toFixed(7);
902 }
903 } else if (config.type === 'circle') {
904
905 // 如果只有一个结点
906 if (orgData.deep == 1 && orgData.size == 1) {
907 orgData.node[orgData.root].left = config.cx;
908 orgData.node[orgData.root].top = config.cy;
909 }
910
911 // 如果有多个结点
912 else {
913
914 // 每层间距
915 var _dis3 = config.radius / (orgData.deep - 1);
916 // 兄弟间隔弧度
917 var _dis4 = config.deg / (orgData.size - -0.5);
918 for (var _i4 in orgData.node) {
919 var _node3 = orgData.node[_i4];
920 orgData.node[_i4].deg = (config['begin-deg'] - -_dis4 * _node3.top) % (Math.PI * 2);
921 var pos = _rotate2(config.cx, config.cy, orgData.node[_i4].deg, config.cx - -_dis3 * (_node3.left - 0.5), config.cy);
922 orgData.node[_i4].left = +pos[0];
923 orgData.node[_i4].top = +pos[1];
924 }
925 }
926 }
927
928 // 启动绘图
929 if (isFunction(config.drawer)) {
930
931 // 如果配置了绘图方法,就调用绘图方法
932 config.drawer(orgData);
933 return treeObj;
934 } else {
935
936 // 否则返回数据
937 return orgData;
938 }
939 };
940
941 // 配置
942 treeObj.config = function (_config) {
943 config = initConfig(config, _config);
944 return treeObj;
945 };
946
947 // 设置绘图方法
948 treeObj.drawer = function (drawerback) {
949 config.drawer = drawerback;
950 return treeObj;
951 };
952
953 return treeObj;
954 }
955
956 function pieLayout(config) {
957
958 config = initConfig({
959
960 // 饼图的开始和跨域角度[可选]
961 "begin-deg": -Math.PI / 2,
962 "deg": Math.PI * 2,
963
964 // 饼图中一个瓣的中心参考半径,可以有多个[可选]
965 "radius": []
966 // "cx": "",
967 // "cy": "",
968
969 // 设置数据结构[必选]
970 // "value": function (data, key, index) { }
971
972 }, config);
973
974 if (!isFunction(config.value)) {
975 throw new Error('config.value must be a function!');
976 }
977
978 var pieObj = function pieObj(initData) {
979
980 var i = 0,
981 innerData = [],
982 allData = 0;
983 for (var key in initData) {
984 innerData.push({
985 "value": config.value(initData[key], key, i),
986 "data": initData[key],
987 "key": key,
988 "index": i,
989 "dots": []
990 });
991 allData += innerData[i].value;
992 i += 1;
993 }
994
995 for (i = 0; i < innerData.length; i++) {
996
997 // 起始弧度
998 innerData[i].beginDeg = i === 0 ? config['begin-deg'] : innerData[i - 1].beginDeg + innerData[i - 1].deg;
999
1000 // 百分比
1001 var percent = innerData[i].value / allData;
1002
1003 // 跨越弧度
1004 innerData[i].deg = percent * config.deg;
1005
1006 innerData[i].percent = new Number(percent * 100).toFixed(2);
1007 }
1008
1009 // 中心点(用于辅助绘制折线)
1010 if (isNumber(config.cx) && isNumber(config.cy)) {
1011 for (i = 0; i < config.radius.length; i++) {
1012
1013 for (var j = 0; j < innerData.length; j++) {
1014 innerData[j].dots.push(_rotate2(config.cx, config.cy, innerData[j].beginDeg + innerData[j].deg * 0.5, config.cx + config.radius[i], config.cy));
1015 }
1016 }
1017 }
1018
1019 // 启动绘图
1020 if (isFunction(config.drawer)) {
1021 config.drawer(innerData);
1022 return pieObj;
1023 } else {
1024 return innerData;
1025 }
1026 };
1027
1028 // 配置
1029 pieObj.config = function (_config) {
1030 config = initConfig(config, _config);
1031 return pieObj;
1032 };
1033
1034 // 设置绘图方法
1035 pieObj.drawer = function (drawerback) {
1036 config.drawer = drawerback;
1037 return pieObj;
1038 };
1039
1040 return pieObj;
1041 }
1042
1043 /**
1044 * 在(a,b,c)方向位移d
1045 */
1046 function _move(d, a, b, c) {
1047 c = c || 0;
1048 var sqrt = Math.sqrt(a * a + b * b + c * c);
1049 return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, a * d / sqrt, b * d / sqrt, c * d / sqrt, 1];
1050 }
1051
1052 /**
1053 * 围绕0Z轴旋转
1054 * 其它的旋转可以借助transform实现
1055 * 旋转角度单位采用弧度制
1056 */
1057 function _rotate(deg) {
1058 var sin = Math.sin(deg),
1059 cos = Math.cos(deg);
1060 return [cos, sin, 0, 0, -sin, cos, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
1061 }
1062
1063 /**
1064 * 围绕圆心x、y和z分别缩放xTimes, yTimes和zTimes倍
1065 */
1066 function _scale(xTimes, yTimes, zTimes, cx, cy, cz) {
1067 cx = cx || 0;cy = cy || 0;cz = cz || 0;
1068 return [xTimes, 0, 0, 0, 0, yTimes, 0, 0, 0, 0, zTimes, 0, cx - cx * xTimes, cy - cy * yTimes, cz - cz * zTimes, 1];
1069 }
1070
1071 /**
1072 * 针对任意射线(a1,b1,c1)->(a2,b2,c2)
1073 * 计算出二个变换矩阵
1074 * 分别为:任意射线变成OZ轴变换矩阵 + OZ轴变回原来的射线的变换矩阵
1075 */
1076 function _transform(a1, b1, c1, a2, b2, c2) {
1077
1078 if (typeof a1 === 'number' && typeof b1 === 'number') {
1079
1080 // 如果设置二个点
1081 // 表示二维上围绕某个点旋转
1082 if (typeof c1 !== 'number') {
1083 c1 = 0;a2 = a1;b2 = b1;c2 = 1;
1084 }
1085 // 只设置三个点(设置不足六个点都认为只设置了三个点)
1086 // 表示围绕从原点出发的射线旋转
1087 else if (typeof a2 !== 'number' || typeof b2 !== 'number' || typeof c2 !== 'number') {
1088 a2 = a1;b2 = b1;c2 = c1;a1 = 0;b1 = 0;c1 = 0;
1089 }
1090
1091 if (a1 == a2 && b1 == b2 && c1 == c2) throw new Error('It\'s not a legitimate ray!');
1092
1093 var sqrt1 = Math.sqrt((a2 - a1) * (a2 - a1) + (b2 - b1) * (b2 - b1)),
1094 cos1 = sqrt1 != 0 ? (b2 - b1) / sqrt1 : 1,
1095 sin1 = sqrt1 != 0 ? (a2 - a1) / sqrt1 : 0,
1096 b = (a2 - a1) * sin1 + (b2 - b1) * cos1,
1097 c = c2 - c1,
1098 sqrt2 = Math.sqrt(b * b + c * c),
1099 cos2 = sqrt2 != 0 ? c / sqrt2 : 1,
1100 sin2 = sqrt2 != 0 ? b / sqrt2 : 0;
1101
1102 return [
1103
1104 // 任意射线变成OZ轴变换矩阵
1105 [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],
1106
1107 // OZ轴变回原来的射线的变换矩阵
1108 [cos1, -sin1, 0, 0, cos2 * sin1, cos2 * cos1, -sin2, 0, sin1 * sin2, cos1 * sin2, cos2, 0, a1, b1, c1, 1]];
1109 } else {
1110 throw new Error('a1 and b1 is required!');
1111 }
1112 }
1113
1114 // 二个4x4矩阵相乘
1115 // 或矩阵和齐次坐标相乘
1116 var _multiply = function _multiply(matrix4, param) {
1117 var newParam = [];
1118 for (var i = 0; i < 4; i++) {
1119 for (var j = 0; j < param.length / 4; j++) {
1120 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];
1121 }
1122 }return newParam;
1123 };
1124
1125 /*!
1126 * 💡 - 列主序存储的4x4矩阵
1127 * https://github.com/hai2007/tool.js/blob/master/Matrix4.js
1128 *
1129 * author hai2007 < https://hai2007.gitee.io/sweethome >
1130 *
1131 * Copyright (c) 2020-present hai2007 走一步,再走一步。
1132 * Released under the MIT license
1133 */
1134
1135 function Matrix4(initMatrix4) {
1136
1137 var matrix4 = initMatrix4 || [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
1138
1139 var matrix4Obj = {
1140
1141 // 移动
1142 "move": function move(dis, a, b, c) {
1143 matrix4 = _multiply(_move(dis, a, b, c), matrix4);
1144 return matrix4Obj;
1145 },
1146
1147 // 旋转
1148 "rotate": function rotate(deg, a1, b1, c1, a2, b2, c2) {
1149 var matrix4s = _transform(a1, b1, c1, a2, b2, c2);
1150 matrix4 = _multiply(_multiply(_multiply(matrix4s[1], _rotate(deg)), matrix4s[0]), matrix4);
1151 return matrix4Obj;
1152 },
1153
1154 // 缩放
1155 "scale": function scale(xTimes, yTimes, zTimes, cx, cy, cz) {
1156 matrix4 = _multiply(_scale(xTimes, yTimes, zTimes, cx, cy, cz), matrix4);
1157 return matrix4Obj;
1158 },
1159
1160 // 乘法
1161 // 可以传入一个矩阵(matrix4,flag)
1162 "multiply": function multiply(newMatrix4, flag) {
1163 matrix4 = flag ? _multiply(matrix4, newMatrix4) : _multiply(newMatrix4, matrix4);
1164 return matrix4Obj;
1165 },
1166
1167 // 对一个坐标应用变换
1168 // 齐次坐标(x,y,z,w)
1169 "use": function use(x, y, z, w) {
1170 // w为0表示点位于无穷远处,忽略
1171 z = z || 0;w = w || 1;
1172 var temp = _multiply(matrix4, [x, y, z, w]);
1173 temp[0] = +temp[0].toFixed(7);
1174 temp[1] = +temp[1].toFixed(7);
1175 temp[2] = +temp[2].toFixed(7);
1176 temp[3] = +temp[3].toFixed(7);
1177 return temp;
1178 },
1179
1180 // 矩阵的值
1181 "value": function value() {
1182 return matrix4;
1183 }
1184
1185 };
1186
1187 return matrix4Obj;
1188 }
1189
1190 //当前正在运动的动画的tick函数堆栈
1191 var $timers = [];
1192 //唯一定时器的定时间隔
1193 var $interval = 13;
1194 //指定了动画时长duration默认值
1195 var $speeds = 400;
1196 //定时器ID
1197 var $timerId = null;
1198
1199 /*!
1200 * 💡 - 动画轮播
1201 * https://github.com/hai2007/tool.js/blob/master/animation.js
1202 *
1203 * author hai2007 < https://hai2007.gitee.io/sweethome >
1204 *
1205 * Copyright (c) 2020-present hai2007 走一步,再走一步。
1206 * Released under the MIT license
1207 */
1208
1209 /**
1210 * @param {function} doback 轮询函数,有一个形参deep,0-1,表示执行进度
1211 * @param {number} duration 动画时长,可选
1212 * @param {function} callback 动画结束回调,可选,有一个形参deep,0-1,表示执行进度
1213 *
1214 * @returns {function} 返回一个函数,调用该函数,可以提前结束动画
1215 */
1216 function animation(doback, duration, callback) {
1217
1218 // 如果没有传递时间,使用内置默认值
1219 if (arguments.length < 2) duration = $speeds;
1220
1221 var clock = {
1222 //把tick函数推入堆栈
1223 "timer": function timer(tick, duration, callback) {
1224 if (!tick) {
1225 throw new Error('Tick is required!');
1226 }
1227 var id = new Date().valueOf() + "_" + (Math.random() * 1000).toFixed(0);
1228 $timers.push({
1229 "id": id,
1230 "createTime": new Date(),
1231 "tick": tick,
1232 "duration": duration,
1233 "callback": callback
1234 });
1235 clock.start();
1236 return id;
1237 },
1238
1239 //开启唯一的定时器timerId
1240 "start": function start() {
1241 if (!$timerId) {
1242 $timerId = setInterval(clock.tick, $interval);
1243 }
1244 },
1245
1246 //被定时器调用,遍历timers堆栈
1247 "tick": function tick() {
1248 var createTime,
1249 flag,
1250 tick,
1251 callback,
1252 timer,
1253 duration,
1254 passTime,
1255 timers = $timers;
1256 $timers = [];
1257 $timers.length = 0;
1258 for (flag = 0; flag < timers.length; flag++) {
1259 //初始化数据
1260 timer = timers[flag];
1261 createTime = timer.createTime;
1262 tick = timer.tick;
1263 duration = timer.duration;
1264 callback = timer.callback;
1265
1266 //执行
1267 passTime = (+new Date() - createTime) / duration;
1268 passTime = passTime > 1 ? 1 : passTime;
1269 tick(passTime);
1270 if (passTime < 1 && timer.id) {
1271 //动画没有结束再添加
1272 $timers.push(timer);
1273 } else if (callback) {
1274 callback(passTime);
1275 }
1276 }
1277 if ($timers.length <= 0) {
1278 clock.stop();
1279 }
1280 },
1281
1282 //停止定时器,重置timerId=null
1283 "stop": function stop() {
1284 if ($timerId) {
1285 clearInterval($timerId);
1286 $timerId = null;
1287 }
1288 }
1289 };
1290
1291 var id = clock.timer(function (deep) {
1292 //其中deep为0-1,表示改变的程度
1293 doback(deep);
1294 }, duration, callback);
1295
1296 // 返回一个函数
1297 // 用于在动画结束前结束动画
1298 return function () {
1299 var i;
1300 for (i in $timers) {
1301 if ($timers[i].id == id) {
1302 $timers[i].id = undefined;
1303 return;
1304 }
1305 }
1306 };
1307 }
1308
1309 /**
1310 * 初始化配置文件
1311 *
1312 * @param {Json} init 默认值
1313 * @param {Json} data
1314 * @return {Json}
1315 */
1316 function initConfig$1(init, data) {
1317 for (var key in data) {
1318 try {
1319 init[key] = data[key];
1320 } catch (e) {
1321 throw new Error("Illegal property value!");
1322 }
1323 }return init;
1324 }
1325
1326 /*!
1327 * 💡 - Hermite三次插值
1328 * https://github.com/hai2007/tool.js/blob/master/Hermite.js
1329 *
1330 * author hai2007 < https://hai2007.gitee.io/sweethome >
1331 *
1332 * Copyright (c) 2020-present hai2007 走一步,再走一步。
1333 * Released under the MIT license
1334 */
1335
1336 function hermite(config) {
1337
1338 config = initConfig$1({
1339 // 张弛系数
1340 "u": 0.5
1341 }, config);
1342
1343 var MR, a, b;
1344
1345 /**
1346 * 根据x值返回y值
1347 * @param {Number} x
1348 */
1349 var hermite = function hermite(x) {
1350 if (MR) {
1351 var sx = (x - a) / (b - a),
1352 sx2 = sx * sx,
1353 sx3 = sx * sx2;
1354 var sResult = sx3 * MR[0] + sx2 * MR[1] + sx * MR[2] + MR[3];
1355 return sResult * (b - a);
1356 } else throw new Error('You shoud first set the position!');
1357 };
1358
1359 /**
1360 * 设置点的位置
1361 * @param {Number} x1 左边点的位置
1362 * @param {Number} y1
1363 * @param {Number} x2 右边点的位置
1364 * @param {Number} y2
1365 * @param {Number} s1 二个点的斜率
1366 * @param {Number} s2
1367 */
1368 hermite.setP = function (x1, y1, x2, y2, s1, s2) {
1369 if (x1 < x2) {
1370 // 记录原始尺寸
1371 a = x1;b = x2;
1372 var p3 = config.u * s1,
1373 p4 = config.u * s2;
1374 // 缩放到[0,1]定义域
1375 y1 /= x2 - x1;
1376 y2 /= x2 - x1;
1377 // MR是提前计算好的多项式通解矩阵
1378 // 为了加速计算
1379 // 如上面说的
1380 // 统一在[0,1]上计算后再通过缩放和移动恢复
1381 // 避免了动态求解矩阵的麻烦
1382 MR = [2 * y1 - 2 * y2 + p3 + p4, 3 * y2 - 3 * y1 - 2 * p3 - p4, p3, y1];
1383 } else throw new Error('The point x-position should be increamented!');
1384 return hermite;
1385 };
1386
1387 return hermite;
1388 }
1389
1390 /**
1391 * 轮询动画
1392 * @param {function} doback 轮询触发方法
1393 * @param {number} time 动画时长,可选
1394 * @param {function} callback 动画结束回调,可选
1395 * @param {array|string} timing 动画进度控制参数,可选
1396 *
1397 * @return {function} stop函数,可以提前停止动画
1398 */
1399 function animation$1(doback, time, callback, timing) {
1400
1401 if (!isFunction(callback)) {
1402 timing = callback;
1403 callback = false;
1404 }
1405
1406 // 获取插值计算参数
1407 var transition_timing = {
1408 "ease": [0.25, 0.1, 0.5, 1],
1409 "ease-in": [0.5, 0.0, 0.75, 0.6],
1410 "ease-in-out": [0.43, 0.01, 0.58, 1],
1411 "ease-out": [0.25, 0.6, 0.5, 1],
1412 "linear": "default"
1413 }[timing] || timing;
1414
1415 var transition_timing_function = function transition_timing_function(deep) {
1416 return deep;
1417 };
1418 if (transition_timing && isArray(transition_timing) && transition_timing.length == 4) {
1419 transition_timing_function = hermite({
1420 "u": 1
1421 }).setP(0, 0, 1, 1, transition_timing[1] / transition_timing[0], (1 - transition_timing[3]) / (1 - transition_timing[2]));
1422 }
1423
1424 return animation(function (deep) {
1425 doback(transition_timing_function(deep));
1426 }, time, function (deep) {
1427 if (isFunction(callback)) {
1428 if (deep != 1) deep = transition_timing_function(deep);
1429 callback(deep);
1430 }
1431 });
1432 }
1433
1434 /**
1435 * Cardinal三次插值
1436 * ----------------------------
1437 * Hermite拟合的计算是,确定两个点和两个点的斜率
1438 * 用一个y=ax(3)+bx(2)+cx+d的三次多项式来求解
1439 * 而Cardinal是建立在此基础上
1440 * 给定需要拟合的两个点和第一个点的前一个点+最后一个点的后一个点
1441 * 第一个点的斜率由第一个点的前一个点和第二个点的斜率确定
1442 * 第二个点的斜率由第一个点和第二个点的后一个点的斜率确定
1443 */
1444
1445 function cardinal(config) {
1446
1447 config = initConfig({
1448 // 该参数用于调整曲线走势,默认数值t=0,分水岭t=-1,|t-(-1)|的值越大,曲线走势调整的越严重
1449 "t": 0
1450 }, config);
1451
1452 var HS = void 0,
1453 i = void 0;
1454
1455 // 根据x值返回y值
1456 var cardinal = function cardinal(x) {
1457
1458 if (HS) {
1459 i = -1;
1460 // 寻找记录x实在位置的区间
1461 // 这里就是寻找对应的拟合函数
1462 while (i + 1 < HS.x.length && (x > HS.x[i + 1] || i == -1 && x >= HS.x[i + 1])) {
1463 i += 1;
1464 }
1465 if (i == -1 || i >= HS.h.length) throw new Error('Coordinate crossing!');
1466 return HS.h[i](x);
1467 } else {
1468 throw new Error('You shoud first set the position!');
1469 }
1470 };
1471
1472 // 设置张弛系数【应该在点的位置设置前设置】
1473 cardinal.setT = function (t) {
1474
1475 if (typeof t === 'number') {
1476 config.t = t;
1477 } else {
1478 throw new Error('Expecting a figure!');
1479 }
1480 return cardinal;
1481 };
1482
1483 // 设置点的位置
1484 // 参数格式:[[x,y],[x,y],...]
1485 // 至少两个点
1486 cardinal.setP = function (points) {
1487
1488 HS = {
1489 "x": [],
1490 "h": []
1491 };
1492 var flag = void 0,
1493 slope = (points[1][1] - points[0][1]) / (points[1][0] - points[0][0]),
1494 temp = void 0;
1495 HS.x[0] = points[0][0];
1496 for (flag = 1; flag < points.length; flag++) {
1497 if (points[flag][0] <= points[flag - 1][0]) throw new Error('The point position should be increamented!');
1498 HS.x[flag] = points[flag][0];
1499 // 求点斜率
1500 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]);
1501 // 求解两个点之间的拟合方程
1502 // 第一个点的前一个点直接取第一个点
1503 // 最后一个点的后一个点直接取最后一个点
1504 HS.h[flag - 1] = hermite({
1505 "u": (1 - config.t) * 0.5
1506 }).setP(points[flag - 1][0], points[flag - 1][1], points[flag][0], points[flag][1], slope, temp);
1507 slope = temp;
1508 }
1509 return cardinal;
1510 };
1511
1512 return cardinal;
1513 }
1514
1515 /**
1516 * 返回渲染后的CSS样式值
1517 * @param {DOM} dom 目标结点
1518 * @param {String} name 属性名称(可选)
1519 * @return {String}
1520 */
1521 function getStyle(dom, name) {
1522
1523 // 获取结点的全部样式
1524 var allStyle = document.defaultView && document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(dom, null) : dom.currentStyle;
1525
1526 // 如果没有指定属性名称,返回全部样式
1527 return isString(name) ? allStyle.getPropertyValue(name) : allStyle;
1528 }
1529
1530 // 把颜色统一转变成rgba(x,x,x,x)格式
1531 // 返回数字数组[r,g,b,a]
1532 var formatColor = function formatColor(color) {
1533 var colorNode = document.getElementsByTagName('head')[0];
1534 colorNode.style['color'] = color;
1535 var rgba = getStyle(colorNode, 'color');
1536 var rgbaArray = rgba.replace(/^rgba?\(([^)]+)\)$/, '$1').split(new RegExp('\\,' + REGEXP.whitespace));
1537 return [+rgbaArray[0], +rgbaArray[1], +rgbaArray[2], rgbaArray[3] == undefined ? 1 : +rgbaArray[3]];
1538 };
1539
1540 // 获取一组随机色彩
1541 var getRandomColors = function getRandomColors(num, alpha) {
1542 if (!(alpha && alpha >= 0 && alpha <= 1)) alpha = 1;
1543 var temp = [];
1544 for (var flag = 1; flag <= num; flag++) {
1545 temp.push('rgba(' + (Math.random(1) * 230 + 20).toFixed(0) + ',' + (Math.random(1) * 230 + 20).toFixed(0) + ',' + (Math.random(1) * 230 + 20).toFixed(0) + ',' + alpha + ')');
1546 }
1547 return temp;
1548 };
1549
1550 // 获取一组循环色彩
1551 var getLoopColors = function getLoopColors(num, alpha) {
1552 if (!(alpha && alpha >= 0 && alpha <= 1)) alpha = 1;
1553 // 颜色集合
1554 var colorList = ['rgba(84,112,198,' + alpha + ")", 'rgba(145,204,117,' + alpha + ")", 'rgba(250,200,88,' + alpha + ")", 'rgba(238,102,102,' + alpha + ")", 'rgba(115,192,222,' + alpha + ")", 'rgba(59,162,114,' + alpha + ")", 'rgba(252,132,82,' + alpha + ")", 'rgba(154,96,180,' + alpha + ")", 'rgba(234,124,204,' + alpha + ")"];
1555
1556 var colors = [];
1557
1558 // 根据情况返回颜色数组
1559 if (num <= colorList.length) {
1560 // 这种情况就不需要任何处理
1561 return colorList;
1562 } else {
1563 // 如果正好是集合长度的倍数
1564 if (num % colorList.length == 0) {
1565 // 将颜色数组循环加入后再返回
1566 for (var i = 0; i < num / colorList.length; i++) {
1567 colors = colors.concat(colorList);
1568 }
1569 } else {
1570 for (var j = 1; j < num / colorList.length; j++) {
1571 colors = colors.concat(colorList);
1572 }
1573 // 防止最后一个颜色和第一个颜色重复
1574 if (num % colorList.length == 1) {
1575 colors = colors.concat(colorList[4]);
1576 } else {
1577 for (var k = 0; k < num % colorList.length; k++) {
1578 colors = colors.concat(colorList[k]);
1579 }
1580 }
1581 }
1582 }
1583
1584 // 返回结果
1585 return colors;
1586 };
1587
1588 /**
1589 * 绑定事件
1590 * @param {string} eventType
1591 * @param {function} callback
1592 */
1593 var bind = function bind(eventType, callback) {
1594
1595 if (window.attachEvent) {
1596 for (var flag = 0; flag < this.length; flag++) {
1597 this[flag].attachEvent("on" + eventType, callback);
1598 } // 后绑定的先执行
1599 } else {
1600 for (var _flag2 = 0; _flag2 < this.length; _flag2++) {
1601 this[_flag2].addEventListener(eventType, callback, false);
1602 } // 捕获
1603 }
1604
1605 return this;
1606 };
1607
1608 /**
1609 * 解除绑定事件
1610 * @param {string} eventType
1611 * @param {function} handler
1612 */
1613 var unbind = function unbind(eventType, handler) {
1614
1615 if (window.detachEvent) {
1616 for (var flag = 0; flag < this.length; flag++) {
1617 this[flag].detachEvent("on" + eventType, handler);
1618 }
1619 } else {
1620 for (var _flag3 = 0; _flag3 < this.length; _flag3++) {
1621 this[_flag3].removeEventListener(eventType, handler, false);
1622 }
1623 }
1624
1625 return this;
1626 };
1627
1628 /**
1629 * 获取鼠标相对特定元素左上角位置
1630 * @param {Event} event
1631 */
1632 var position = function position(event) {
1633
1634 // 返回元素的大小及其相对于视口的位置
1635 var bounding = this[0].getBoundingClientRect();
1636
1637 if (!event || !event.clientX) throw new Error('Event is necessary!');
1638 return {
1639
1640 // 鼠标相对元素位置 = 鼠标相对窗口坐标 - 元素相对窗口坐标
1641 "x": event.clientX - bounding.left,
1642 "y": event.clientY - bounding.top
1643 };
1644 };
1645
1646 /**
1647 * 阻止冒泡
1648 * @param {Event} event
1649 */
1650 var stopPropagation = function stopPropagation(event) {
1651 event = event || window.event;
1652 if (event.stopPropagation) {
1653 //这是其他非IE浏览器
1654 event.stopPropagation();
1655 } else {
1656 event.cancelBubble = true;
1657 }
1658 return this;
1659 };
1660
1661 /**
1662 * 阻止默认事件
1663 * @param {Event} event
1664 */
1665 var preventDefault = function preventDefault(event) {
1666 event = event || window.event;
1667 if (event.preventDefault) {
1668 event.preventDefault();
1669 } else {
1670 event.returnValue = false;
1671 }
1672 return this;
1673 };
1674
1675 /* 等角斜方位投影 */
1676
1677 var
1678 // 围绕X轴旋转
1679 _rotateX = function _rotateX(deg, x, y, z) {
1680 var cos = Math.cos(deg),
1681 sin = Math.sin(deg);
1682 return [x, y * cos - z * sin, y * sin + z * cos];
1683 },
1684
1685 // 围绕Y轴旋转
1686 _rotateY = function _rotateY(deg, x, y, z) {
1687 var cos = Math.cos(deg),
1688 sin = Math.sin(deg);
1689 return [z * sin + x * cos, y, z * cos - x * sin];
1690 },
1691
1692 // 围绕Z轴旋转
1693 _rotateZ = function _rotateZ(deg, x, y, z) {
1694 var cos = Math.cos(deg),
1695 sin = Math.sin(deg);
1696 return [x * cos - y * sin, x * sin + y * cos, z];
1697 };
1698
1699 var p = [];
1700
1701 function eoap(config, longitude, latitude) {
1702 /**
1703 * 通过旋转的方法
1704 * 先旋转出点的位置
1705 * 然后根据把地心到旋转中心的这条射线变成OZ这条射线的变换应用到初始化点上
1706 * 这样求的的点的x,y就是最终结果
1707 *
1708 * 计算过程:
1709 * 1.初始化点的位置是p(x,0,0),其中x的值是地球半径除以缩放倍速
1710 * 2.根据点的纬度对p进行旋转,旋转后得到的p的坐标纬度就是目标纬度
1711 * 3.同样的对此刻的p进行经度的旋转,这样就获取了极点作为中心点的坐标
1712 * 4.接着想象一下为了让旋转中心移动到极点需要进行旋转的经纬度是多少,记为lo和la
1713 * 5.然后再对p进行经度度旋转lo获得新的p
1714 * 6.然后再对p进行纬度旋转la获得新的p
1715 * 7.旋转结束
1716 *
1717 * 特别注意:第5和第6步顺序一定不可以调换,原因来自经纬度定义上
1718 * 【除了经度为0的位置,不然纬度的旋转会改变原来的经度值,反过来不会】
1719 *
1720 */
1721 p = _rotateY((360 - latitude) / 180 * Math.PI, 100 * config.scale, 0, 0);
1722 p = _rotateZ(longitude / 180 * Math.PI, p[0], p[1], p[2]);
1723 p = _rotateZ((90 - config.center[0]) / 180 * Math.PI, p[0], p[1], p[2]);
1724 p = _rotateX((90 - config.center[1]) / 180 * Math.PI, p[0], p[1], p[2]);
1725
1726 return [-p[0], //加-号是因为浏览器坐标和地图不一样
1727 p[1], p[2]];
1728 }
1729
1730 function map(_config) {
1731
1732 var config = initConfig({
1733
1734 // 默认使用「等角斜方位投影」
1735 type: 'eoap',
1736
1737 // 缩放比例
1738 scale: 1,
1739
1740 // 投影中心经纬度
1741 center: [107, 36]
1742
1743 }, _config);
1744
1745 var map = function map(longitude, latitude) {
1746
1747 switch (config.type) {
1748 case 'eoap':
1749 {
1750 return eoap(config, longitude, latitude);
1751 }
1752 default:
1753 {
1754 throw new Error('Map type configuration error!');
1755 }
1756 }
1757 };
1758
1759 // 修改配置
1760 map.config = function (_config) {
1761 config = initConfig(config, _config);
1762 return map;
1763 };
1764
1765 return map;
1766 }
1767
1768 /*!
1769 * 💡 - 刻度尺刻度求解
1770 * https://github.com/hai2007/tool.js/blob/master/ruler.js
1771 *
1772 * author hai2007 < https://hai2007.gitee.io/sweethome >
1773 *
1774 * Copyright (c) 2021-present hai2007 走一步,再走一步。
1775 * Released under the MIT license
1776 */
1777
1778 // 需要注意的是,实际的间距个数可能是 num-1 或 num 或 num+1 或 1
1779 function ruler(maxValue, minValue, num) {
1780
1781 // 如果最大值最小值反了
1782 if (maxValue < minValue) {
1783 var temp = minValue;
1784 minValue = maxValue;
1785 maxValue = temp;
1786 }
1787
1788 // 如果相等
1789 else if (maxValue == minValue) {
1790 return [maxValue];
1791 }
1792
1793 // 计算最终小数点应该保留的位数
1794 var dotMaxNum = (maxValue + ".").split('.')[1].length;
1795 var dotMinNum = (minValue + ".").split('.')[1].length;
1796 var dotNum = dotMaxNum > dotMinNum ? dotMaxNum : dotMinNum;
1797
1798 // 为了变成 -100 ~ 100 需要放大或者缩小的倍数
1799 var times100 = function (_value) {
1800
1801 // 先确定基调,是放大还是缩小
1802 var _times100_base = _value < 100 && _value > -100 ? 10 : 0.1;
1803
1804 // 记录当前缩放倍数
1805 var _times100 = 1,
1806 _tiemsValue = _value;
1807
1808 while (_times100_base == 10 ?
1809 // 如果是放大,超过 -100 ~ 100 就应该停止
1810 _tiemsValue >= -100 && _tiemsValue <= 100 :
1811 // 如果是缩小,进入 -100 ~ 100 就应该停止
1812 _tiemsValue <= -100 || _tiemsValue >= 100) {
1813
1814 _times100 *= _times100_base;
1815 _tiemsValue *= _times100_base;
1816 }
1817
1818 return _times100;
1819 }(
1820
1821 // 根据差值来缩放
1822 maxValue - minValue);
1823
1824 // 求解出 -100 ~ 100 的最佳间距值 后直接转换原来的倍数
1825 var distance = +(Math.ceil((maxValue - minValue) * times100 / num) / times100).toFixed(dotNum);
1826
1827 if (distance <= 0) return [minValue, maxValue];
1828
1829 // 最小值,也就是起点
1830 var begin = Math.floor(minValue / distance) * distance;
1831
1832 var rulerArray = [],
1833 index;
1834 // 获取最终的刻度尺数组
1835 rulerArray.push(begin);
1836 for (index = 1; rulerArray[rulerArray.length - 1] < maxValue; index++) {
1837 rulerArray.push(+(begin + distance * index).toFixed(dotNum));
1838 }
1839 rulerArray[0] = +begin.toFixed(dotNum);
1840
1841 // 最后,进行校对
1842 var _rulerArray = [rulerArray[0]];
1843 for (var i = 1; i < rulerArray.length; i++) {
1844 if (_rulerArray[_rulerArray.length - 1] != rulerArray[i]) {
1845 _rulerArray.push(rulerArray[i]);
1846 }
1847 }
1848 return _rulerArray;
1849 }
1850
1851 // 刻度计算
1852 function ruler$1(maxValue, minValue) {
1853 var num = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 5;
1854
1855
1856 var rulerArray = ruler(maxValue, minValue, num);
1857
1858 return {
1859 min: rulerArray[0],
1860 max: rulerArray[rulerArray.length - 1],
1861 distance: rulerArray.length <= 1 ? 0 : rulerArray[1] - rulerArray[0],
1862 num: rulerArray.length - 1,
1863 ruler: rulerArray
1864 };
1865 }
1866
1867 /**
1868 * 把当前维护的结点加到目标结点内部的结尾
1869 * @param {selector} target
1870 * @return {image2D}
1871 */
1872 var appendTo = function appendTo(target, context) {
1873 var nodes = sizzle(target, context || document);
1874 if (nodes.length > 0) {
1875 for (var i = 0; i < this.length; i++) {
1876 nodes[0].appendChild(this[i]);
1877 }
1878 } else {
1879 throw new Error('Target empty!');
1880 }
1881 return this;
1882 };
1883
1884 /**
1885 * 把当前维护的结点加到目标结点内部的开头
1886 * @param {selector} target
1887 * @return {image2D}
1888 */
1889 var prependTo = function prependTo(target, context) {
1890 var nodes = sizzle(target, context || document);
1891 if (nodes.length > 0) {
1892 for (var i = 0; i < this.length; i++) {
1893 nodes[0].insertBefore(this[i], nodes[0].childNodes[0]);
1894 }
1895 } else {
1896 throw new Error('Target empty!');
1897 }
1898 return this;
1899 };
1900
1901 /**
1902 * 把当前维护的结点加到目标结点之后
1903 * @param {selector} target
1904 * @return {image2D}
1905 */
1906 var afterTo = function afterTo(target, context) {
1907 var nodes = sizzle(target, context || document);
1908 if (nodes.length > 0) {
1909 for (var i = 0; i < this.length; i++) {
1910 //如果第二个参数undefined,在结尾追加,目的一样达到
1911 nodes[0].parentNode.insertBefore(this[i], nodes[0].nextSibling);
1912 }
1913 } else {
1914 throw new Error('Target empty!');
1915 }
1916 return this;
1917 };
1918
1919 /**
1920 * 把当前维护的结点加到目标结点之前
1921 * @param {selector} target
1922 * @return {image2D}
1923 */
1924 var beforeTo = function beforeTo(target, context) {
1925 var nodes = sizzle(target, context || document);
1926 if (nodes.length > 0) {
1927 for (var i = 0; i < this.length; i++) {
1928 nodes[0].parentNode.insertBefore(this[i], nodes[0]);
1929 }
1930 } else {
1931 throw new Error('Target empty!');
1932 }
1933 return this;
1934 };
1935
1936 // 删除当前维护的结点
1937 var remove = function remove() {
1938 for (var i = 0; i < this.length; i++) {
1939 this[i].parentNode.removeChild(this[i]);
1940 }return this;
1941 };
1942
1943 // 筛选当前结点
1944 var filter = function filter(filterback) {
1945 var temp = [];
1946 for (var i = 0; i < this.length; i++) {
1947 if (filterback(i, image2D(this[i]))) temp.push(this[i]);
1948 }
1949 return image2D(temp);
1950 };
1951
1952 // 修改文本或获取结点文本
1953 var text = function text(content) {
1954 if (arguments.length > 0) {
1955 for (var i = 0; i < this.length; i++) {
1956 this[i].textContent = content;
1957 }return this;
1958 }
1959 if (this.length <= 0) throw new Error('Target empty!');
1960 return this[0].textContent;
1961 };
1962
1963 // 设置或获取结点中的xhtml字符串模板(相当于innerHTML)
1964 var html = function html(xhtmlString) {
1965 if (arguments.length > 0) {
1966 for (var i = 0; i < this.length; i++) {
1967
1968 // 如果是SVG标签
1969 if (/[a-z]/.test(this[i].tagName)) {
1970 setSVG(this[i], xhtmlString);
1971 }
1972
1973 // 否则是普通html标签
1974 else {
1975 this[i].innerHTML = xhtmlString;
1976 }
1977 }
1978 return this;
1979 }
1980 if (this.length <= 0) throw new Error('Target empty!');
1981 return this[0].innerHTML;
1982 };
1983
1984 // 获取元素大小
1985 var size = function size(type) {
1986 if (this.length <= 0) throw new Error('Target empty!');
1987
1988 var elemHeight = void 0,
1989 elemWidth = void 0,
1990 dom = this[0];
1991
1992 if (type == 'content') {
1993 //内容
1994 elemWidth = dom.clientWidth - (getStyle(dom, 'padding-left') + "").replace('px', '') - (getStyle(dom, 'padding-right') + "").replace('px', '');
1995 elemHeight = dom.clientHeight - (getStyle(dom, 'padding-top') + "").replace('px', '') - (getStyle(dom, 'padding-bottom') + "").replace('px', '');
1996 } else if (type == 'padding') {
1997 //内容+内边距
1998 elemWidth = dom.clientWidth;
1999 elemHeight = dom.clientHeight;
2000 } else if (type == 'border') {
2001 //内容+内边距+边框
2002 elemWidth = dom.offsetWidth;
2003 elemHeight = dom.offsetHeight;
2004 } else if (type == 'scroll') {
2005 //包含滚动的尺寸(不包括border)
2006 elemWidth = dom.scrollWidth;
2007 elemHeight = dom.scrollHeight;
2008 } else {
2009 elemWidth = dom.offsetWidth;
2010 elemHeight = dom.offsetHeight;
2011 }
2012 return {
2013 width: elemWidth,
2014 height: elemHeight
2015 };
2016 };
2017
2018 /**
2019 * 设置或获取样式
2020 * @arguments(key):获取指定样式
2021 * @arguments(key,value):设置指定样式
2022 * @arguments():获取全部样式
2023 * @arguments(json):设置大量样式
2024 */
2025 function style() {
2026
2027 // 获取样式
2028 if (arguments.length <= 1 && (arguments.length <= 0 || _typeof(arguments[0]) !== 'object')) {
2029 if (this.length <= 0) throw new Error('Target empty!');
2030
2031 // 为了获取非style定义的样式,需要使用特殊的方法获取
2032 return getStyle(this[0], arguments[0]);
2033 }
2034
2035 // 设置样式
2036 for (var i = 0; i < this.length; i++) {
2037 if (arguments.length === 1) {
2038 for (var key in arguments[0]) {
2039 this[i].style[key] = arguments[0][key];
2040 }
2041 } else this[i].style[arguments[0]] = arguments[1];
2042 }
2043
2044 return this;
2045 }
2046
2047 var setAttribute = function setAttribute(dom, attr, val) {
2048 if (/[a-z]/.test(dom.tagName) && XLINK_ATTRIBUTE.indexOf(attr) >= 0) {
2049 // 如果是xml元素
2050 // 针对xlink使用特殊方法赋值
2051 dom.setAttributeNS(NAMESPACE.xlink, 'xlink:' + attr, val);
2052 } else dom.setAttribute(attr, val);
2053 };
2054
2055 /**
2056 * 设置或获取属性
2057 * @arguments(attr):获取属性
2058 * @arguments(attr,value):设置指定属性值
2059 * @arguments(json):设置大量属性
2060 */
2061 function attribute() {
2062
2063 // 获取属性值
2064 if (arguments.length === 1 && _typeof(arguments[0]) !== 'object') {
2065 if (this.length <= 0) throw new Error('Target empty!');
2066 return this[0].getAttribute(arguments[0]);
2067 }
2068
2069 // 设置属性值
2070 else if (arguments.length > 0) {
2071 for (var i = 0; i < this.length; i++) {
2072 if (arguments.length === 1) {
2073 for (var key in arguments[0]) {
2074 setAttribute(this[i], key, arguments[0][key]);
2075 }
2076 } else setAttribute(this[i], arguments[0], arguments[1]);
2077 }
2078 }
2079
2080 return this;
2081 }
2082
2083 // 用于把数据绑定到一组结点或返回第一个结点数据
2084 // 可以传递函数对数据处理
2085 var datum = function datum(data, calcback) {
2086
2087 // 获取数据
2088 if (arguments.length <= 0) {
2089 if (this.length <= 0) throw new Error('Target empty!');
2090 return this[0].__data__;
2091 }
2092
2093 // 设置数据
2094 for (var i = 0; i < this.length; i++) {
2095 this[i].__data__ = typeof calcback === 'function' ? calcback(data, i) : data;
2096 }return this;
2097 };
2098
2099 // 用于把一组数据绑定到一组结点或返回一组结点数据
2100 // 可以传递函数对数据处理
2101 var data = function data(datas, calcback) {
2102
2103 // 获取数据
2104 if (arguments.length <= 0) {
2105 var _temp3 = [];
2106 for (var _i5 = 0; _i5 < this.length; _i5++) {
2107 _temp3[_i5] = this[_i5].__data__;
2108 }return _temp3;
2109 }
2110
2111 // 设置数据
2112 var temp = [],
2113 i = void 0;
2114 for (i = 0; i < this.length && i < datas.length; i++) {
2115 this[i].__data__ = typeof calcback === 'function' ? calcback(datas[i], i) : datas[i];
2116 temp.push(this[i]);
2117 }
2118 var newImage2D = image2D(temp);
2119
2120 // 记录需要去平衡的数据
2121 newImage2D.__enter__ = [];
2122 for (; i < datas.length; i++) {
2123 newImage2D.__enter__.push(typeof calcback === 'function' ? calcback(datas[i], i) : datas[i]);
2124 } // 记录需要去平衡的结点
2125 newImage2D.__exit__ = [];
2126 for (; i < this.length; i++) {
2127 newImage2D.__exit__.push(this[i]);
2128 }return newImage2D;
2129 };
2130
2131 // 把过滤出来多于结点的数据部分变成结点返回
2132 // 需要传递一个字符串来标明新创建元素是什么
2133 var enter = function enter(template, type) {
2134
2135 if (!this.__enter__ || this.__enter__.constructor !== Array) throw new Error('Not a data node object to be balanced!');
2136
2137 var temp = [];
2138 for (var i = 0; i < this.__enter__.length; i++) {
2139 temp[i] = toNode$1(template, type);
2140 temp[i].__data__ = this.__enter__[i];
2141 }
2142
2143 delete this.__enter__;
2144 return image2D(temp);
2145 };
2146
2147 // 把过滤出来多于数据的结点部分返回
2148 var exit = function exit() {
2149
2150 if (!this.__exit__ || this.__exit__.constructor !== Array) throw new Error('Not a data node object to be balanced!');
2151
2152 var exitImage2D = image2D(this.__exit__);
2153 delete this.__exit__;
2154 return exitImage2D;
2155 };
2156
2157 // 在维护的结点上轮询执行传入的方法
2158 // doback(data,index,image2D)
2159 var loop = function loop(doback) {
2160
2161 for (var i = 0; i < this.length; i++) {
2162 doback(this[i].__data__, i, image2D(this[i]));
2163 }return this;
2164 };
2165
2166 // r1和r2,内半径和外半径
2167 // beginA起点弧度,rotateA旋转弧度式
2168 function arc(beginA, rotateA, cx, cy, r1, r2, doback) {
2169
2170 // 有了前置的判断,这里可以省略了
2171 // if (rotateA > Math.PI * 2) rotateA = Math.PI * 2;
2172 // if (rotateA < -Math.PI * 2) rotateA = -Math.PI * 2;
2173
2174 // 保证逆时针也是可以的
2175 if (rotateA < 0) {
2176 beginA += rotateA;
2177 rotateA *= -1;
2178 }
2179
2180 var temp = [],
2181 p = void 0;
2182
2183 // 内部
2184 p = _rotate2(0, 0, beginA, r1, 0);
2185 temp[0] = p[0];
2186 temp[1] = p[1];
2187 p = _rotate2(0, 0, rotateA, p[0], p[1]);
2188 temp[2] = p[0];
2189 temp[3] = p[1];
2190
2191 // 外部
2192 p = _rotate2(0, 0, beginA, r2, 0);
2193 temp[4] = p[0];
2194 temp[5] = p[1];
2195 p = _rotate2(0, 0, rotateA, p[0], p[1]);
2196 temp[6] = p[0];
2197 temp[7] = p[1];
2198
2199 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);
2200 }
2201
2202 // 文字统一设置方法
2203 var initText = function initText(painter, config, x, y, deg) {
2204
2205 painter.beginPath();
2206 painter.translate(x, y);
2207 painter.rotate(deg);
2208 painter.font = config['font-size'] + "px " + config['font-family'];
2209 return painter;
2210 };
2211
2212 // 画弧统一设置方法
2213 var initArc = function initArc(painter, config, cx, cy, r1, r2, beginDeg, deg) {
2214
2215 if (r1 > r2) {
2216 var temp = r1;
2217 r1 = r2;
2218 r2 = temp;
2219 }
2220
2221 beginDeg = beginDeg % (Math.PI * 2);
2222
2223 // 当|deg|>=2π的时候都认为是一个圆环
2224 // 为什么不取2π比较,是怕部分浏览器浮点不精确,同时也是为了和svg保持一致
2225 if (deg >= Math.PI * 1.999999 || deg <= -Math.PI * 1.999999) {
2226 deg = Math.PI * 2;
2227 } else {
2228 deg = deg % (Math.PI * 2);
2229 }
2230
2231 arc(beginDeg, deg, cx, cy, r1, r2, function (beginA, endA, begInnerX, begInnerY, begOuterX, begOuterY, endInnerX, endInnerY, endOuterX, endOuterY, r) {
2232 if (r < 0) r = -r;
2233 painter.beginPath();
2234 painter.moveTo(begInnerX, begInnerY);
2235 painter.arc(
2236 // (圆心x,圆心y,半径,开始角度,结束角度,true逆时针/false顺时针)
2237 cx, cy, r1, beginA, endA, false);
2238
2239 // 结尾
2240 if (config["arc-end-cap"] == 'round') painter.arc((endInnerX + endOuterX) * 0.5, (endInnerY + endOuterY) * 0.5, r, endA - Math.PI, endA, true);else if (config["arc-end-cap"] == '-round') painter.arc((endInnerX + endOuterX) * 0.5, (endInnerY + endOuterY) * 0.5, r, endA - Math.PI, endA, false);else painter.lineTo(endOuterX, endOuterY);
2241
2242 painter.arc(cx, cy, r2, endA, beginA, true);
2243
2244 // 开头
2245 if (config["arc-start-cap"] == 'round') painter.arc((begInnerX + begOuterX) * 0.5, (begInnerY + begOuterY) * 0.5, r, beginA, beginA - Math.PI, true);else if (config["arc-start-cap"] == '-round') painter.arc((begInnerX + begOuterX) * 0.5, (begInnerY + begOuterY) * 0.5, r, beginA, beginA - Math.PI, false);else painter.lineTo(begInnerX, begInnerY);
2246 });
2247 if (config["arc-start-cap"] == 'butt') painter.closePath();
2248 return painter;
2249 };
2250
2251 // 画圆统一设置方法
2252 var initCircle = function initCircle(painter, cx, cy, r) {
2253 painter.beginPath();
2254 painter.moveTo(cx + r, cy);
2255 painter.arc(cx, cy, r, 0, Math.PI * 2);
2256 return painter;
2257 };
2258
2259 // 画矩形统一设置方法
2260 var initRect = function initRect(painter, x, y, width, height) {
2261 painter.beginPath();
2262 painter.rect(x, y, width, height);
2263 return painter;
2264 };
2265
2266 // 线性渐变
2267 var linearGradient = function linearGradient(painter, x0, y0, x1, y1) {
2268 var gradient = painter.createLinearGradient(x0, y0, x1, y1);
2269 var enhanceGradient = {
2270 "value": function value() {
2271 return gradient;
2272 },
2273 "addColorStop": function addColorStop(stop, color) {
2274 gradient.addColorStop(stop, color);
2275 return enhanceGradient;
2276 }
2277 };
2278 return enhanceGradient;
2279 };
2280
2281 // 环形渐变
2282 var radialGradient = function radialGradient(painter, cx, cy, r) {
2283 var gradient = painter.createRadialGradient(cx, cy, 0, cx, cy, r);
2284 var enhanceGradient = {
2285 "value": function value() {
2286 return gradient;
2287 },
2288 "addColorStop": function addColorStop(stop, color) {
2289 gradient.addColorStop(stop, color);
2290 return enhanceGradient;
2291 }
2292 };
2293 return enhanceGradient;
2294 };
2295
2296 // 加强版本的画笔
2297 function painter_canvas2D(canvas, noHiddenWarn) {
2298
2299 // 获取canvas2D画笔
2300 var painter = canvas.getContext("2d");
2301
2302 var isLayer = canvas.__image2D__layer__ == 'yes';
2303
2304 // 图层是内部的,明确获取方法
2305 // 对外的一律使用clientXXX,区分是否显示
2306 var width = isLayer ? canvas.getAttribute('width') : canvas.clientWidth,
2307 //内容+内边距
2308 height = isLayer ? canvas.getAttribute('height') : canvas.clientHeight;
2309
2310 if (width == 0 || height == 0) {
2311
2312 if (!noHiddenWarn) console.warn('Canvas is hidden or size is zero!');
2313
2314 if (canvas.__image2D__noLayer_getSize__ == 'yes') {
2315
2316 width = canvas.width / 2;
2317 height = canvas.height / 2;
2318 } else {
2319
2320 width = canvas.width;
2321 height = canvas.height;
2322
2323 // 标记已经特殊获取大小了
2324 canvas.__image2D__noLayer_getSize__ = 'yes';
2325 }
2326 }
2327
2328 // 设置显示大小
2329 canvas.style.width = width + "px";
2330 canvas.style.height = height + "px";
2331
2332 // 设置画布大小(画布大小设置为显示的两倍,使得显示的时候更加清晰)
2333 canvas.setAttribute('width', width * 2);
2334 canvas.setAttribute('height', height * 2);
2335
2336 // 通过缩放实现模糊问题
2337 painter.scale(2, 2);
2338
2339 // 默认配置canvas2D对象已经存在的属性
2340 painter.textBaseline = 'middle';
2341 painter.textAlign = 'left';
2342
2343 // 默认配置不应该有canvas2D对象已经存在的属性
2344 // 这里是为了简化或和svg统一接口而自定义的属性
2345 var config = {
2346 "font-size": "16", // 文字大小
2347 "font-family": "sans-serif", // 字体
2348 "arc-start-cap": "butt", // 弧开始闭合方式
2349 "arc-end-cap": "butt" // 弧结束闭合方式
2350 };
2351
2352 // 配置生效方法
2353 var useConfig = function useConfig(key, value) {
2354
2355 /**
2356 * -----------------------------
2357 * 特殊的设置开始
2358 * -----------------------------
2359 */
2360
2361 if (key == 'lineDash') {
2362 try {
2363 painter.setLineDash(value);
2364 } catch (e) {
2365 console.error(e);
2366 }
2367 }
2368
2369 /**
2370 * -----------------------------
2371 * 常规的配置开始
2372 * -----------------------------
2373 */
2374
2375 // 如果已经存在默认配置中,说明只需要缓存起来即可
2376 else if (config[key]) {
2377 config[key] = value;
2378 }
2379
2380 // 其它情况直接生效即可
2381 else {
2382 painter[key] = value;
2383 }
2384 };
2385
2386 // 画笔
2387 var enhancePainter = {
2388
2389 // 属性设置或获取
2390 "config": function config() {
2391 if (arguments.length === 1) {
2392 if (_typeof(arguments[0]) !== 'object') return painter[arguments[0]];
2393 for (var key in arguments[0]) {
2394 useConfig(key, arguments[0][key]);
2395 }
2396 } else if (arguments.length === 2) {
2397 useConfig(arguments[0], arguments[1]);
2398 }
2399 return enhancePainter;
2400 },
2401
2402 // 文字
2403 "fillText": function fillText(text, x, y, deg) {
2404 painter.save();
2405 initText(painter, config, x, y, deg || 0).fillText(text, 0, 0);
2406 painter.restore();
2407 return enhancePainter;
2408 },
2409 "strokeText": function strokeText(text, x, y, deg) {
2410 painter.save();
2411 initText(painter, config, x, y, deg || 0).strokeText(text, 0, 0);
2412 painter.restore();
2413 return enhancePainter;
2414 },
2415 "fullText": function fullText(text, x, y, deg) {
2416 painter.save();
2417 initText(painter, config, x, y, deg || 0);
2418 painter.fillText(text, 0, 0);
2419 painter.strokeText(text, 0, 0);
2420 painter.restore();
2421 return enhancePainter;
2422 },
2423
2424 // 路径
2425 "beginPath": function beginPath() {
2426 painter.beginPath();return enhancePainter;
2427 },
2428 "closePath": function closePath() {
2429 painter.closePath();return enhancePainter;
2430 },
2431 "moveTo": function moveTo(x, y) {
2432 painter.moveTo(x, y);return enhancePainter;
2433 },
2434 "lineTo": function lineTo(x, y) {
2435 painter.lineTo(x, y);return enhancePainter;
2436 },
2437 "arc": function arc(x, y, r, beginDeg, deg) {
2438 painter.arc(x, y, r, beginDeg, beginDeg + deg, deg < 0);
2439 return enhancePainter;
2440 },
2441 "fill": function fill() {
2442 painter.fill();return enhancePainter;
2443 },
2444 "stroke": function stroke() {
2445 painter.stroke();return enhancePainter;
2446 },
2447 "full": function full() {
2448 painter.fill();painter.stroke();return enhancePainter;
2449 },
2450
2451 "save": function save() {
2452 painter.save();return enhancePainter;
2453 },
2454 "restore": function restore() {
2455 painter.restore();return enhancePainter;
2456 },
2457
2458 // 路径 - 贝塞尔曲线
2459 "quadraticCurveTo": function quadraticCurveTo(cpx, cpy, x, y) {
2460 painter.quadraticCurveTo(cpx, cpy, x, y);return enhancePainter;
2461 },
2462 "bezierCurveTo": function bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
2463 painter.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);return enhancePainter;
2464 },
2465
2466 // 擦除画面
2467 "clearRect": function clearRect(x, y, width, height) {
2468 painter.clearRect(x || 0, y || 0, width || canvas.getAttribute('width') / 2, height || canvas.getAttribute('height') / 2);return enhancePainter;
2469 },
2470
2471 // 地址图片
2472 "toDataURL": function toDataURL() {
2473 return canvas.toDataURL();
2474 },
2475
2476 // image
2477 // v1.5.0开始,做了参数调整(非向下兼容)
2478 "drawImage": function drawImage(img, sx, sy, sw, sh, x, y, w, h) {
2479 sx = sx || 0;
2480 sy = sy || 0;
2481 x = x || 0;
2482 y = y || 0;
2483 w = w ? w * 2 : canvas.getAttribute('width');
2484 h = h ? h * 2 : canvas.getAttribute('height');
2485
2486 if (img.nodeName == 'CANVAS') {
2487 // 我们不考虑别的canvas,我们认为我们面对的canvas都是自己控制的
2488 // 如果有必要,未来可以对任意canvas进行向下兼容
2489 w = w / 2;
2490 h = h / 2;
2491 sw = sw ? sw * 2 : canvas.getAttribute('width');
2492 sh = sh ? sh * 2 : canvas.getAttribute('height');
2493 } else {
2494 // 默认类型是图片
2495 sw = (sw || img.width) * 2;
2496 sh = (sh || img.height) * 2;
2497 }
2498
2499 painter.drawImage(img, sx, sy, sw, sh, x, y, w, h);
2500 return enhancePainter;
2501 },
2502
2503 // 弧
2504 "fillArc": function fillArc(cx, cy, r1, r2, beginDeg, deg) {
2505 initArc(painter, config, cx, cy, r1, r2, beginDeg, deg).fill();return enhancePainter;
2506 },
2507 "strokeArc": function strokeArc(cx, cy, r1, r2, beginDeg, deg) {
2508 initArc(painter, config, cx, cy, r1, r2, beginDeg, deg).stroke();return enhancePainter;
2509 },
2510 "fullArc": function fullArc(cx, cy, r1, r2, beginDeg, deg) {
2511 initArc(painter, config, cx, cy, r1, r2, beginDeg, deg);
2512 painter.fill();
2513 painter.stroke();
2514 return enhancePainter;
2515 },
2516
2517 // 圆形
2518 "fillCircle": function fillCircle(cx, cy, r) {
2519 initCircle(painter, cx, cy, r).fill();return enhancePainter;
2520 },
2521 "strokeCircle": function strokeCircle(cx, cy, r) {
2522 initCircle(painter, cx, cy, r).stroke();return enhancePainter;
2523 },
2524 "fullCircle": function fullCircle(cx, cy, r) {
2525 initCircle(painter, cx, cy, r);
2526 painter.fill();
2527 painter.stroke();
2528 return enhancePainter;
2529 },
2530
2531 // 矩形
2532 "fillRect": function fillRect(x, y, width, height) {
2533 initRect(painter, x, y, width, height).fill();return enhancePainter;
2534 },
2535 "strokeRect": function strokeRect(x, y, width, height) {
2536 initRect(painter, x, y, width, height).stroke();return enhancePainter;
2537 },
2538 "fullRect": function fullRect(x, y, width, height) {
2539 initRect(painter, x, y, width, height);
2540 painter.fill();
2541 painter.stroke();
2542 return enhancePainter;
2543 },
2544
2545 /**
2546 * 渐变
2547 * -------------
2548 */
2549
2550 // 线性渐变
2551 "createLinearGradient": function createLinearGradient(x0, y0, x1, y1) {
2552 return linearGradient(painter, x0, y0, x1, y1);
2553 },
2554
2555 // 环形渐变
2556 "createRadialGradient": function createRadialGradient(cx, cy, r) {
2557 return radialGradient(painter, cx, cy, r);
2558 },
2559
2560 /**
2561 * 变换
2562 * --------------
2563 */
2564
2565 // 移动
2566 // 用来移动 canvas 的原点到指定的位置
2567 "translate": function translate(x, y) {
2568 painter.translate(x, y);return enhancePainter;
2569 },
2570
2571 // 旋转
2572 "rotate": function rotate(deg) {
2573 painter.rotate(deg);return enhancePainter;
2574 },
2575
2576 // 缩放
2577 "scale": function scale(x, y) {
2578 y = y || x;painter.scale(x, y);return enhancePainter;
2579 }
2580 };
2581
2582 return enhancePainter;
2583 }
2584
2585 function normalConfig(key, value) {
2586
2587 // 文字水平对齐方式
2588 if (key === 'textAlign') {
2589 return {
2590 "left": "start",
2591 "right": "end",
2592 "center": "middle"
2593 }[value] || value;
2594 }
2595
2596 return value;
2597 }
2598 // 文字统一设置方法
2599 var initText$1 = function initText$1(painter, config, x, y, deg) {
2600 if (!painter || painter.length <= 0 || painter[0].nodeName.toLowerCase() !== 'text') throw new Error('Need a <text> !');
2601
2602 deg = deg % (Math.PI * 2);
2603
2604 // 垂直对齐采用dy实现
2605 painter.attr('dy', {
2606 "top": config['font-size'] * 0.5,
2607 "middle": 0,
2608 "bottom": -config['font-size'] * 0.5
2609 }[config.textBaseline]).css({
2610
2611 // 文字对齐方式
2612 "text-anchor": config.textAlign,
2613 "dominant-baseline": "central",
2614
2615 // 文字大小和字体设置
2616 "font-size": config['font-size'] + "px",
2617 "font-family": config['font-family']
2618 }).attr({ "x": x, "y": y });
2619
2620 return {
2621 "transform": "rotate(" + deg * 180 / Math.PI + "," + x + "," + y + ")"
2622 };
2623 };
2624
2625 // 画弧统一设置方法
2626 var initArc$1 = function initArc$1(painter, config, cx, cy, r1, r2, beginDeg, deg) {
2627
2628 if (!painter || painter.length <= 0 || painter[0].nodeName.toLowerCase() !== 'path') throw new Error('Need a <path> !');
2629
2630 beginDeg = beginDeg % (Math.PI * 2);
2631
2632 if (r1 > r2) {
2633 var temp = r1;
2634 r1 = r2;
2635 r2 = temp;
2636 }
2637
2638 // 当|deg|>=2π的时候都认为是一个圆环
2639 if (deg >= Math.PI * 1.999999 || deg <= -Math.PI * 1.999999) {
2640 deg = Math.PI * 1.999999;
2641 } else {
2642 deg = deg % (Math.PI * 2);
2643 }
2644
2645 arc(beginDeg, deg, cx, cy, r1, r2, function (beginA, endA, begInnerX, begInnerY, begOuterX, begOuterY, endInnerX, endInnerY, endOuterX, endOuterY, r) {
2646 var f = endA - beginA > Math.PI ? 1 : 0,
2647 d = "M" + begInnerX + " " + begInnerY;
2648 if (r < 0) r = -r;
2649 d +=
2650 // 横半径 竖半径 x轴偏移角度 0小弧/1大弧 0逆时针/1顺时针 终点x 终点y
2651 "A" + r1 + " " + r1 + " 0 " + f + " 1 " + endInnerX + " " + endInnerY;
2652
2653 // 结尾
2654 if (config["arc-end-cap"] == 'round') d += "A" + r + " " + r + " " + " 0 1 0 " + endOuterX + " " + endOuterY;else if (config["arc-end-cap"] == '-round') d += "A" + r + " " + r + " " + " 0 1 1 " + endOuterX + " " + endOuterY;else d += "L" + endOuterX + " " + endOuterY;
2655 d += "A" + r2 + " " + r2 + " 0 " + f + " 0 " + begOuterX + " " + begOuterY;
2656
2657 // 开头
2658 if (config["arc-start-cap"] == 'round') d += "A" + r + " " + r + " " + " 0 1 0 " + begInnerX + " " + begInnerY;else if (config["arc-start-cap"] == '-round') d += "A" + r + " " + r + " " + " 0 1 1 " + begInnerX + " " + begInnerY;else d += "L" + begInnerX + " " + begInnerY;
2659
2660 if (config["arc-start-cap"] == 'butt') d += "Z";
2661
2662 painter.attr('d', d);
2663 });
2664 return painter;
2665 };
2666
2667 // 画圆统一设置方法
2668 var initCircle$1 = function initCircle$1(painter, cx, cy, r) {
2669 if (!painter || painter.length <= 0 || painter[0].nodeName.toLowerCase() !== 'circle') throw new Error('Need a <circle> !');
2670 painter.attr({
2671 "cx": cx,
2672 "cy": cy,
2673 "r": r
2674 });
2675 return painter;
2676 };
2677
2678 // 路径统一设置方法
2679 var initPath = function initPath(painter, path) {
2680 if (!painter || painter.length <= 0 || painter[0].nodeName.toLowerCase() !== 'path') throw new Error('Need a <path> !');
2681 painter.attr('d', path);
2682 return painter;
2683 };
2684
2685 // 画矩形统一设置方法
2686 var initRect$1 = function initRect$1(painter, x, y, width, height) {
2687 if (!painter || painter.length <= 0 || painter[0].nodeName.toLowerCase() !== 'rect') throw new Error('Need a <rect> !');
2688
2689 // 由于height和宽不可以是负数,校对一下
2690
2691 if (height < 0) {
2692 height *= -1;y -= height;
2693 }
2694
2695 if (width < 0) {
2696 width *= -1;x -= width;
2697 }
2698
2699 painter.attr({
2700 "x": x,
2701 "y": y,
2702 "width": width,
2703 "height": height
2704 });
2705 return painter;
2706 };
2707
2708 var initDefs = function initDefs(target) {
2709 var defs = target.getElementsByTagName('defs');
2710 if (defs.length <= 0) {
2711 defs = [toNode$1("<defs>", "SVG")];
2712 target.appendChild(defs[0]);
2713 }
2714 return defs[0];
2715 };
2716
2717 // 线性渐变
2718 var linearGradient$1 = function linearGradient$1(painter, target, x0, y0, x1, y1) {
2719 var defs = initDefs(target);
2720 var gradientId = "image2D-lg-" + new Date().valueOf() + "-" + Math.random();
2721 var gradientDom = toNode$1('<linearGradient id="' + gradientId + '" x1="' + x0 + '%" y1="' + y0 + '%" x2="' + x1 + '%" y2="' + y1 + '%"></linearGradient>');
2722 defs.appendChild(gradientDom);
2723 var enhanceGradient = {
2724 "value": function value() {
2725 return "url(#" + gradientId + ")";
2726 },
2727 "addColorStop": function addColorStop(stop, color) {
2728 gradientDom.appendChild(toNode$1('<stop offset="' + stop * 100 + '%" style="stop-color:' + color + ';" />'));
2729 return enhanceGradient;
2730 }
2731 };
2732 return enhanceGradient;
2733 };
2734
2735 // 环形渐变
2736 var radialGradient$1 = function radialGradient$1(painter, target, cx, cy, r) {
2737 var defs = initDefs(target);
2738 var gradientId = "image2D-rg-" + new Date().valueOf() + "-" + Math.random();
2739 var gradientDom = toNode$1('<radialGradient id="' + gradientId + '" cx="' + cx + '%" cy="' + cy + '%" r="' + r + '%"></radialGradient>');
2740 defs.appendChild(gradientDom);
2741 var enhanceGradient = {
2742 "value": function value() {
2743 return "url(#" + gradientId + ")";
2744 },
2745 "addColorStop": function addColorStop(stop, color) {
2746 gradientDom.appendChild(toNode$1('<stop offset="' + stop * 100 + '%" style="stop-color:' + color + ';" />'));
2747 return enhanceGradient;
2748 }
2749 };
2750 return enhanceGradient;
2751 };
2752
2753 function painter_svg(target, selector) {
2754
2755 var painter = void 0;
2756 if (selector) painter = image2D(selector, target);
2757
2758 // 类似canvas画笔的属性
2759 var _config2 = {
2760
2761 // 基本设置
2762 "fillStyle": "#000",
2763 "strokeStyle": "#000",
2764 "lineWidth": 1,
2765
2766 // 文字对齐方式
2767 "textAlign": "start",
2768 "textBaseline": "middle",
2769
2770 // 文字设置
2771 "font-size": "16",
2772 "font-family": "sans-serif",
2773
2774 // arc二端闭合方式['butt':直线闭合,'round':圆帽闭合]
2775 "arc-start-cap": "butt",
2776 "arc-end-cap": "butt",
2777
2778 // 虚线设置
2779 "lineDash": []
2780
2781 };
2782
2783 // 路径(和canvas2D的类似)
2784 var path = "",
2785 currentPosition = [];
2786
2787 // 变换(和canvas2D的类似,内部维护了用于记录)
2788 var transform_history = [],
2789 transform_current = "";
2790
2791 // 画笔
2792 var enhancePainter = {
2793
2794 // 属性设置或获取
2795 "config": function config() {
2796 if (arguments.length === 1) {
2797 if (_typeof(arguments[0]) !== 'object') return _config2[arguments[0]];
2798 for (var key in arguments[0]) {
2799 _config2[key] = normalConfig(key, arguments[0][key]);
2800 }
2801 } else if (arguments.length === 2) _config2[arguments[0]] = normalConfig(arguments[0], arguments[1]);
2802 return enhancePainter;
2803 },
2804
2805 // 基础方法
2806 "bind": function bind(selector) {
2807 painter = image2D(selector, target);return enhancePainter;
2808 },
2809 "appendTo": function appendTo(selector) {
2810 painter.appendTo(selector || target, target);return enhancePainter;
2811 },
2812 "prependTo": function prependTo(selector) {
2813 painter.prependTo(selector || target, target);return enhancePainter;
2814 },
2815 "afterTo": function afterTo(selector) {
2816 painter.afterTo(selector || target, target);return enhancePainter;
2817 },
2818 "beforeTo": function beforeTo(selector) {
2819 painter.beforeTo(selector || target, target);return enhancePainter;
2820 },
2821
2822 // 路径
2823 "beginPath": function beginPath() {
2824 path = "";currentPosition = [];return enhancePainter;
2825 },
2826 "closePath": function closePath() {
2827 path += "Z";return enhancePainter;
2828 },
2829 "moveTo": function moveTo(x, y) {
2830 path += "M" + x + " " + y;currentPosition = [x, y];return enhancePainter;
2831 },
2832 "lineTo": function lineTo(x, y) {
2833 path += (path == "" ? "M" : "L") + x + " " + y;currentPosition = [x, y];return enhancePainter;
2834 },
2835 "arc": function arc(x, y, r, beginDeg, deg) {
2836 var begPosition = _rotate2(x, y, beginDeg, x + r, y);
2837 var endPosition = _rotate2(x, y, beginDeg + deg, x + r, y);
2838 beginDeg = beginDeg / Math.PI * 180;
2839 deg = deg / Math.PI * 180;
2840 // 如果当前没有路径,说明是开始的,就移动到正确位置
2841 if (path == '') {
2842 path += "M" + begPosition[0] + "," + begPosition[1];
2843 }
2844 // 如果当前有路径,位置不正确,应该画到正确位置(和canvas保持一致)
2845 else if (begPosition[0] != currentPosition[0] || begPosition[1] != currentPosition[1]) {
2846 path += "L" + begPosition[0] + "," + begPosition[1];
2847 }
2848 path += "A" + r + "," + r + " 0 " + (deg > 180 || deg < -180 ? 1 : 0) + "," + (deg > 0 ? 1 : 0) + " " + endPosition[0] + "," + endPosition[1];
2849 return enhancePainter;
2850 },
2851 "fill": function fill() {
2852 initPath(painter, path).attr('transform', transform_current).attr("fill", _config2.fillStyle);
2853 return enhancePainter;
2854 },
2855 "stroke": function stroke() {
2856 initPath(painter, path).attr('transform', transform_current).attr({
2857 "stroke-width": _config2.lineWidth,
2858 "stroke": _config2.strokeStyle,
2859 "fill": "none",
2860 "stroke-dasharray": _config2.lineDash.join(',')
2861 });
2862 return enhancePainter;
2863 },
2864 "full": function full() {
2865 initPath(painter, path).attr('transform', transform_current).attr({
2866 "stroke-width": _config2.lineWidth,
2867 "stroke": _config2.strokeStyle,
2868 "fill": _config2.fillStyle,
2869 "stroke-dasharray": _config2.lineDash.join(',')
2870 });
2871 return enhancePainter;
2872 },
2873
2874 "save": function save() {
2875 transform_history.push(transform_current);
2876 return enhancePainter;
2877 },
2878 "restore": function restore() {
2879 if (transform_history.length > 0) transform_current = transform_history.pop();
2880 return enhancePainter;
2881 },
2882
2883 // 路径 - 贝塞尔曲线
2884 "quadraticCurveTo": function quadraticCurveTo(cpx, cpy, x, y) {
2885 path += "Q" + cpx + " " + cpy + "," + x + " " + y;return enhancePainter;
2886 },
2887 "bezierCurveTo": function bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
2888 path += "C" + cp1x + " " + cp1y + "," + cp2x + " " + cp2y + "," + x + " " + y;return enhancePainter;
2889 },
2890
2891 // 文字
2892 "fillText": function fillText(text, x, y, deg) {
2893 var returnJSon = initText$1(painter, _config2, x, y, deg || 0);
2894 painter.attr('transform', transform_current + returnJSon.transform).attr("fill", _config2.fillStyle)[0].textContent = text;
2895 return enhancePainter;
2896 },
2897 "strokeText": function strokeText(text, x, y, deg) {
2898 var returnJSon = initText$1(painter, _config2, x, y, deg || 0);
2899 painter.attr('transform', transform_current + returnJSon.transform).attr({
2900 "stroke": _config2.strokeStyle,
2901 "fill": "none",
2902 "stroke-dasharray": _config2.lineDash.join(',')
2903 })[0].textContent = text;
2904 return enhancePainter;
2905 },
2906 "fullText": function fullText(text, x, y, deg) {
2907 var returnJSon = initText$1(painter, _config2, x, y, deg || 0);
2908 painter.attr('transform', transform_current + returnJSon.transform).attr({
2909 "stroke": _config2.strokeStyle,
2910 "fill": _config2.fillStyle,
2911 "stroke-dasharray": _config2.lineDash.join(',')
2912 })[0].textContent = text;
2913 return enhancePainter;
2914 },
2915
2916 // 弧
2917 "fillArc": function fillArc(cx, cy, r1, r2, beginDeg, deg) {
2918 initArc$1(painter, _config2, cx, cy, r1, r2, beginDeg, deg).attr('transform', transform_current).attr("fill", _config2.fillStyle);
2919 return enhancePainter;
2920 },
2921 "strokeArc": function strokeArc(cx, cy, r1, r2, beginDeg, deg) {
2922 initArc$1(painter, _config2, cx, cy, r1, r2, beginDeg, deg).attr('transform', transform_current).attr({
2923 "stroke-width": _config2.lineWidth,
2924 "stroke": _config2.strokeStyle,
2925 "fill": "none",
2926 "stroke-dasharray": _config2.lineDash.join(',')
2927 });
2928 return enhancePainter;
2929 },
2930 "fullArc": function fullArc(cx, cy, r1, r2, beginDeg, deg) {
2931 initArc$1(painter, _config2, cx, cy, r1, r2, beginDeg, deg).attr('transform', transform_current).attr({
2932 "stroke-width": _config2.lineWidth,
2933 "stroke": _config2.strokeStyle,
2934 "fill": _config2.fillStyle,
2935 "stroke-dasharray": _config2.lineDash.join(',')
2936 });
2937 return enhancePainter;
2938 },
2939
2940 // 圆形
2941 "fillCircle": function fillCircle(cx, cy, r) {
2942 initCircle$1(painter, cx, cy, r).attr('transform', transform_current).attr("fill", _config2.fillStyle);return enhancePainter;
2943 },
2944 "strokeCircle": function strokeCircle(cx, cy, r) {
2945 initCircle$1(painter, cx, cy, r).attr('transform', transform_current).attr({
2946 "stroke-width": _config2.lineWidth,
2947 "stroke": _config2.strokeStyle,
2948 "fill": "none",
2949 "stroke-dasharray": _config2.lineDash.join(',')
2950 });return enhancePainter;
2951 },
2952 "fullCircle": function fullCircle(cx, cy, r) {
2953 initCircle$1(painter, cx, cy, r).attr('transform', transform_current).attr({
2954 "stroke-width": _config2.lineWidth,
2955 "stroke": _config2.strokeStyle,
2956 "fill": _config2.fillStyle,
2957 "stroke-dasharray": _config2.lineDash.join(',')
2958 });return enhancePainter;
2959 },
2960
2961 // 矩形
2962 "fillRect": function fillRect(x, y, width, height) {
2963 initRect$1(painter, x, y, width, height).attr('transform', transform_current).attr("fill", _config2.fillStyle);return enhancePainter;
2964 },
2965 "strokeRect": function strokeRect(x, y, width, height) {
2966 initRect$1(painter, x, y, width, height).attr('transform', transform_current).attr({
2967 "stroke-width": _config2.lineWidth,
2968 "stroke": _config2.strokeStyle,
2969 "fill": "none",
2970 "stroke-dasharray": _config2.lineDash.join(',')
2971 });return enhancePainter;
2972 },
2973 "fullRect": function fullRect(x, y, width, height) {
2974 initRect$1(painter, x, y, width, height).attr('transform', transform_current).attr({
2975 "stroke-width": _config2.lineWidth,
2976 "stroke": _config2.strokeStyle,
2977 "fill": _config2.fillStyle,
2978 "stroke-dasharray": _config2.lineDash.join(',')
2979 });return enhancePainter;
2980 },
2981
2982 /**
2983 * 渐变
2984 * -------------
2985 */
2986
2987 // 线性渐变
2988 "createLinearGradient": function createLinearGradient(x0, y0, x1, y1) {
2989 return linearGradient$1(painter, target, x0, y0, x1, y1);
2990 },
2991
2992 // 环形渐变
2993 "createRadialGradient": function createRadialGradient(cx, cy, r) {
2994 return radialGradient$1(painter, target, cx, cy, r);
2995 },
2996
2997 /**
2998 * 变换
2999 * --------------
3000 */
3001
3002 // 移动
3003 "translate": function translate(x, y) {
3004 transform_current += ' translate(' + x + ',' + y + ')';
3005 return enhancePainter;
3006 },
3007
3008 // 旋转
3009 "rotate": function rotate(deg) {
3010 deg = deg % (Math.PI * 2);
3011 transform_current += ' rotate(' + deg / Math.PI * 180 + ')';
3012 return enhancePainter;
3013 },
3014
3015 // 缩放
3016 "scale": function scale(x, y) {
3017 y = y || x;
3018 transform_current += ' scale(' + x + ',' + y + ')';
3019 return enhancePainter;
3020 }
3021
3022 };
3023
3024 return enhancePainter;
3025 }
3026
3027 // 统一画笔
3028 // 负责启动具体的绘图对象
3029 function painter() {
3030
3031 // 因为绘图画布是必须的,因此在判断画布类型前,如果压根没有结点,肯定是非法的
3032 if (!isElement(this[0])) throw new Error('Target empty!');
3033
3034 var target = this[0],
3035 nodeName = target.nodeName.toLowerCase();
3036
3037 // canvas2D
3038 if (nodeName === 'canvas') return painter_canvas2D(target, arguments[0]);
3039
3040 // svg
3041 if (nodeName === 'svg') return painter_svg(target, arguments[0]);
3042
3043 throw new Error('Painter is not a function!');
3044 }
3045
3046 function layer() {
3047
3048 if (!isElement(this[0])) throw new Error('Target empty!');
3049
3050 if (this[0].nodeName.toLowerCase() !== 'canvas') throw new Error('Layer is not a function!');
3051
3052 // 画笔
3053 var painter = this.painter(),
3054
3055 // 图层集合
3056 layer = {},
3057 layer_index = [];
3058 var width = this[0].clientWidth,
3059 //内容+内边距
3060 height = this[0].clientHeight;
3061
3062 var layerManager = {
3063
3064 // 获取指定图层画笔
3065 "painter": function painter(id) {
3066 if (!layer[id]) {
3067 // 初始化的图层都可见
3068 layer[id] = { "visible": true };
3069
3070 // 后期可以考虑使用离线画布offScreenCanvas提高效率
3071 layer[id].canvas = document.createElement('canvas');
3072 // 设置大小才会避免莫名其妙的错误
3073 layer[id].canvas.setAttribute('width', width);
3074 layer[id].canvas.setAttribute('height', height);
3075
3076 // 标记是图层
3077 layer[id].canvas.__image2D__layer__ = 'yes';
3078
3079 layer[id].painter = image2D(layer[id].canvas).painter();
3080
3081 layer_index.push(id);
3082 }
3083 return layer[id].painter;
3084 },
3085
3086 // 删除图层
3087 "delete": function _delete(id) {
3088 // 删除索引
3089 for (var i = 0; i < layer_index.length; i++) {
3090 if (layer_index[i] === id) {
3091 layer_index.splice(i, 1);
3092 break;
3093 }
3094 } // 删除图层
3095 delete layer[id];
3096 return layerManager;
3097 },
3098
3099 // 更新内容到画布
3100 "update": function update() {
3101 painter.clearRect(0, 0, width, height);
3102 painter.save();
3103
3104 for (var i = 0; i < layer_index.length; i++) {
3105 if (layer[layer_index[i]].visible) painter.drawImage(layer[layer_index[i]].canvas, 0, 0, width, height, 0, 0, width, height);
3106 }
3107 painter.restore();
3108 return layerManager;
3109 },
3110
3111 // 隐藏图层
3112 "hidden": function hidden(id) {
3113 layer[id].visible = false;
3114 return layerManager;
3115 },
3116
3117 // 显示图层
3118 "show": function show(id) {
3119 layer[id].visible = true;
3120 return layerManager;
3121 }
3122 };
3123
3124 return layerManager;
3125 }
3126
3127 image2D.extend({
3128
3129 // 布局
3130 treeLayout: treeLayout$1, pieLayout: pieLayout,
3131
3132 // 矩阵变换
3133 Matrix4: Matrix4,
3134
3135 // 二维简单变换
3136 rotate: _rotate2, move: _move2, scale: _scale2, dot: dot,
3137
3138 // 动画类
3139 animation: animation$1,
3140
3141 // 插值类计算
3142 cardinal: cardinal,
3143
3144 // 色彩类
3145 formatColor: formatColor, getRandomColors: getRandomColors, getLoopColors: getLoopColors,
3146
3147 // 事件相关
3148 stopPropagation: stopPropagation, preventDefault: preventDefault,
3149
3150 // 地图映射
3151 map: map,
3152
3153 // 刻度尺辅助计算
3154 ruler: ruler$1
3155
3156 });
3157 image2D.prototype.extend({
3158
3159 // 结点操作
3160 appendTo: appendTo, prependTo: prependTo, afterTo: afterTo, beforeTo: beforeTo, remove: remove, filter: filter, text: text, html: html, size: size,
3161
3162 // 结点属性或样式操作
3163 css: style, attr: attribute,
3164
3165 // 结点和数据绑定
3166 datum: datum, data: data, enter: enter, exit: exit, loop: loop,
3167
3168 // 结点事件
3169 bind: bind, unbind: unbind, position: position,
3170
3171 // 自定义画笔
3172 painter: painter,
3173
3174 // 图层
3175 layer: layer
3176
3177 });
3178
3179 image2D.fn = image2D.prototype;
3180
3181 // 判断当前环境,如果不是浏览器环境
3182 if ((typeof module === "undefined" ? "undefined" : _typeof(module)) === "object" && _typeof(module.exports) === "object") {
3183 module.exports = image2D;
3184 }
3185 // 浏览器环境下
3186 // 因为浏览器下挂载到window对象上
3187 // 为了防止覆盖,额外提供一个noConflict方法,用以在覆盖的时候恢复
3188 else {
3189 var
3190 // 保存之前的image2D,防止直接覆盖
3191 _image2D = window.image2D,
3192
3193
3194 // 保存之前的$$,防止直接覆盖
3195 _$$ = window.$$;
3196
3197 image2D.noConflict = function (deep) {
3198
3199 // 如果当前的$$是被最新的image2D覆盖的
3200 // 恢复之前的
3201 if (window.$$ === image2D) {
3202 window.$$ = _$$;
3203 }
3204
3205 // 如果当前的image2D是被最新的image2D覆盖的
3206 // 且标记需要恢复
3207 // 恢复之前的
3208 if (deep && window.image2D === image2D) {
3209 window.image2D = _image2D;
3210 }
3211
3212 // 返回当前image2D
3213 // 因为调用这个方法以后
3214 // 全局window下的image2D和$$是什么
3215 // 已经不一定了
3216 return image2D;
3217 };
3218 // 挂载库对象到根
3219 window.image2D = window.$$ = image2D;
3220 }
3221})();
3222
3223/*!
3224
3225 我还惊讶地意识到, 在我生命中有很多时刻, 每当我遇到一个遥不可及、令人害怕的情境,
3226 并感到惊慌失措时, 我都能够应付——因为我回想起了很久以前自己上过的那一课。
3227 我提醒自己不要看下面遥远的岩石, 而是注意相对轻松、容易的第一小步, 迈出一小步、再一小步,
3228 就这样体会每一步带来的成就感, 直到完成了自己想要完成的, 达到了自己的目标,
3229 然后再回头看时, 不禁对自己走过的这段漫漫长路感到惊讶和自豪。
3230
3231 ———— 摘自 莫顿·亨特《走一步,再走一步》
3232
3233*/
\No newline at end of file