UNPKG

121 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.13.2
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:Mon Jun 14 2021 13:16:20 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 var Math_trunc = function Math_trunc(value) {
1769 return value < 0 ? Math.ceil(value) : Math.floor(value);
1770 };
1771
1772 // 刻度计算
1773 function ruler(cormax, cormin) {
1774 var cornumber = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 5;
1775
1776
1777 var tmpstep = void 0,
1778 corstep = void 0,
1779 temp = void 0;
1780
1781 //先判断所有数据都相等的情况
1782 if (cormax == cormin) {
1783 //在数据相等的情况下先计算所有数为正数
1784 if (cormin > 0) {
1785 //直接求出初始间隔
1786 corstep = cormax / cornumber;
1787 } else if (cormin < 0) {
1788 //当所有数为负数且相等时
1789 corstep = cormax / cornumber;
1790 //因为间隔为负影响下面的计算,所以直接取反
1791 corstep = -corstep;
1792 }
1793 //求间隔corstep的数量级temp (10,100,1000)
1794 if (Math.pow(10, Math_trunc(Math.log(corstep) / Math.log(10))) == corstep) {
1795 temp = Math.pow(10, Math_trunc(Math.log(corstep) / Math.log(10)));
1796 } else {
1797 temp = Math.pow(10, Math_trunc(Math.log(corstep) / Math.log(10)) + 1);
1798 }
1799 //将间隔corstep进行归一化,求出tmpstep(tpmstep在0.1 0.2 0.25 0.5 1之间取值)
1800 tmpstep = corstep / temp;
1801 if (tmpstep >= 0 && tmpstep <= 0.1) {
1802 tmpstep = 0.1;
1803 } else if (tmpstep >= 0.100001 && tmpstep <= 0.2) {
1804 tmpstep = 0.2;
1805 } else if (tmpstep >= 0.200001 && tmpstep <= 0.25) {
1806 tmpstep = 0.25;
1807 } else if (tmpstep >= 0.250001 && tmpstep <= 0.5) {
1808 tmpstep = 0.5;
1809 } else {
1810 tmpstep = 1;
1811 }
1812 //将间隔恢复,求出实际间隔距离
1813 tmpstep = tmpstep * temp;
1814 //刻度尺最小必须从0开始
1815 cormin = 0;
1816 //调整刻度尺的最大刻度
1817 cormax = Math_trunc(cormax / tmpstep + 1) * tmpstep;
1818 //求出刻度尺的间隔
1819 cornumber = (cormax - cormin) / tmpstep;
1820 } else if (cormax != cormin) {
1821 //根据传入的数据初步求出刻度数之间的间隔corstep
1822 corstep = (cormax - cormin) / cornumber;
1823 //求间隔corstep的数量级temp (10,100,1000)
1824 if (Math.pow(10, Math_trunc(Math.log(corstep) / Math.log(10))) == corstep) {
1825 temp = Math.pow(10, Math_trunc(Math.log(corstep) / Math.log(10)));
1826 } else {
1827 temp = Math.pow(10, Math_trunc(Math.log(corstep) / Math.log(10)) + 1);
1828 }
1829
1830 //将间隔corstep进行归一化,求出tmpstep(tpmstep在0.1 0.2 0.25 0.5 1之间取值)
1831 tmpstep = corstep / temp;
1832 if (tmpstep >= 0 && tmpstep <= 0.1) {
1833 tmpstep = 0.1;
1834 } else if (tmpstep >= 0.100001 && tmpstep <= 0.2) {
1835 tmpstep = 0.2;
1836 } else if (tmpstep >= 0.200001 && tmpstep <= 0.25) {
1837 tmpstep = 0.25;
1838 } else if (tmpstep >= 0.250001 && tmpstep <= 0.5) {
1839 tmpstep = 0.5;
1840 } else {
1841 tmpstep = 1;
1842 }
1843
1844 //将间隔恢复,求出实际间隔距离
1845 tmpstep = tmpstep * temp;
1846
1847 //调整刻度尺的最小刻度
1848 if (Math_trunc(cormin / tmpstep) != cormin / tmpstep) {
1849 if (cormin < 0) {
1850 cormin = -1 * Math.ceil(Math.abs(cormin / tmpstep)) * tmpstep;
1851 } else {
1852 cormin = Math_trunc(Math.abs(cormin / tmpstep)) * tmpstep;
1853 }
1854 }
1855 //调整刻度尺的最大刻度
1856 cormax = Math_trunc(cormax / tmpstep + 1) * tmpstep;
1857
1858 //求新的cornumber、cormax、cormin
1859 var tmpnumber = (cormax - cormin) / tmpstep;
1860 if (tmpnumber < cornumber) {
1861 var extranumber = cornumber - tmpnumber;
1862 tmpnumber = cornumber;
1863 if (extranumber % 2 == 0) {
1864 cormax = cormax + tmpstep * Math_trunc(extranumber / 2);
1865 } else {
1866 cormax = cormax + tmpstep * Math_trunc(extranumber / 2 + 1);
1867 }
1868 cormin = cormin - tmpstep * Math_trunc(extranumber / 2);
1869 }
1870 cornumber = tmpnumber;
1871 }
1872
1873 var resultData = {
1874 min: cormin,
1875 max: cormax,
1876 distance: tmpstep,
1877 num: cornumber,
1878 ruler: []
1879 };
1880
1881 // 得出最终的刻度数组
1882 for (var i = 0; i <= cornumber; i++) {
1883 resultData.ruler.push(cormin + tmpstep * i);
1884 }
1885
1886 return resultData;
1887 }
1888
1889 /**
1890 * 把当前维护的结点加到目标结点内部的结尾
1891 * @param {selector} target
1892 * @return {image2D}
1893 */
1894 var appendTo = function appendTo(target, context) {
1895 var nodes = sizzle(target, context || document);
1896 if (nodes.length > 0) {
1897 for (var i = 0; i < this.length; i++) {
1898 nodes[0].appendChild(this[i]);
1899 }
1900 } else {
1901 throw new Error('Target empty!');
1902 }
1903 return this;
1904 };
1905
1906 /**
1907 * 把当前维护的结点加到目标结点内部的开头
1908 * @param {selector} target
1909 * @return {image2D}
1910 */
1911 var prependTo = function prependTo(target, context) {
1912 var nodes = sizzle(target, context || document);
1913 if (nodes.length > 0) {
1914 for (var i = 0; i < this.length; i++) {
1915 nodes[0].insertBefore(this[i], nodes[0].childNodes[0]);
1916 }
1917 } else {
1918 throw new Error('Target empty!');
1919 }
1920 return this;
1921 };
1922
1923 /**
1924 * 把当前维护的结点加到目标结点之后
1925 * @param {selector} target
1926 * @return {image2D}
1927 */
1928 var afterTo = function afterTo(target, context) {
1929 var nodes = sizzle(target, context || document);
1930 if (nodes.length > 0) {
1931 for (var i = 0; i < this.length; i++) {
1932 //如果第二个参数undefined,在结尾追加,目的一样达到
1933 nodes[0].parentNode.insertBefore(this[i], nodes[0].nextSibling);
1934 }
1935 } else {
1936 throw new Error('Target empty!');
1937 }
1938 return this;
1939 };
1940
1941 /**
1942 * 把当前维护的结点加到目标结点之前
1943 * @param {selector} target
1944 * @return {image2D}
1945 */
1946 var beforeTo = function beforeTo(target, context) {
1947 var nodes = sizzle(target, context || document);
1948 if (nodes.length > 0) {
1949 for (var i = 0; i < this.length; i++) {
1950 nodes[0].parentNode.insertBefore(this[i], nodes[0]);
1951 }
1952 } else {
1953 throw new Error('Target empty!');
1954 }
1955 return this;
1956 };
1957
1958 // 删除当前维护的结点
1959 var remove = function remove() {
1960 for (var i = 0; i < this.length; i++) {
1961 this[i].parentNode.removeChild(this[i]);
1962 }return this;
1963 };
1964
1965 // 筛选当前结点
1966 var filter = function filter(filterback) {
1967 var temp = [];
1968 for (var i = 0; i < this.length; i++) {
1969 if (filterback(i, image2D(this[i]))) temp.push(this[i]);
1970 }
1971 return image2D(temp);
1972 };
1973
1974 // 修改文本或获取结点文本
1975 var text = function text(content) {
1976 if (arguments.length > 0) {
1977 for (var i = 0; i < this.length; i++) {
1978 this[i].textContent = content;
1979 }return this;
1980 }
1981 if (this.length <= 0) throw new Error('Target empty!');
1982 return this[0].textContent;
1983 };
1984
1985 // 设置或获取结点中的xhtml字符串模板(相当于innerHTML)
1986 var html = function html(xhtmlString) {
1987 if (arguments.length > 0) {
1988 for (var i = 0; i < this.length; i++) {
1989
1990 // 如果是SVG标签
1991 if (/[a-z]/.test(this[i].tagName)) {
1992 setSVG(this[i], xhtmlString);
1993 }
1994
1995 // 否则是普通html标签
1996 else {
1997 this[i].innerHTML = xhtmlString;
1998 }
1999 }
2000 return this;
2001 }
2002 if (this.length <= 0) throw new Error('Target empty!');
2003 return this[0].innerHTML;
2004 };
2005
2006 // 获取元素大小
2007 var size = function size(type) {
2008 if (this.length <= 0) throw new Error('Target empty!');
2009
2010 var elemHeight = void 0,
2011 elemWidth = void 0,
2012 dom = this[0];
2013
2014 if (type == 'content') {
2015 //内容
2016 elemWidth = dom.clientWidth - (getStyle(dom, 'padding-left') + "").replace('px', '') - (getStyle(dom, 'padding-right') + "").replace('px', '');
2017 elemHeight = dom.clientHeight - (getStyle(dom, 'padding-top') + "").replace('px', '') - (getStyle(dom, 'padding-bottom') + "").replace('px', '');
2018 } else if (type == 'padding') {
2019 //内容+内边距
2020 elemWidth = dom.clientWidth;
2021 elemHeight = dom.clientHeight;
2022 } else if (type == 'border') {
2023 //内容+内边距+边框
2024 elemWidth = dom.offsetWidth;
2025 elemHeight = dom.offsetHeight;
2026 } else if (type == 'scroll') {
2027 //包含滚动的尺寸(不包括border)
2028 elemWidth = dom.scrollWidth;
2029 elemHeight = dom.scrollHeight;
2030 } else {
2031 elemWidth = dom.offsetWidth;
2032 elemHeight = dom.offsetHeight;
2033 }
2034 return {
2035 width: elemWidth,
2036 height: elemHeight
2037 };
2038 };
2039
2040 /**
2041 * 设置或获取样式
2042 * @arguments(key):获取指定样式
2043 * @arguments(key,value):设置指定样式
2044 * @arguments():获取全部样式
2045 * @arguments(json):设置大量样式
2046 */
2047 function style() {
2048
2049 // 获取样式
2050 if (arguments.length <= 1 && (arguments.length <= 0 || _typeof(arguments[0]) !== 'object')) {
2051 if (this.length <= 0) throw new Error('Target empty!');
2052
2053 // 为了获取非style定义的样式,需要使用特殊的方法获取
2054 return getStyle(this[0], arguments[0]);
2055 }
2056
2057 // 设置样式
2058 for (var i = 0; i < this.length; i++) {
2059 if (arguments.length === 1) {
2060 for (var key in arguments[0]) {
2061 this[i].style[key] = arguments[0][key];
2062 }
2063 } else this[i].style[arguments[0]] = arguments[1];
2064 }
2065
2066 return this;
2067 }
2068
2069 var setAttribute = function setAttribute(dom, attr, val) {
2070 if (/[a-z]/.test(dom.tagName) && XLINK_ATTRIBUTE.indexOf(attr) >= 0) {
2071 // 如果是xml元素
2072 // 针对xlink使用特殊方法赋值
2073 dom.setAttributeNS(NAMESPACE.xlink, 'xlink:' + attr, val);
2074 } else dom.setAttribute(attr, val);
2075 };
2076
2077 /**
2078 * 设置或获取属性
2079 * @arguments(attr):获取属性
2080 * @arguments(attr,value):设置指定属性值
2081 * @arguments(json):设置大量属性
2082 */
2083 function attribute() {
2084
2085 // 获取属性值
2086 if (arguments.length === 1 && _typeof(arguments[0]) !== 'object') {
2087 if (this.length <= 0) throw new Error('Target empty!');
2088 return this[0].getAttribute(arguments[0]);
2089 }
2090
2091 // 设置属性值
2092 else if (arguments.length > 0) {
2093 for (var i = 0; i < this.length; i++) {
2094 if (arguments.length === 1) {
2095 for (var key in arguments[0]) {
2096 setAttribute(this[i], key, arguments[0][key]);
2097 }
2098 } else setAttribute(this[i], arguments[0], arguments[1]);
2099 }
2100 }
2101
2102 return this;
2103 }
2104
2105 // 用于把数据绑定到一组结点或返回第一个结点数据
2106 // 可以传递函数对数据处理
2107 var datum = function datum(data, calcback) {
2108
2109 // 获取数据
2110 if (arguments.length <= 0) {
2111 if (this.length <= 0) throw new Error('Target empty!');
2112 return this[0].__data__;
2113 }
2114
2115 // 设置数据
2116 for (var i = 0; i < this.length; i++) {
2117 this[i].__data__ = typeof calcback === 'function' ? calcback(data, i) : data;
2118 }return this;
2119 };
2120
2121 // 用于把一组数据绑定到一组结点或返回一组结点数据
2122 // 可以传递函数对数据处理
2123 var data = function data(datas, calcback) {
2124
2125 // 获取数据
2126 if (arguments.length <= 0) {
2127 var _temp3 = [];
2128 for (var _i5 = 0; _i5 < this.length; _i5++) {
2129 _temp3[_i5] = this[_i5].__data__;
2130 }return _temp3;
2131 }
2132
2133 // 设置数据
2134 var temp = [],
2135 i = void 0;
2136 for (i = 0; i < this.length && i < datas.length; i++) {
2137 this[i].__data__ = typeof calcback === 'function' ? calcback(datas[i], i) : datas[i];
2138 temp.push(this[i]);
2139 }
2140 var newImage2D = image2D(temp);
2141
2142 // 记录需要去平衡的数据
2143 newImage2D.__enter__ = [];
2144 for (; i < datas.length; i++) {
2145 newImage2D.__enter__.push(typeof calcback === 'function' ? calcback(datas[i], i) : datas[i]);
2146 } // 记录需要去平衡的结点
2147 newImage2D.__exit__ = [];
2148 for (; i < this.length; i++) {
2149 newImage2D.__exit__.push(this[i]);
2150 }return newImage2D;
2151 };
2152
2153 // 把过滤出来多于结点的数据部分变成结点返回
2154 // 需要传递一个字符串来标明新创建元素是什么
2155 var enter = function enter(template, type) {
2156
2157 if (!this.__enter__ || this.__enter__.constructor !== Array) throw new Error('Not a data node object to be balanced!');
2158
2159 var temp = [];
2160 for (var i = 0; i < this.__enter__.length; i++) {
2161 temp[i] = toNode$1(template, type);
2162 temp[i].__data__ = this.__enter__[i];
2163 }
2164
2165 delete this.__enter__;
2166 return image2D(temp);
2167 };
2168
2169 // 把过滤出来多于数据的结点部分返回
2170 var exit = function exit() {
2171
2172 if (!this.__exit__ || this.__exit__.constructor !== Array) throw new Error('Not a data node object to be balanced!');
2173
2174 var exitImage2D = image2D(this.__exit__);
2175 delete this.__exit__;
2176 return exitImage2D;
2177 };
2178
2179 // 在维护的结点上轮询执行传入的方法
2180 // doback(data,index,image2D)
2181 var loop = function loop(doback) {
2182
2183 for (var i = 0; i < this.length; i++) {
2184 doback(this[i].__data__, i, image2D(this[i]));
2185 }return this;
2186 };
2187
2188 // r1和r2,内半径和外半径
2189 // beginA起点弧度,rotateA旋转弧度式
2190 function arc(beginA, rotateA, cx, cy, r1, r2, doback) {
2191
2192 // 有了前置的判断,这里可以省略了
2193 // if (rotateA > Math.PI * 2) rotateA = Math.PI * 2;
2194 // if (rotateA < -Math.PI * 2) rotateA = -Math.PI * 2;
2195
2196 // 保证逆时针也是可以的
2197 if (rotateA < 0) {
2198 beginA += rotateA;
2199 rotateA *= -1;
2200 }
2201
2202 var temp = [],
2203 p = void 0;
2204
2205 // 内部
2206 p = _rotate2(0, 0, beginA, r1, 0);
2207 temp[0] = p[0];
2208 temp[1] = p[1];
2209 p = _rotate2(0, 0, rotateA, p[0], p[1]);
2210 temp[2] = p[0];
2211 temp[3] = p[1];
2212
2213 // 外部
2214 p = _rotate2(0, 0, beginA, r2, 0);
2215 temp[4] = p[0];
2216 temp[5] = p[1];
2217 p = _rotate2(0, 0, rotateA, p[0], p[1]);
2218 temp[6] = p[0];
2219 temp[7] = p[1];
2220
2221 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);
2222 }
2223
2224 // 文字统一设置方法
2225 var initText = function initText(painter, config, x, y, deg) {
2226
2227 painter.beginPath();
2228 painter.translate(x, y);
2229 painter.rotate(deg);
2230 painter.font = config['font-size'] + "px " + config['font-family'];
2231 return painter;
2232 };
2233
2234 // 画弧统一设置方法
2235 var initArc = function initArc(painter, config, cx, cy, r1, r2, beginDeg, deg) {
2236
2237 if (r1 > r2) {
2238 var temp = r1;
2239 r1 = r2;
2240 r2 = temp;
2241 }
2242
2243 beginDeg = beginDeg % (Math.PI * 2);
2244
2245 // 当|deg|>=2π的时候都认为是一个圆环
2246 // 为什么不取2π比较,是怕部分浏览器浮点不精确,同时也是为了和svg保持一致
2247 if (deg >= Math.PI * 1.999999 || deg <= -Math.PI * 1.999999) {
2248 deg = Math.PI * 2;
2249 } else {
2250 deg = deg % (Math.PI * 2);
2251 }
2252
2253 arc(beginDeg, deg, cx, cy, r1, r2, function (beginA, endA, begInnerX, begInnerY, begOuterX, begOuterY, endInnerX, endInnerY, endOuterX, endOuterY, r) {
2254 if (r < 0) r = -r;
2255 painter.beginPath();
2256 painter.moveTo(begInnerX, begInnerY);
2257 painter.arc(
2258 // (圆心x,圆心y,半径,开始角度,结束角度,true逆时针/false顺时针)
2259 cx, cy, r1, beginA, endA, false);
2260 // 结尾
2261 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);
2262 painter.arc(cx, cy, r2, endA, beginA, true);
2263 // 开头
2264 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);
2265 });
2266 if (config["arc-start-cap"] == 'butt') painter.closePath();
2267 return painter;
2268 };
2269
2270 // 画圆统一设置方法
2271 var initCircle = function initCircle(painter, cx, cy, r) {
2272 painter.beginPath();
2273 painter.moveTo(cx + r, cy);
2274 painter.arc(cx, cy, r, 0, Math.PI * 2);
2275 return painter;
2276 };
2277
2278 // 画矩形统一设置方法
2279 var initRect = function initRect(painter, x, y, width, height) {
2280 painter.beginPath();
2281 painter.rect(x, y, width, height);
2282 return painter;
2283 };
2284
2285 // 线性渐变
2286 var linearGradient = function linearGradient(painter, x0, y0, x1, y1) {
2287 var gradient = painter.createLinearGradient(x0, y0, x1, y1);
2288 var enhanceGradient = {
2289 "value": function value() {
2290 return gradient;
2291 },
2292 "addColorStop": function addColorStop(stop, color) {
2293 gradient.addColorStop(stop, color);
2294 return enhanceGradient;
2295 }
2296 };
2297 return enhanceGradient;
2298 };
2299
2300 // 环形渐变
2301 var radialGradient = function radialGradient(painter, cx, cy, r) {
2302 var gradient = painter.createRadialGradient(cx, cy, 0, cx, cy, r);
2303 var enhanceGradient = {
2304 "value": function value() {
2305 return gradient;
2306 },
2307 "addColorStop": function addColorStop(stop, color) {
2308 gradient.addColorStop(stop, color);
2309 return enhanceGradient;
2310 }
2311 };
2312 return enhanceGradient;
2313 };
2314
2315 // 加强版本的画笔
2316 function painter_canvas2D(canvas, noHiddenWarn) {
2317
2318 // 获取canvas2D画笔
2319 var painter = canvas.getContext("2d");
2320
2321 var isLayer = canvas.__image2D__layer__ == 'yes';
2322
2323 // 图层是内部的,明确获取方法
2324 // 对外的一律使用clientXXX,区分是否显示
2325 var width = isLayer ? canvas.getAttribute('width') : canvas.clientWidth,
2326 //内容+内边距
2327 height = isLayer ? canvas.getAttribute('height') : canvas.clientHeight;
2328
2329 if (width == 0 || height == 0) {
2330
2331 if (!noHiddenWarn) console.warn('Canvas is hidden or size is zero!');
2332
2333 if (canvas.__image2D__noLayer_getSize__ == 'yes') {
2334
2335 width = canvas.width / 2;
2336 height = canvas.height / 2;
2337 } else {
2338
2339 width = canvas.width;
2340 height = canvas.height;
2341
2342 // 标记已经特殊获取大小了
2343 canvas.__image2D__noLayer_getSize__ = 'yes';
2344 }
2345 }
2346
2347 // 设置显示大小
2348 canvas.style.width = width + "px";
2349 canvas.style.height = height + "px";
2350
2351 // 设置画布大小(画布大小设置为显示的二倍,使得显示的时候更加清晰)
2352 canvas.setAttribute('width', width * 2);
2353 canvas.setAttribute('height', height * 2);
2354
2355 // 通过缩放实现模糊问题
2356 painter.scale(2, 2);
2357
2358 // 默认配置canvas2D对象已经存在的属性
2359 painter.textBaseline = 'middle';
2360 painter.textAlign = 'left';
2361
2362 // 默认配置不应该有canvas2D对象已经存在的属性
2363 // 这里是为了简化或和svg统一接口而自定义的属性
2364 var config = {
2365 "font-size": "16", // 文字大小
2366 "font-family": "sans-serif", // 字体
2367 "arc-start-cap": "butt", // 弧开始闭合方式
2368 "arc-end-cap": "butt" // 弧结束闭合方式
2369 };
2370
2371 // 配置生效方法
2372 var useConfig = function useConfig(key, value) {
2373
2374 /**
2375 * -----------------------------
2376 * 特殊的设置开始
2377 * -----------------------------
2378 */
2379
2380 if (key == 'lineDash') {
2381 painter.setLineDash(value);
2382 }
2383
2384 /**
2385 * -----------------------------
2386 * 常规的配置开始
2387 * -----------------------------
2388 */
2389
2390 // 如果已经存在默认配置中,说明只需要缓存起来即可
2391 else if (config[key]) {
2392 config[key] = value;
2393 }
2394
2395 // 其它情况直接生效即可
2396 else {
2397 painter[key] = value;
2398 }
2399 };
2400
2401 // 画笔
2402 var enhancePainter = {
2403
2404 // 属性设置或获取
2405 "config": function config() {
2406 if (arguments.length === 1) {
2407 if (_typeof(arguments[0]) !== 'object') return painter[arguments[0]];
2408 for (var key in arguments[0]) {
2409 useConfig(key, arguments[0][key]);
2410 }
2411 } else if (arguments.length === 2) {
2412 useConfig(arguments[0], arguments[1]);
2413 }
2414 return enhancePainter;
2415 },
2416
2417 // 文字
2418 "fillText": function fillText(text, x, y, deg) {
2419 painter.save();
2420 initText(painter, config, x, y, deg || 0).fillText(text, 0, 0);
2421 painter.restore();
2422 return enhancePainter;
2423 },
2424 "strokeText": function strokeText(text, x, y, deg) {
2425 painter.save();
2426 initText(painter, config, x, y, deg || 0).strokeText(text, 0, 0);
2427 painter.restore();
2428 return enhancePainter;
2429 },
2430 "fullText": function fullText(text, x, y, deg) {
2431 painter.save();
2432 initText(painter, config, x, y, deg || 0);
2433 painter.fillText(text, 0, 0);
2434 painter.strokeText(text, 0, 0);
2435 painter.restore();
2436 return enhancePainter;
2437 },
2438
2439 // 路径
2440 "beginPath": function beginPath() {
2441 painter.beginPath();return enhancePainter;
2442 },
2443 "closePath": function closePath() {
2444 painter.closePath();return enhancePainter;
2445 },
2446 "moveTo": function moveTo(x, y) {
2447 painter.moveTo(x, y);return enhancePainter;
2448 },
2449 "lineTo": function lineTo(x, y) {
2450 painter.lineTo(x, y);return enhancePainter;
2451 },
2452 "arc": function arc(x, y, r, beginDeg, deg) {
2453 painter.arc(x, y, r, beginDeg, beginDeg + deg, deg < 0);
2454 return enhancePainter;
2455 },
2456 "fill": function fill() {
2457 painter.fill();return enhancePainter;
2458 },
2459 "stroke": function stroke() {
2460 painter.stroke();return enhancePainter;
2461 },
2462 "full": function full() {
2463 painter.fill();painter.stroke();return enhancePainter;
2464 },
2465
2466 "save": function save() {
2467 painter.save();return enhancePainter;
2468 },
2469 "restore": function restore() {
2470 painter.restore();return enhancePainter;
2471 },
2472
2473 // 路径 - 贝塞尔曲线
2474 "quadraticCurveTo": function quadraticCurveTo(cpx, cpy, x, y) {
2475 painter.quadraticCurveTo(cpx, cpy, x, y);return enhancePainter;
2476 },
2477 "bezierCurveTo": function bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
2478 painter.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);return enhancePainter;
2479 },
2480
2481 // 擦除画面
2482 "clearRect": function clearRect(x, y, width, height) {
2483 painter.clearRect(x || 0, y || 0, width || canvas.getAttribute('width') / 2, height || canvas.getAttribute('height') / 2);return enhancePainter;
2484 },
2485
2486 // 地址图片
2487 "toDataURL": function toDataURL() {
2488 return canvas.toDataURL();
2489 },
2490
2491 // image
2492 // v1.5.0开始,做了参数调整(非向下兼容)
2493 "drawImage": function drawImage(img, sx, sy, sw, sh, x, y, w, h) {
2494 sx = sx || 0;
2495 sy = sy || 0;
2496 x = x || 0;
2497 y = y || 0;
2498 w = w ? w * 2 : canvas.getAttribute('width');
2499 h = h ? h * 2 : canvas.getAttribute('height');
2500
2501 if (img.nodeName == 'CANVAS') {
2502 // 我们不考虑别的canvas,我们认为我们面对的canvas都是自己控制的
2503 // 如果有必要,未来可以对任意canvas进行向下兼容
2504 w = w / 2;
2505 h = h / 2;
2506 sw = sw ? sw * 2 : canvas.getAttribute('width');
2507 sh = sh ? sh * 2 : canvas.getAttribute('height');
2508 } else {
2509 // 默认类型是图片
2510 sw = (sw || img.width) * 2;
2511 sh = (sh || img.height) * 2;
2512 }
2513
2514 painter.drawImage(img, sx, sy, sw, sh, x, y, w, h);
2515 return enhancePainter;
2516 },
2517
2518 // 弧
2519 "fillArc": function fillArc(cx, cy, r1, r2, beginDeg, deg) {
2520 initArc(painter, config, cx, cy, r1, r2, beginDeg, deg).fill();return enhancePainter;
2521 },
2522 "strokeArc": function strokeArc(cx, cy, r1, r2, beginDeg, deg) {
2523 initArc(painter, config, cx, cy, r1, r2, beginDeg, deg).stroke();return enhancePainter;
2524 },
2525 "fullArc": function fullArc(cx, cy, r1, r2, beginDeg, deg) {
2526 initArc(painter, config, cx, cy, r1, r2, beginDeg, deg);
2527 painter.fill();
2528 painter.stroke();
2529 return enhancePainter;
2530 },
2531
2532 // 圆形
2533 "fillCircle": function fillCircle(cx, cy, r) {
2534 initCircle(painter, cx, cy, r).fill();return enhancePainter;
2535 },
2536 "strokeCircle": function strokeCircle(cx, cy, r) {
2537 initCircle(painter, cx, cy, r).stroke();return enhancePainter;
2538 },
2539 "fullCircle": function fullCircle(cx, cy, r) {
2540 initCircle(painter, cx, cy, r);
2541 painter.fill();
2542 painter.stroke();
2543 return enhancePainter;
2544 },
2545
2546 // 矩形
2547 "fillRect": function fillRect(x, y, width, height) {
2548 initRect(painter, x, y, width, height).fill();return enhancePainter;
2549 },
2550 "strokeRect": function strokeRect(x, y, width, height) {
2551 initRect(painter, x, y, width, height).stroke();return enhancePainter;
2552 },
2553 "fullRect": function fullRect(x, y, width, height) {
2554 initRect(painter, x, y, width, height);
2555 painter.fill();
2556 painter.stroke();
2557 return enhancePainter;
2558 },
2559
2560 /**
2561 * 渐变
2562 * -------------
2563 */
2564
2565 // 线性渐变
2566 "createLinearGradient": function createLinearGradient(x0, y0, x1, y1) {
2567 return linearGradient(painter, x0, y0, x1, y1);
2568 },
2569
2570 // 环形渐变
2571 "createRadialGradient": function createRadialGradient(cx, cy, r) {
2572 return radialGradient(painter, cx, cy, r);
2573 },
2574
2575 /**
2576 * 变换
2577 * --------------
2578 */
2579
2580 // 移动
2581 // 用来移动 canvas 的原点到指定的位置
2582 "translate": function translate(x, y) {
2583 painter.translate(x, y);return enhancePainter;
2584 },
2585
2586 // 旋转
2587 "rotate": function rotate(deg) {
2588 painter.rotate(deg);return enhancePainter;
2589 },
2590
2591 // 缩放
2592 "scale": function scale(x, y) {
2593 y = y || x;painter.scale(x, y);return enhancePainter;
2594 }
2595 };
2596
2597 return enhancePainter;
2598 }
2599
2600 function normalConfig(key, value) {
2601
2602 // 文字水平对齐方式
2603 if (key === 'textAlign') {
2604 return {
2605 "left": "start",
2606 "right": "end",
2607 "center": "middle"
2608 }[value] || value;
2609 }
2610
2611 return value;
2612 }
2613 // 文字统一设置方法
2614 var initText$1 = function initText$1(painter, config, x, y, deg) {
2615 if (!painter || painter.length <= 0 || painter[0].nodeName.toLowerCase() !== 'text') throw new Error('Need a <text> !');
2616
2617 deg = deg % (Math.PI * 2);
2618
2619 // 垂直对齐采用dy实现
2620 painter.attr('dy', {
2621 "top": config['font-size'] * 0.5,
2622 "middle": 0,
2623 "bottom": -config['font-size'] * 0.5
2624 }[config.textBaseline]).css({
2625
2626 // 文字对齐方式
2627 "text-anchor": config.textAlign,
2628 "dominant-baseline": "central",
2629
2630 // 文字大小和字体设置
2631 "font-size": config['font-size'] + "px",
2632 "font-family": config['font-family']
2633 }).attr({ "x": x, "y": y });
2634
2635 return {
2636 "transform": "rotate(" + deg * 180 / Math.PI + "," + x + "," + y + ")"
2637 };
2638 };
2639
2640 // 画弧统一设置方法
2641 var initArc$1 = function initArc$1(painter, config, cx, cy, r1, r2, beginDeg, deg) {
2642
2643 if (!painter || painter.length <= 0 || painter[0].nodeName.toLowerCase() !== 'path') throw new Error('Need a <path> !');
2644
2645 beginDeg = beginDeg % (Math.PI * 2);
2646
2647 if (r1 > r2) {
2648 var temp = r1;
2649 r1 = r2;
2650 r2 = temp;
2651 }
2652
2653 // 当|deg|>=2π的时候都认为是一个圆环
2654 if (deg >= Math.PI * 1.999999 || deg <= -Math.PI * 1.999999) {
2655 deg = Math.PI * 1.999999;
2656 } else {
2657 deg = deg % (Math.PI * 2);
2658 }
2659
2660 arc(beginDeg, deg, cx, cy, r1, r2, function (beginA, endA, begInnerX, begInnerY, begOuterX, begOuterY, endInnerX, endInnerY, endOuterX, endOuterY, r) {
2661 var f = endA - beginA > Math.PI ? 1 : 0,
2662 d = "M" + begInnerX + " " + begInnerY;
2663 if (r < 0) r = -r;
2664 d +=
2665 // 横半径 竖半径 x轴偏移角度 0小弧/1大弧 0逆时针/1顺时针 终点x 终点y
2666 "A" + r1 + " " + r1 + " 0 " + f + " 1 " + endInnerX + " " + endInnerY;
2667 // 结尾
2668 if (config["arc-end-cap"] != 'round') d += "L" + endOuterX + " " + endOuterY;else d += "A" + r + " " + r + " " + " 0 1 0 " + endOuterX + " " + endOuterY;
2669 d += "A" + r2 + " " + r2 + " 0 " + f + " 0 " + begOuterX + " " + begOuterY;
2670 // 开头
2671 if (config["arc-start-cap"] != 'round') d += "L" + begInnerX + " " + begInnerY;else d += "A" + r + " " + r + " " + " 0 1 0 " + begInnerX + " " + begInnerY;
2672 if (config["arc-start-cap"] == 'butt') d += "Z";
2673 painter.attr('d', d);
2674 });
2675 return painter;
2676 };
2677
2678 // 画圆统一设置方法
2679 var initCircle$1 = function initCircle$1(painter, cx, cy, r) {
2680 if (!painter || painter.length <= 0 || painter[0].nodeName.toLowerCase() !== 'circle') throw new Error('Need a <circle> !');
2681 painter.attr({
2682 "cx": cx,
2683 "cy": cy,
2684 "r": r
2685 });
2686 return painter;
2687 };
2688
2689 // 路径统一设置方法
2690 var initPath = function initPath(painter, path) {
2691 if (!painter || painter.length <= 0 || painter[0].nodeName.toLowerCase() !== 'path') throw new Error('Need a <path> !');
2692 painter.attr('d', path);
2693 return painter;
2694 };
2695
2696 // 画矩形统一设置方法
2697 var initRect$1 = function initRect$1(painter, x, y, width, height) {
2698 if (!painter || painter.length <= 0 || painter[0].nodeName.toLowerCase() !== 'rect') throw new Error('Need a <rect> !');
2699
2700 // 由于height和宽不可以是负数,校对一下
2701
2702 if (height < 0) {
2703 height *= -1;y -= height;
2704 }
2705
2706 if (width < 0) {
2707 width *= -1;x -= width;
2708 }
2709
2710 painter.attr({
2711 "x": x,
2712 "y": y,
2713 "width": width,
2714 "height": height
2715 });
2716 return painter;
2717 };
2718
2719 var initDefs = function initDefs(target) {
2720 var defs = target.getElementsByTagName('defs');
2721 if (defs.length <= 0) {
2722 defs = [toNode$1("<defs>", "SVG")];
2723 target.appendChild(defs[0]);
2724 }
2725 return defs[0];
2726 };
2727
2728 // 线性渐变
2729 var linearGradient$1 = function linearGradient$1(painter, target, x0, y0, x1, y1) {
2730 var defs = initDefs(target);
2731 var gradientId = "image2D-lg-" + new Date().valueOf() + "-" + Math.random();
2732 var gradientDom = toNode$1('<linearGradient id="' + gradientId + '" x1="' + x0 + '%" y1="' + y0 + '%" x2="' + x1 + '%" y2="' + y1 + '%"></linearGradient>');
2733 defs.appendChild(gradientDom);
2734 var enhanceGradient = {
2735 "value": function value() {
2736 return "url(#" + gradientId + ")";
2737 },
2738 "addColorStop": function addColorStop(stop, color) {
2739 gradientDom.appendChild(toNode$1('<stop offset="' + stop * 100 + '%" style="stop-color:' + color + ';" />'));
2740 return enhanceGradient;
2741 }
2742 };
2743 return enhanceGradient;
2744 };
2745
2746 // 环形渐变
2747 var radialGradient$1 = function radialGradient$1(painter, target, cx, cy, r) {
2748 var defs = initDefs(target);
2749 var gradientId = "image2D-rg-" + new Date().valueOf() + "-" + Math.random();
2750 var gradientDom = toNode$1('<radialGradient id="' + gradientId + '" cx="' + cx + '%" cy="' + cy + '%" r="' + r + '%"></radialGradient>');
2751 defs.appendChild(gradientDom);
2752 var enhanceGradient = {
2753 "value": function value() {
2754 return "url(#" + gradientId + ")";
2755 },
2756 "addColorStop": function addColorStop(stop, color) {
2757 gradientDom.appendChild(toNode$1('<stop offset="' + stop * 100 + '%" style="stop-color:' + color + ';" />'));
2758 return enhanceGradient;
2759 }
2760 };
2761 return enhanceGradient;
2762 };
2763
2764 function painter_svg(target, selector) {
2765
2766 var painter = void 0;
2767 if (selector) painter = image2D(selector, target);
2768
2769 // 类似canvas画笔的属性
2770 var _config2 = {
2771
2772 // 基本设置
2773 "fillStyle": "#000",
2774 "strokeStyle": "#000",
2775 "lineWidth": 1,
2776
2777 // 文字对齐方式
2778 "textAlign": "start",
2779 "textBaseline": "middle",
2780
2781 // 文字设置
2782 "font-size": "16",
2783 "font-family": "sans-serif",
2784
2785 // arc二端闭合方式['butt':直线闭合,'round':圆帽闭合]
2786 "arc-start-cap": "butt",
2787 "arc-end-cap": "butt",
2788
2789 // 虚线设置
2790 "lineDash": []
2791
2792 };
2793
2794 // 路径(和canvas2D的类似)
2795 var path = "",
2796 currentPosition = [];
2797
2798 // 变换(和canvas2D的类似,内部维护了用于记录)
2799 var transform_history = [],
2800 transform_current = "";
2801
2802 // 画笔
2803 var enhancePainter = {
2804
2805 // 属性设置或获取
2806 "config": function config() {
2807 if (arguments.length === 1) {
2808 if (_typeof(arguments[0]) !== 'object') return _config2[arguments[0]];
2809 for (var key in arguments[0]) {
2810 _config2[key] = normalConfig(key, arguments[0][key]);
2811 }
2812 } else if (arguments.length === 2) _config2[arguments[0]] = normalConfig(arguments[0], arguments[1]);
2813 return enhancePainter;
2814 },
2815
2816 // 基础方法
2817 "bind": function bind(selector) {
2818 painter = image2D(selector, target);return enhancePainter;
2819 },
2820 "appendTo": function appendTo(selector) {
2821 painter.appendTo(selector || target, target);return enhancePainter;
2822 },
2823 "prependTo": function prependTo(selector) {
2824 painter.prependTo(selector || target, target);return enhancePainter;
2825 },
2826 "afterTo": function afterTo(selector) {
2827 painter.afterTo(selector || target, target);return enhancePainter;
2828 },
2829 "beforeTo": function beforeTo(selector) {
2830 painter.beforeTo(selector || target, target);return enhancePainter;
2831 },
2832
2833 // 路径
2834 "beginPath": function beginPath() {
2835 path = "";currentPosition = [];return enhancePainter;
2836 },
2837 "closePath": function closePath() {
2838 path += "Z";return enhancePainter;
2839 },
2840 "moveTo": function moveTo(x, y) {
2841 path += "M" + x + " " + y;currentPosition = [x, y];return enhancePainter;
2842 },
2843 "lineTo": function lineTo(x, y) {
2844 path += (path == "" ? "M" : "L") + x + " " + y;currentPosition = [x, y];return enhancePainter;
2845 },
2846 "arc": function arc(x, y, r, beginDeg, deg) {
2847 var begPosition = _rotate2(x, y, beginDeg, x + r, y);
2848 var endPosition = _rotate2(x, y, beginDeg + deg, x + r, y);
2849 beginDeg = beginDeg / Math.PI * 180;
2850 deg = deg / Math.PI * 180;
2851 // 如果当前没有路径,说明是开始的,就移动到正确位置
2852 if (path == '') {
2853 path += "M" + begPosition[0] + "," + begPosition[1];
2854 }
2855 // 如果当前有路径,位置不正确,应该画到正确位置(和canvas保持一致)
2856 else if (begPosition[0] != currentPosition[0] || begPosition[1] != currentPosition[1]) {
2857 path += "L" + begPosition[0] + "," + begPosition[1];
2858 }
2859 path += "A" + r + "," + r + " 0 " + (deg > 180 || deg < -180 ? 1 : 0) + "," + (deg > 0 ? 1 : 0) + " " + endPosition[0] + "," + endPosition[1];
2860 return enhancePainter;
2861 },
2862 "fill": function fill() {
2863 initPath(painter, path).attr('transform', transform_current).attr("fill", _config2.fillStyle);
2864 return enhancePainter;
2865 },
2866 "stroke": function stroke() {
2867 initPath(painter, path).attr('transform', transform_current).attr({
2868 "stroke-width": _config2.lineWidth,
2869 "stroke": _config2.strokeStyle,
2870 "fill": "none",
2871 "stroke-dasharray": _config2.lineDash.join(',')
2872 });
2873 return enhancePainter;
2874 },
2875 "full": function full() {
2876 initPath(painter, path).attr('transform', transform_current).attr({
2877 "stroke-width": _config2.lineWidth,
2878 "stroke": _config2.strokeStyle,
2879 "fill": _config2.fillStyle,
2880 "stroke-dasharray": _config2.lineDash.join(',')
2881 });
2882 return enhancePainter;
2883 },
2884
2885 "save": function save() {
2886 transform_history.push(transform_current);
2887 return enhancePainter;
2888 },
2889 "restore": function restore() {
2890 if (transform_history.length > 0) transform_current = transform_history.pop();
2891 return enhancePainter;
2892 },
2893
2894 // 路径 - 贝塞尔曲线
2895 "quadraticCurveTo": function quadraticCurveTo(cpx, cpy, x, y) {
2896 path += "Q" + cpx + " " + cpy + "," + x + " " + y;return enhancePainter;
2897 },
2898 "bezierCurveTo": function bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
2899 path += "C" + cp1x + " " + cp1y + "," + cp2x + " " + cp2y + "," + x + " " + y;return enhancePainter;
2900 },
2901
2902 // 文字
2903 "fillText": function fillText(text, x, y, deg) {
2904 var returnJSon = initText$1(painter, _config2, x, y, deg || 0);
2905 painter.attr('transform', transform_current + returnJSon.transform).attr("fill", _config2.fillStyle)[0].textContent = text;
2906 return enhancePainter;
2907 },
2908 "strokeText": function strokeText(text, x, y, deg) {
2909 var returnJSon = initText$1(painter, _config2, x, y, deg || 0);
2910 painter.attr('transform', transform_current + returnJSon.transform).attr({
2911 "stroke": _config2.strokeStyle,
2912 "fill": "none",
2913 "stroke-dasharray": _config2.lineDash.join(',')
2914 })[0].textContent = text;
2915 return enhancePainter;
2916 },
2917 "fullText": function fullText(text, x, y, deg) {
2918 var returnJSon = initText$1(painter, _config2, x, y, deg || 0);
2919 painter.attr('transform', transform_current + returnJSon.transform).attr({
2920 "stroke": _config2.strokeStyle,
2921 "fill": _config2.fillStyle,
2922 "stroke-dasharray": _config2.lineDash.join(',')
2923 })[0].textContent = text;
2924 return enhancePainter;
2925 },
2926
2927 // 弧
2928 "fillArc": function fillArc(cx, cy, r1, r2, beginDeg, deg) {
2929 initArc$1(painter, _config2, cx, cy, r1, r2, beginDeg, deg).attr('transform', transform_current).attr("fill", _config2.fillStyle);
2930 return enhancePainter;
2931 },
2932 "strokeArc": function strokeArc(cx, cy, r1, r2, beginDeg, deg) {
2933 initArc$1(painter, _config2, cx, cy, r1, r2, beginDeg, deg).attr('transform', transform_current).attr({
2934 "stroke-width": _config2.lineWidth,
2935 "stroke": _config2.strokeStyle,
2936 "fill": "none",
2937 "stroke-dasharray": _config2.lineDash.join(',')
2938 });
2939 return enhancePainter;
2940 },
2941 "fullArc": function fullArc(cx, cy, r1, r2, beginDeg, deg) {
2942 initArc$1(painter, _config2, cx, cy, r1, r2, beginDeg, deg).attr('transform', transform_current).attr({
2943 "stroke-width": _config2.lineWidth,
2944 "stroke": _config2.strokeStyle,
2945 "fill": _config2.fillStyle,
2946 "stroke-dasharray": _config2.lineDash.join(',')
2947 });
2948 return enhancePainter;
2949 },
2950
2951 // 圆形
2952 "fillCircle": function fillCircle(cx, cy, r) {
2953 initCircle$1(painter, cx, cy, r).attr('transform', transform_current).attr("fill", _config2.fillStyle);return enhancePainter;
2954 },
2955 "strokeCircle": function strokeCircle(cx, cy, r) {
2956 initCircle$1(painter, cx, cy, r).attr('transform', transform_current).attr({
2957 "stroke-width": _config2.lineWidth,
2958 "stroke": _config2.strokeStyle,
2959 "fill": "none",
2960 "stroke-dasharray": _config2.lineDash.join(',')
2961 });return enhancePainter;
2962 },
2963 "fullCircle": function fullCircle(cx, cy, r) {
2964 initCircle$1(painter, cx, cy, r).attr('transform', transform_current).attr({
2965 "stroke-width": _config2.lineWidth,
2966 "stroke": _config2.strokeStyle,
2967 "fill": _config2.fillStyle,
2968 "stroke-dasharray": _config2.lineDash.join(',')
2969 });return enhancePainter;
2970 },
2971
2972 // 矩形
2973 "fillRect": function fillRect(x, y, width, height) {
2974 initRect$1(painter, x, y, width, height).attr('transform', transform_current).attr("fill", _config2.fillStyle);return enhancePainter;
2975 },
2976 "strokeRect": function strokeRect(x, y, width, height) {
2977 initRect$1(painter, x, y, width, height).attr('transform', transform_current).attr({
2978 "stroke-width": _config2.lineWidth,
2979 "stroke": _config2.strokeStyle,
2980 "fill": "none",
2981 "stroke-dasharray": _config2.lineDash.join(',')
2982 });return enhancePainter;
2983 },
2984 "fullRect": function fullRect(x, y, width, height) {
2985 initRect$1(painter, x, y, width, height).attr('transform', transform_current).attr({
2986 "stroke-width": _config2.lineWidth,
2987 "stroke": _config2.strokeStyle,
2988 "fill": _config2.fillStyle,
2989 "stroke-dasharray": _config2.lineDash.join(',')
2990 });return enhancePainter;
2991 },
2992
2993 /**
2994 * 渐变
2995 * -------------
2996 */
2997
2998 // 线性渐变
2999 "createLinearGradient": function createLinearGradient(x0, y0, x1, y1) {
3000 return linearGradient$1(painter, target, x0, y0, x1, y1);
3001 },
3002
3003 // 环形渐变
3004 "createRadialGradient": function createRadialGradient(cx, cy, r) {
3005 return radialGradient$1(painter, target, cx, cy, r);
3006 },
3007
3008 /**
3009 * 变换
3010 * --------------
3011 */
3012
3013 // 移动
3014 "translate": function translate(x, y) {
3015 transform_current += ' translate(' + x + ',' + y + ')';
3016 return enhancePainter;
3017 },
3018
3019 // 旋转
3020 "rotate": function rotate(deg) {
3021 deg = deg % (Math.PI * 2);
3022 transform_current += ' rotate(' + deg / Math.PI * 180 + ')';
3023 return enhancePainter;
3024 },
3025
3026 // 缩放
3027 "scale": function scale(x, y) {
3028 y = y || x;
3029 transform_current += ' scale(' + x + ',' + y + ')';
3030 return enhancePainter;
3031 }
3032
3033 };
3034
3035 return enhancePainter;
3036 }
3037
3038 // 统一画笔
3039 // 负责启动具体的绘图对象
3040 function painter() {
3041
3042 // 因为绘图画布是必须的,因此在判断画布类型前,如果压根没有结点,肯定是非法的
3043 if (!isElement(this[0])) throw new Error('Target empty!');
3044
3045 var target = this[0],
3046 nodeName = target.nodeName.toLowerCase();
3047
3048 // canvas2D
3049 if (nodeName === 'canvas') return painter_canvas2D(target, arguments[0]);
3050
3051 // svg
3052 if (nodeName === 'svg') return painter_svg(target, arguments[0]);
3053
3054 throw new Error('Painter is not a function!');
3055 }
3056
3057 function layer() {
3058
3059 if (!isElement(this[0])) throw new Error('Target empty!');
3060
3061 if (this[0].nodeName.toLowerCase() !== 'canvas') throw new Error('Layer is not a function!');
3062
3063 // 画笔
3064 var painter = this.painter(),
3065
3066 // 图层集合
3067 layer = {},
3068 layer_index = [];
3069 var width = this[0].clientWidth,
3070 //内容+内边距
3071 height = this[0].clientHeight;
3072
3073 var layerManager = {
3074
3075 // 获取指定图层画笔
3076 "painter": function painter(id) {
3077 if (!layer[id]) {
3078 // 初始化的图层都可见
3079 layer[id] = { "visible": true };
3080
3081 // 后期可以考虑使用离线画布offScreenCanvas提高效率
3082 layer[id].canvas = document.createElement('canvas');
3083 // 设置大小才会避免莫名其妙的错误
3084 layer[id].canvas.setAttribute('width', width);
3085 layer[id].canvas.setAttribute('height', height);
3086
3087 // 标记是图层
3088 layer[id].canvas.__image2D__layer__ = 'yes';
3089
3090 layer[id].painter = image2D(layer[id].canvas).painter();
3091
3092 layer_index.push(id);
3093 }
3094 return layer[id].painter;
3095 },
3096
3097 // 删除图层
3098 "delete": function _delete(id) {
3099 // 删除索引
3100 for (var i = 0; i < layer_index.length; i++) {
3101 if (layer_index[i] === id) {
3102 layer_index.splice(i, 1);
3103 break;
3104 }
3105 } // 删除图层
3106 delete layer[id];
3107 return layerManager;
3108 },
3109
3110 // 更新内容到画布
3111 "update": function update() {
3112 painter.clearRect(0, 0, width, height);
3113 painter.save();
3114
3115 for (var i = 0; i < layer_index.length; i++) {
3116 if (layer[layer_index[i]].visible) painter.drawImage(layer[layer_index[i]].canvas, 0, 0, width, height, 0, 0, width, height);
3117 }
3118 painter.restore();
3119 return layerManager;
3120 },
3121
3122 // 隐藏图层
3123 "hidden": function hidden(id) {
3124 layer[id].visible = false;
3125 return layerManager;
3126 },
3127
3128 // 显示图层
3129 "show": function show(id) {
3130 layer[id].visible = true;
3131 return layerManager;
3132 }
3133 };
3134
3135 return layerManager;
3136 }
3137
3138 image2D.extend({
3139
3140 // 布局
3141 treeLayout: treeLayout$1, pieLayout: pieLayout,
3142
3143 // 矩阵变换
3144 Matrix4: Matrix4,
3145
3146 // 二维简单变换
3147 rotate: _rotate2, move: _move2, scale: _scale2, dot: dot,
3148
3149 // 动画类
3150 animation: animation$1,
3151
3152 // 插值类计算
3153 cardinal: cardinal,
3154
3155 // 色彩类
3156 formatColor: formatColor, getRandomColors: getRandomColors, getLoopColors: getLoopColors,
3157
3158 // 事件相关
3159 stopPropagation: stopPropagation, preventDefault: preventDefault,
3160
3161 // 地图映射
3162 map: map,
3163
3164 // 刻度尺辅助计算
3165 ruler: ruler
3166
3167 });
3168 image2D.prototype.extend({
3169
3170 // 结点操作
3171 appendTo: appendTo, prependTo: prependTo, afterTo: afterTo, beforeTo: beforeTo, remove: remove, filter: filter, text: text, html: html, size: size,
3172
3173 // 结点属性或样式操作
3174 css: style, attr: attribute,
3175
3176 // 结点和数据绑定
3177 datum: datum, data: data, enter: enter, exit: exit, loop: loop,
3178
3179 // 结点事件
3180 bind: bind, unbind: unbind, position: position,
3181
3182 // 自定义画笔
3183 painter: painter,
3184
3185 // 图层
3186 layer: layer
3187
3188 });
3189
3190 image2D.fn = image2D.prototype;
3191
3192 // 添加版本信息,方便调试
3193 image2D.version = '1.11.0';
3194
3195 // 判断当前环境,如果不是浏览器环境
3196 if ((typeof module === "undefined" ? "undefined" : _typeof(module)) === "object" && _typeof(module.exports) === "object") {
3197 module.exports = image2D;
3198 }
3199 // 浏览器环境下
3200 // 因为浏览器下挂载到window对象上
3201 // 为了防止覆盖,额外提供一个noConflict方法,用以在覆盖的时候恢复
3202 else {
3203 var
3204 // 保存之前的image2D,防止直接覆盖
3205 _image2D = window.image2D,
3206
3207
3208 // 保存之前的$$,防止直接覆盖
3209 _$$ = window.$$;
3210
3211 image2D.noConflict = function (deep) {
3212
3213 // 如果当前的$$是被最新的image2D覆盖的
3214 // 恢复之前的
3215 if (window.$$ === image2D) {
3216 window.$$ = _$$;
3217 }
3218
3219 // 如果当前的image2D是被最新的image2D覆盖的
3220 // 且标记需要恢复
3221 // 恢复之前的
3222 if (deep && window.image2D === image2D) {
3223 window.image2D = _image2D;
3224 }
3225
3226 // 返回当前image2D
3227 // 因为调用这个方法以后
3228 // 全局window下的image2D和$$是什么
3229 // 已经不一定了
3230 return image2D;
3231 };
3232 // 挂载库对象到根
3233 window.image2D = window.$$ = image2D;
3234 }
3235})();
3236
3237/*!
3238
3239 我还惊讶地意识到, 在我生命中有很多时刻, 每当我遇到一个遥不可及、令人害怕的情境,
3240 并感到惊慌失措时, 我都能够应付——因为我回想起了很久以前自己上过的那一课。
3241 我提醒自己不要看下面遥远的岩石, 而是注意相对轻松、容易的第一小步, 迈出一小步、再一小步,
3242 就这样体会每一步带来的成就感, 直到完成了自己想要完成的, 达到了自己的目标,
3243 然后再回头看时, 不禁对自己走过的这段漫漫长路感到惊讶和自豪。
3244
3245 ———— 摘自 莫顿·亨特《走一步,再走一步》
3246
3247*/
\No newline at end of file