可视化对象元素包括(可视化调试某个js对象的属性UI插件 class HTUI)
导读:依赖的类:...
依赖的类:
1 "use strict";
2 import {
3 UTILS,
4 Box,
5 EventDispatcher,
6 Point,
7 AnimateLoop,
8 TreeStruct,
9 Timer,
10 TweenCache,
11 RGBColor,
12 Line,
13 Polygon,
14 Circle,
15 RoundedRectangle,
16 Meter,
17 SecurityDoor,
18 } from ./Utils.js;
19
20
21 /* Touch 事件
22 touchstart
23 当用户在触摸平面上放置了一个触点时触发 。事件的目标 element 将是触点位置上的那个目标 element
24
25 touchend
26 当一个触点被用户从触摸平面上移除(即用户的一个手指或手写笔离开触摸平面)时触发 。当触点移出触摸平面的边界时也将触发 。例如用户将手指划出屏幕边缘 。
27 事件的目标 element 与触发 touchstart 事件的目标 element 相同 ,即使 touchend 事件触发时 ,触点已经移出了该 element 。
28 已经被从触摸平面上移除的触点,可以在 changedTouches 属性定义的 TouchList 中找到 。
29
30 touchmove
31 当用户在触摸平面上移动触点时触发 。事件的目标 element 和触发 touchstart 事件的目标 element 相同 ,即使当 touchmove 事件触发时 ,触点已经移出了该 element 。
32 当触点的半径 、旋转角度以及压力大小发生变化时 ,也将触发此事件 。
33 注意: 不同浏览器上 touchmove 事件的触发频率并不相同。这个触发频率还和硬件设备的性能有关 。因此决不能让程序的运作依赖于某个特定的触发频率 。
34
35 touchcancel
36 当触点由于某些原因被中断时触发。有几种可能的原因如下(具体的原因根据不同的设备和浏览器有所不同):
37 由于某个事件出现而取消了触摸:例如触摸过程被弹窗打断 。
38 触点离开了文档窗口 ,而进入了浏览器的界面元素 、插件或者其他外部内容区域 。
39 当用户产生的触点个数超过了设备支持的个数 ,从而导致 TouchList 中最早的 Touch 对象被取消。
40
41
42 TouchEvent.changedTouches
43 这个 TouchList 对象列出了和这个触摸事件对应的 Touch 对象 。
44 对于 touchstart 事件 ,这个 TouchList 对象列出在此次事件中新增加的触点 。
45 对于 touchmove 事件 ,列出和上一次事件相比较 ,发生了变化的触点 。
46 对于 touchend 事件 ,changedTouches 是已经从触摸面的离开的触点的集合(也就是说,手指已经离开了屏幕/触摸面) 。
47
48 TouchEvent.targetTouches
49 targetTouches 是一个只读的 TouchList 列表 ,包含仍与触摸面接触的所有触摸点的 Touch 对象 。touchstart (en-US)事件触发在哪个element内 ,它就是当前目标元素 。
50
51 TouchEvent.touches
52 一个 TouchList,其会列出所有当前在与触摸表面接触的 Touch 对象 ,不管触摸点是否已经改变或其目标元素是在处于 touchstart 阶段 。
53
54 1 event.changedTouches 上一次的触点列表
55 1 event.targetTouches 某个元素的当前触点列表
56 1 event.touches 屏幕上所有的当前触点列表
57 5 touches: Array[Object{
58 clientX, clientY
59 pageX, pageY
60 screenX, screenY
61 radiusX, radiusY
62 force
63 identifier
64 rotationAngle
65 target
66 }]
67
68 */
69
70
71 function _roundRect(con, x, y, w, h, r){ //con: context || Path2D
72 const _x = x + r,
73 _y = y + r,
74 mx = x + w,
75 my = y + h,
76 _mx = mx - r,
77 _my = my - r;
78
79 //上
80 con.moveTo(_x, y);
81 con.lineTo(_mx, y);
82 con.arcTo(mx, y, mx, _y, r);
83
84 //右
85 con.lineTo(mx, _y);
86 con.lineTo(mx, _my);
87 con.arcTo(mx, my, _x, my, r);
88
89 //下
90 con.lineTo(_x, my);
91 con.lineTo(_mx, my);
92 con.arcTo(x, my, x, _my, r);
93
94 //左
95 con.lineTo(x, _y);
96 con.lineTo(x, _my);
97 con.arcTo(x, y, _x, y, r);
98 }
99
100
101 const PI2 = Math.PI*2;
102
103 const ElementUtils = {
104
105 getRect(elem){
106 return elem.getBoundingClientRect();
107 },
108
109 downloadFile(blob, fileName){
110 const link = document.createElement("a");
111 link.href = URL.createObjectURL(blob);
112 link.download = fileName;
113 link.click();
114 },
115
116 loadFileJSON(onload){
117 const input = document.createElement("input");
118 input.type = "file";
119 input.accept = ".json";
120
121 input.onchange = a => {
122 if(a.target.files.length === 0) return;
123 const fr = new FileReader();
124 fr.onloadend = b => onload(b.target.result);
125 fr.readAsText(a.target.files[0]); //fr.readAsDataURL(a.target.files[0]);
126 }
127
128 input.click();
129 },
130
131 loadFileImages(onload){
132 const input = document.createElement("input");
133 input.type = "file";
134 input.multiple = "multiple";
135 input.accept = ".png, .PNG, .jpg, .JPG, .jpeg, .JPEG, .bmp, .BMP, .gif, .GIF";
136
137 input.onchange = e => {
138 const files = e.target.files, len = files.length;
139 if(len === 0) return;
140
141 var i = 0;
142 const fr = new FileReader(), result = [];
143 fr.onerror = () => {
144 i++; if(i === len && typeof onload === "function") onload(result);
145 }
146 fr.onloadend = ef => {
147 if(typeof ef.target.result === "string" && ef.target.result.length > 0) result.push(ef.target.result);
148 i++;
149 if(i === len && typeof onload === "function") onload(result);
150 else fr.readAsDataURL(files[i]);
151 }
152
153 fr.readAsDataURL(files[0]);
154 }
155
156 input.click();
157 },
158
159 createCanvas(w = 1, h = 1, className = ""){
160 const canvas = document.createElement("canvas");
161 canvas.width = w;
162 canvas.height = h;
163 canvas.className = className;
164 return canvas;
165 },
166
167 createContext(w = 1, h = 1, alpha = true){
168 const canvas = document.createElement("canvas"),
169 context = canvas.getContext("2d", {alpha: alpha});
170 canvas.width = w;
171 canvas.height = h;
172 return context;
173 },
174
175 //加载图片并把图片缩放至 w, h 大小(如果与w,h大小一样则不缩放直接返回image而不是canvas);
176 //urls: Array[string]; 此参数为引用(只对urls读操作)
177 //constrainScale: 在缩放图像时是否约束其比例; 默认 false
178 createCanvasFromURL(w, h, urls, constrainScale, onload, onchange){
179 var i = 0;
180 const len = urls.length, images = [],
181 done = () => {
182 i++; if(len !== i){
183 if(typeof onchange === "function") onchange(i, len);
184 return;
185 }
186
187 for(let k = 0; k < len; k++){
188 const image = images[k];
189 if(image.width !== w || image.height !== h){
190 const context = this.createContext(w, h, true);
191 if(constrainScale === true){
192 const scale = UTILS.getSameScale(image, {width: w, height: h}),
193 nw = scale * image.width,
194 nh = scale * image.height;
195 context.drawImage(image, (w - nw) / 2, (h - nh) / 2, nw, nh);
196 } else {
197 context.drawImage(image, 0, 0, w, h);
198 }
199 images[k] = context.canvas;
200 }
201 }
202
203 if(typeof onload === "function") onload(images);
204 }
205
206 for(let k = 0; k < len; k++){
207 images[k] = new Image();
208 images[k].onload = done;
209 images[k].src = urls[k];
210 }
211 },
212
213 //创建画布, 并且在此画布上绘制两种颜色交叉的底板色
214 createCanvasTCC(width, height, size, round = 0, c1 = "rgb(255,255,255)", c2 = "rgb(127,127,127)"){
215 const lenX = Math.ceil(width/size), lenY = Math.ceil(height/size),
216 con = this.createContext(width, height, true),
217 l_2 = con.lineWidth / 2, p$1 = new Path2D(), p$2 = new Path2D();
218
219 if(round < 1){
220 con.rect(l_2, l_2, width - con.lineWidth, height - con.lineWidth);
221 } else {
222 _roundRect(con, l_2, l_2, width - con.lineWidth, height - con.lineWidth, round);
223 con.clip();
224 }
225
226 for(let ix = 0, iy = 0; ix < lenX; ix++){
227
228 for(iy = 0; iy < lenY; iy++){
229
230 ((ix + iy) % 2 === 0 ? p$1 : p$2).rect(ix * size, iy * size, size, size);
231
232 }
233
234 }
235
236 con.fillStyle = c1;
237 con.fill(p$1);
238
239 con.fillStyle = c2;
240 con.fill(p$2);
241
242 return con.canvas;
243 },
244
245 createElem(tagName, className = "", textContent = ""){
246 const elem = document.createElement(tagName);
247 elem.className = className;
248 elem.textContent = textContent;
249 return elem;
250 },
251
252 createInput(type, className = ""){
253 const input = document.createElement("input");
254 input.type = type;
255 input.className = className;
256 return input;
257 },
258
259 appendChilds(parentElem, ...nodes){
260 const msgContainer = document.createDocumentFragment();
261 for(let k = 0, len = nodes.length; k < len; k++) msgContainer.appendChild(nodes[k]);
262 parentElem.appendChild(msgContainer);
263 },
264
265 removeChild(elem){
266 if(elem.parentElement) elem.parentElement.removeChild(elem);
267 },
268
269 rotate(elem, a, o = "center"){
270 elem.style.transformOrigin = o;
271 elem.style.transform = `rotate(${a}deg)`;
272 },
273
274 bindButton(elem, callback, ondown = null){
275 var timeout = 0;
276
277 const param = {offsetX: 0, offsetY: 0},
278
279 onUp = event => {
280 elem.removeEventListener(pointerup, onUp);
281 if(Date.now() - timeout < 300) callback(event, param);
282 },
283
284 onDown = event => {
285 timeout = Date.now();
286 param.offsetX = event.offsetX;
287 param.offsetY = event.offsetY;
288 if(ondown !== null) ondown(event, param);
289 elem.removeEventListener(pointerup, onUp);
290 elem.addEventListener(pointerup, onUp);
291 }
292
293 elem.addEventListener(pointerdown, onDown);
294
295 return function (){
296 elem.removeEventListener(pointerup, onUp);
297 elem.removeEventListener(pointerdown, onDown);
298 }
299 },
300
301 bindMobileButton(elem, callback, ondown = null){
302 var timeout = 0, rect;
303
304 const param = {offsetX: 0, offsetY: 0},
305
306 onUp = event => {
307 event.preventDefault();
308 if(Date.now() - timeout < 300) callback(event, param);
309 },
310
311 onDown = event => {
312 event.preventDefault();
313 timeout = Date.now();
314 rect = elem.getBoundingClientRect();
315 param.offsetX = event.targetTouches[0].pageX - rect.x;
316 param.offsetY = event.targetTouches[0].pageY - rect.y;
317 if(ondown !== null) ondown(event, param);
318 }
319
320 elem.addEventListener(touchend, onUp);
321 elem.addEventListener(touchstart, onDown);
322
323 return function (){
324 elem.removeEventListener(touchend, onUp);
325 elem.removeEventListener(touchstart, onDown);
326 }
327 },
328
329 }
330
331
332 function gradientColor(gradient, colors, close = false){
333 if(UTILS.emptyArray(colors)) return;
334 const len = colors.length;
335 if(close === false){
336 for(let k = 0, _len = len - 1; k < len; k++) gradient.addColorStop(k / _len, colors[k]);
337 }else{
338 for(let k = 0; k < len; k++) gradient.addColorStop(k / len, colors[k]);
339 gradient.addColorStop(1, colors[0]);
340 }
341 return gradient;
342 }
343
344 function gradientColorSymme(gradient, colors){
345 if(Array.isArray(colors) === true){
346
347 const len = Math.round(colors.length/2), count = len * 2;
348
349 for(let k = 0; k < len; k++){
350 gradient.addColorStop(k / count, colors[k]);
351 }
352
353 for(let k = len, i = len; k >= 0; k--, i++){
354 gradient.addColorStop(i / count, colors[k]);
355 }
356
357 }
358 return gradient;
359 }
360
361 //解决像素过高导致模糊问题
362 function setCS(c, w, h){
363 if(devicePixelRatio <= 1){
364 c.width = Math.round(w);
365 c.height = Math.round(h);
366 } else {
367 c.style.width = w + px;
368 c.style.height = h + px;
369 c.width = Math.round(w * devicePixelRatio);
370 c.height = Math.round(h * devicePixelRatio);
371 }
372 }
373 function getCX(x){
374 return devicePixelRatio <= 1 ? x : x * devicePixelRatio;
375 }
376
377
378 /* CanvasElementEvent domElement 绑定 移动 或 桌面 down, move, up 事件 (使两端的事件触发逻辑和参数保持一致)
379 parameter: null
380 attributes: null
381 method:
382 initEvent(domElement, list, box): function;
383 initEventMobile(domElement, list, box): function;
384 domElement: HTMLCanvasElement
385 list: Array[CanvasEventTarget]
386 box: Box
387 function: 删除绑定的事件
388 */
389 class CanvasElementEvent{
390
391 initEvent(domElement, list, box, cis = null){
392 const onups = [];
393
394 const ondown = event => {
395 let i, ci, len = list.length;
396 for(i = 0; i < onups.length; i++) onups[i]();
397 onups.length = 0;
398
399 const sTime = Date.now();
400 //为什么不用event.offsetX, 而是 rect, 用rect是为了鼠标即使移出了目标dom的范围其参数值也是有效的
401 const targets = [], rect = domElement.getBoundingClientRect(),
402 _offsetX = event.pageX - rect.x,
403 _offsetY = event.pageY - rect.y,
404 offsetX = _offsetX + box.x,
405 offsetY = _offsetY + box.y;
406
407 for(i = 0; i < len; i++){
408 ci = list[i];
409 if(ci.visible === false) continue;
410 if(ci.position === ""){
411 if(ci.box.containsPoint(offsetX, offsetY) === false) continue;
412 } else if(ci.position === "fixed"){
413 if(ci.box.containsPoint(_offsetX, _offsetY) === false) continue;
414 }
415
416 if(targets.length === 0) targets[0] = ci;
417 else{
418 if(ci.index === targets[0].index) targets.push(ci);
419 else if(ci.index > targets[0].index){
420 targets.length = 0;
421 targets[0] = ci;
422 }
423 }
424 }
425
426 len = targets.length;
427
428 if(len !== 0){
429 if(cis !== null) cis.disable(this);
430 const info = {targets: targets, target: targets[len-1], offsetX: offsetX, offsetY: offsetY, delta: 0, moveStep: 0},
431
432 onmove = e => {
433 info.moveStep++;
434 info.offsetX = e.pageX - rect.x + box.x,
435 info.offsetY = e.pageY - rect.y + box.y;
436 info.delta = Date.now() - sTime;
437 for(i = 0; i < len; i++){
438 if(targets[i].hasEventListener("move") === true) targets[i].trigger("move", info, e);
439 }
440 },
441
442 onup = e => {
443 domElement.releasePointerCapture(e.pointerId);
444 domElement.removeEventListener("pointerup", onup);
445 domElement.removeEventListener("pointermove", onmove);
446 info.delta = Date.now() - sTime;
447 for(i = 0; i < len; i++){
448 if(targets[i].hasEventListener("up") === true) targets[i].trigger("up", info, e);
449 }
450
451 //click
452 if(info.delta < 300 && info.moveStep === 0){
453 for(i = 0; i < len; i++){
454 if(targets[i].hasEventListener("click") === true) targets[i].trigger("click", info, e);
455 }
456 }
457
458 if(cis !== null) cis.enable(this);
459 }
460
461 onups.push(() => {
462 domElement.removeEventListener("pointerup", onup);
463 domElement.removeEventListener("pointermove", onmove);
464 });
465
466 domElement.setPointerCapture(event.pointerId);
467 domElement.addEventListener("pointerup", onup);
468 domElement.addEventListener("pointermove", onmove);
469 for(i = 0; i < len; i++){
470 if(targets[i].hasEventListener("down") === true) targets[i].trigger("down", info, event);
471 }
472
473 }
474
475 }
476
477 domElement.addEventListener("pointerdown", ondown);
478 return function (){
479 for(let i = 0; i < onups.length; i++) onups[i]();
480 onups.length = 0;
481 domElement.removeEventListener("pointerdown", ondown);
482 if(cis !== null) cis.enable(this);
483 }
484 }
485
486 initEventMobile(domElement, list, box, cis = null){
487
488 const ondown = event => {
489 const sTime = Date.now();
490 event.preventDefault();
491 let i, ci, len = list.length;
492
493 const targets = [], rect = domElement.getBoundingClientRect(),
494 _offsetX = event.targetTouches[0].pageX - rect.x,
495 _offsetY = event.targetTouches[0].pageY - rect.y,
496 offsetX = _offsetX + box.x,
497 offsetY = _offsetY + box.y;
498
499 for(i = 0; i < len; i++){
500 ci = list[i];
501 if(ci.visible === false) continue;
502 if(ci.position === ""){
503 if(ci.box.containsPoint(offsetX, offsetY) === false) continue;
504 } else if(ci.position === "fixed"){
505 if(ci.box.containsPoint(_offsetX, _offsetY) === false) continue;
506 }
507 if(targets.length === 0) targets[0] = ci;
508 else{
509 if(ci.index === targets[0].index) targets.push(ci);
510 else if(ci.index > targets[0].index){
511 targets.length = 0;
512 targets[0] = ci;
513 }
514 }
515 }
516
517 len = targets.length;
518
519 if(len !== 0){
520 if(cis !== null) cis.disable(this);
521 const info = {targets: targets, target: targets[len-1], offsetX: offsetX, offsetY: offsetY, delta: 0, moveStep: 0},
522
523 onmove = e => {
524 info.moveStep++;
525 info.offsetX = e.targetTouches[0].pageX - rect.x + box.x;
526 info.offsetY = e.targetTouches[0].pageY - rect.y + box.y;
527 info.delta = Date.now() - sTime;
528 for(i = 0; i < len; i++){
529 if(targets[i].hasEventListener("move") === true) targets[i].trigger("move", info, e);
530 }
531 },
532
533 onup = e => {
534 domElement.removeEventListener("touchmove", onmove);
535 domElement.removeEventListener("touchend", onup);
536 domElement.removeEventListener("touchcancel", onup);
537 info.delta = Date.now() - sTime;
538 for(i = 0; i < len; i++){
539 if(targets[i].hasEventListener("up") === true) targets[i].trigger("up", info, e);
540 }
541
542 //click
543 if(info.delta < 300 && info.moveStep === 0){
544 for(i = 0; i < len; i++){
545 if(targets[i].hasEventListener("click") === true) targets[i].trigger("click", info, e);
546 }
547 }
548
549 if(cis !== null) cis.enable(this);
550 }
551
552 domElement.addEventListener("touchcancel", onup);
553 domElement.addEventListener("touchend", onup);
554 domElement.addEventListener("touchmove", onmove);
555 for(i = 0; i < len; i++){
556 if(targets[i].hasEventListener("down") === true) targets[i].trigger("down", info, event);
557 }
558 }
559
560 }
561
562 domElement.addEventListener("touchstart", ondown);
563 return function (){
564 domElement.removeEventListener("touchstart", ondown);
565 if(cis !== null) cis.enable(this);
566 }
567
568 }
569
570 }
571
572
573 /* CanvasPath2D CanvasImage.path2D
574 注意: 线模糊问题任然存在(只有.rect().progress()做了模糊优化)
575 parameter:
576 drawType = "stroke", drawStyle = null, order = "after"
577
578 attributes:
579 drawType: String; //填充路径或描绘路径 可能值: 默认 stroke | fill
580 drawStyle: Object; //canvas.context的属性
581 order: Bool; //是否在 CanvasImage 之前绘制 "before"||"after"默认
582 value: any;
583
584 method:
585 reset(): this; //设为零值(清空不在绘制)
586 line(line: Line): this; //线段
587 rect(rect: Box||RoundedRectangle): this; //矩形
588 circle(circle: Circle): this; //圆
589 path(polygon: Polygon): this; //线
590 progress(meter: Meter): this; //进度条(为 CanvasImage 创建默认的进度条, 修改 meter.value 更新进度条视图)
591
592 demo:
593 const test = new CanvasImageCustom().size(100, 100).pos(100, 100).rect().fill("#664466");
594 test.path2D = new CanvasPath2D("stroke", {strokeStyle: "blue", lineWidth: 4});
595
596 const path2D = new Path2D();
597 path2D.roundRect(12, 12, 40, 40, 10); //圆角矩形
598 test.path2D.path(path2D);
599
600 //test.path2D.line(new Line(4, 4, 4, 150000));
601
602 */
603 class CanvasPath2D{
604
605 #pathType = "";
606 get isDraw(){
607 return this.value && this.#pathType !== "";
608 }
609
610 constructor(drawType = "stroke", drawStyle = null, order = "after"){
611 this.order = order;
612 this.drawType = drawType;
613 this.drawStyle = drawStyle;
614 this.value = undefined;
615 }
616
617 reset(){
618 this.#pathType = "";
619 this.value = undefined;
620 return this;
621 }
622
623 line(line = new Line()){
624 this.#pathType = "line";
625 this.value = line;
626 return this;
627 }
628
629 rect(rect = new RoundedRectangle()){
630 this.#pathType = "rect";
631 this.value = rect;
632 return this;
633 }
634
635 circle(circle = new Circle()){
636 this.#pathType = "circle";
637 this.value = circle;
638 return this;
639 }
640
641 path(polygon = new Polygon()){
642 this.#pathType = "path";
643 this.value = polygon;
644 return this;
645 }
646
647 progress(meter = new Meter()){
648 this.#pathType = "progress";
649 this.value = meter;
650 return this;
651 }
652
653 _drawPath(con){
654 if(this.drawStyle === null) con[this.drawType]();
655 else{
656 con.save();
657 for(let n in this.drawStyle){
658 if(this.drawStyle[n] !== con[n]) con[n] = this.drawStyle[n];
659 }
660 con[this.drawType]();
661 con.restore();
662 }
663 }
664
665 _draw(con, x, y, w, h){
666 var lw;
667 con.save();
668 con.beginPath();
669 con.rect(x, y, w, h);
670 con.clip();
671 con.translate(x, y);
672 const val = this.value;
673 switch(this.#pathType){
674 case "line":
675 con.beginPath();
676 con.moveTo(val.x, val.y);
677 con.lineTo(val.x1, val.y1);
678 this._drawPath(con);
679 break;
680
681 case "rect":
682 con.beginPath();
683 lw = this.drawStyle.lineWidth || con.lineWidth;
684 if(lw % 2 === 0){
685 const lw_2 = lw / 2;
686 x = Math.floor(val.x+lw_2);
687 y<%2
创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!