vben
2021-08-24 56a966cfbf8db5b29a42185f0f25a0e800c30dbb
提交 | 用户 | age
a1d956 1 import type { FunctionArgs } from '@vueuse/core';
2f6253 2 import { upperFirst } from 'lodash-es';
3
4 export interface ViewportOffsetResult {
5   left: number;
6   top: number;
7   right: number;
8   bottom: number;
9   rightIncludeBody: number;
10   bottomIncludeBody: number;
11 }
12
13 export function getBoundingClientRect(element: Element): DOMRect | number {
14   if (!element || !element.getBoundingClientRect) {
15     return 0;
16   }
17   return element.getBoundingClientRect();
18 }
bdce84 19
8a9ca4 20 function trim(string: string) {
2f6253 21   return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '');
8a9ca4 22 }
bdce84 23
2f6253 24 /* istanbul ignore next */
25 export function hasClass(el: Element, cls: string) {
26   if (!el || !cls) return false;
27   if (cls.indexOf(' ') !== -1) throw new Error('className should not contain space.');
28   if (el.classList) {
29     return el.classList.contains(cls);
30   } else {
31     return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1;
32   }
33 }
bdce84 34
2f6253 35 /* istanbul ignore next */
36 export function addClass(el: Element, cls: string) {
37   if (!el) return;
38   let curClass = el.className;
39   const classes = (cls || '').split(' ');
40
41   for (let i = 0, j = classes.length; i < j; i++) {
42     const clsName = classes[i];
43     if (!clsName) continue;
44
45     if (el.classList) {
46       el.classList.add(clsName);
47     } else if (!hasClass(el, clsName)) {
48       curClass += ' ' + clsName;
49     }
50   }
51   if (!el.classList) {
52     el.className = curClass;
53   }
54 }
55
56 /* istanbul ignore next */
57 export function removeClass(el: Element, cls: string) {
58   if (!el || !cls) return;
59   const classes = cls.split(' ');
60   let curClass = ' ' + el.className + ' ';
61
62   for (let i = 0, j = classes.length; i < j; i++) {
63     const clsName = classes[i];
64     if (!clsName) continue;
65
66     if (el.classList) {
67       el.classList.remove(clsName);
68     } else if (hasClass(el, clsName)) {
69       curClass = curClass.replace(' ' + clsName + ' ', ' ');
70     }
71   }
72   if (!el.classList) {
73     el.className = trim(curClass);
74   }
75 }
76 /**
be3a3e 77  * Get the left and top offset of the current element
V 78  * left: the distance between the leftmost element and the left side of the document
79  * top: the distance from the top of the element to the top of the document
80  * right: the distance from the far right of the element to the right of the document
81  * bottom: the distance from the bottom of the element to the bottom of the document
82  * rightIncludeBody: the distance between the leftmost element and the right side of the document
83  * bottomIncludeBody: the distance from the bottom of the element to the bottom of the document
2f6253 84  *
85  * @description:
86  */
87 export function getViewportOffset(element: Element): ViewportOffsetResult {
88   const doc = document.documentElement;
89
90   const docScrollLeft = doc.scrollLeft;
91   const docScrollTop = doc.scrollTop;
92   const docClientLeft = doc.clientLeft;
93   const docClientTop = doc.clientTop;
94
95   const pageXOffset = window.pageXOffset;
96   const pageYOffset = window.pageYOffset;
97
98   const box = getBoundingClientRect(element);
99
100   const { left: retLeft, top: rectTop, width: rectWidth, height: rectHeight } = box as DOMRect;
101
102   const scrollLeft = (pageXOffset || docScrollLeft) - (docClientLeft || 0);
103   const scrollTop = (pageYOffset || docScrollTop) - (docClientTop || 0);
104   const offsetLeft = retLeft + pageXOffset;
105   const offsetTop = rectTop + pageYOffset;
106
107   const left = offsetLeft - scrollLeft;
108   const top = offsetTop - scrollTop;
109
110   const clientWidth = window.document.documentElement.clientWidth;
111   const clientHeight = window.document.documentElement.clientHeight;
112   return {
113     left: left,
114     top: top,
115     right: clientWidth - rectWidth - left,
116     bottom: clientHeight - rectHeight - top,
117     rightIncludeBody: clientWidth - left,
118     bottomIncludeBody: clientHeight - top,
119   };
120 }
121
122 export function hackCss(attr: string, value: string) {
123   const prefix: string[] = ['webkit', 'Moz', 'ms', 'OT'];
124
125   const styleObj: any = {};
126   prefix.forEach((item) => {
127     styleObj[`${item}${upperFirst(attr)}`] = value;
128   });
129   return {
130     ...styleObj,
131     [attr]: value,
132   };
133 }
134
135 /* istanbul ignore next */
144ab5 136 export function on(
bdce84 137   element: Element | HTMLElement | Document | Window,
2f6253 138   event: string,
56a966 139   handler: EventListenerOrEventListenerObject,
2f6253 140 ): void {
141   if (element && event && handler) {
142     element.addEventListener(event, handler, false);
143   }
144ab5 144 }
2f6253 145
146 /* istanbul ignore next */
144ab5 147 export function off(
bdce84 148   element: Element | HTMLElement | Document | Window,
2f6253 149   event: string,
56a966 150   handler: Fn,
2f6253 151 ): void {
152   if (element && event && handler) {
153     element.removeEventListener(event, handler, false);
154   }
144ab5 155 }
bdce84 156
V 157 /* istanbul ignore next */
144ab5 158 export function once(el: HTMLElement, event: string, fn: EventListener): void {
bdce84 159   const listener = function (this: any, ...args: unknown[]) {
V 160     if (fn) {
161       fn.apply(this, args);
162     }
163     off(el, event, listener);
164   };
165   on(el, event, listener);
144ab5 166 }
a1d956 167
Y 168 export function useRafThrottle<T extends FunctionArgs>(fn: T): T {
169   let locked = false;
170   // @ts-ignore
171   return function (...args: any[]) {
172     if (locked) return;
173     locked = true;
174     window.requestAnimationFrame(() => {
175       // @ts-ignore
176       fn.apply(this, args);
177       locked = false;
178     });
179   };
180 }