Skip to content

Commit c3c6e0d

Browse files
committed
docs:merge
1 parent 22f5fe1 commit c3c6e0d

File tree

3 files changed

+445
-0
lines changed

3 files changed

+445
-0
lines changed
Lines changed: 365 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,365 @@
1+
export const isObject = (val: unknown): val is Record<any, any> =>
2+
val !== null && typeof val === 'object'
3+
4+
export const isString = (val: unknown): val is string => typeof val === 'string'
5+
6+
export const enum NodeType {
7+
ELEMENT_NODE = 1,
8+
ATTRIBUTE_NODE = 2,
9+
TEXT_NODE = 3,
10+
CDATA_SECTION_NODE = 4,
11+
ENTITY_REFERENCE_NODE = 5,
12+
COMMENT_NODE = 6,
13+
PROCESSING_INSTRUCTION_NODE = 7,
14+
DOCUMENT_NODE = 9
15+
}
16+
17+
export interface DragEventOptions {
18+
drag?: (event: Event) => void
19+
start?: (event: Event) => void
20+
end?: (event: Event) => void
21+
}
22+
23+
export type ScrollElement = Element | Window
24+
25+
export class DOMUtils {
26+
static isWindow(val: unknown): val is Window {
27+
return val === window
28+
}
29+
30+
/**
31+
* 添加事件
32+
*
33+
*
34+
* @param element 如果为null将不会添加事件
35+
* @param event
36+
* @param handler
37+
* @param options
38+
*/
39+
static addEventListener(
40+
element: HTMLElement | Document | Window | null,
41+
event: string,
42+
handler: EventListenerOrEventListenerObject,
43+
options: boolean | AddEventListenerOptions = false
44+
): void {
45+
if (element && event && handler) {
46+
element.addEventListener(event, handler, options)
47+
}
48+
}
49+
50+
/**
51+
* 移除事件
52+
*
53+
* @param element 如果为null将不会移除事件
54+
* @param event
55+
* @param handler
56+
* @param options
57+
*/
58+
static removeEventListener(
59+
element: HTMLElement | Document | Window | null,
60+
event: string,
61+
handler: EventListenerOrEventListenerObject,
62+
options: boolean | EventListenerOptions = false
63+
): void {
64+
if (element && event && handler) {
65+
element.removeEventListener(event, handler, options)
66+
}
67+
}
68+
69+
/**
70+
* 触发拖拽事件
71+
*
72+
* @param element
73+
* @param options
74+
*/
75+
static triggerDragEvent(element: HTMLElement, options: DragEventOptions): void {
76+
let isDragging = false
77+
78+
const moveFn = function (event: Event) {
79+
options.drag?.(event)
80+
}
81+
82+
const upFn = (event: Event) => {
83+
DOMUtils.removeEventListener(document, 'mousemove', moveFn)
84+
DOMUtils.removeEventListener(document, 'mouseup', upFn)
85+
DOMUtils.removeEventListener(document, 'touchmove', moveFn)
86+
DOMUtils.removeEventListener(document, 'touchend', upFn)
87+
document.onselectstart = null
88+
document.ondragstart = null
89+
90+
isDragging = false
91+
options.end?.(event)
92+
}
93+
const downFn = (event: Event) => {
94+
if (isDragging) return
95+
document.onselectstart = () => false
96+
document.ondragstart = () => false
97+
DOMUtils.addEventListener(document, 'mousemove', moveFn)
98+
DOMUtils.addEventListener(document, 'mouseup', upFn)
99+
DOMUtils.addEventListener(document, 'touchmove', moveFn)
100+
DOMUtils.addEventListener(document, 'touchend', upFn)
101+
isDragging = true
102+
103+
options.start?.(event)
104+
}
105+
DOMUtils.addEventListener(element, 'mousedown', downFn)
106+
DOMUtils.addEventListener(element, 'touchstart', downFn)
107+
return
108+
}
109+
110+
static getBoundingClientRect(element: HTMLElement): DOMRect | null {
111+
if (element && isObject(element) && element.nodeType === NodeType.ELEMENT_NODE) {
112+
return element.getBoundingClientRect()
113+
}
114+
115+
return null
116+
}
117+
118+
/**
119+
* 判断是否存在className样式
120+
*
121+
* @param element
122+
* @param className
123+
*/
124+
public static hasClass(element: HTMLElement, className: string): boolean {
125+
if (
126+
element &&
127+
isObject(element) &&
128+
isString(className) &&
129+
element.nodeType === NodeType.ELEMENT_NODE
130+
) {
131+
return element.classList.contains(className.trim())
132+
}
133+
return false
134+
}
135+
136+
/**
137+
* 添加样式
138+
*
139+
* @param element
140+
* @param className
141+
*/
142+
public static addClass(element: HTMLElement, className: string): void {
143+
if (
144+
element &&
145+
isObject(element) &&
146+
isString(className) &&
147+
element.nodeType === NodeType.ELEMENT_NODE
148+
) {
149+
className = className.trim()
150+
if (!DOMUtils.hasClass(element, className)) {
151+
const cl = element.className
152+
element.className = cl ? cl + ' ' + className : className
153+
}
154+
}
155+
}
156+
157+
/**
158+
* 移除样式
159+
*
160+
* @param element
161+
* @param className
162+
*/
163+
public static removeClass(element: HTMLElement, className: string): void {
164+
if (
165+
element &&
166+
isObject(element) &&
167+
isString(className) &&
168+
element.nodeType === NodeType.ELEMENT_NODE &&
169+
typeof element.className === 'string'
170+
) {
171+
className = className.trim()
172+
const classes = element.className.trim().split(' ')
173+
for (let i = classes.length - 1; i >= 0; i--) {
174+
classes[i] = classes[i].trim()
175+
if (!classes[i] || classes[i] === className) {
176+
classes.splice(i, 1)
177+
}
178+
}
179+
element.className = classes.join(' ')
180+
}
181+
}
182+
183+
/**
184+
* 切换样式
185+
*
186+
* @param element
187+
* @param className
188+
* @param force
189+
*/
190+
public static toggleClass(element: HTMLElement, className: string, force?: boolean): void {
191+
if (
192+
element &&
193+
isObject(element) &&
194+
isString(className) &&
195+
element.nodeType === NodeType.ELEMENT_NODE
196+
) {
197+
element.classList.toggle(className, force)
198+
}
199+
}
200+
201+
/**
202+
* 替换样式
203+
*
204+
* @param element
205+
* @param oldClassName
206+
* @param newClassName
207+
*/
208+
public static replaceClass(
209+
element: HTMLElement,
210+
oldClassName: string,
211+
newClassName: string
212+
): void {
213+
if (
214+
element &&
215+
isObject(element) &&
216+
isString(oldClassName) &&
217+
isString(newClassName) &&
218+
element.nodeType === NodeType.ELEMENT_NODE
219+
) {
220+
oldClassName = oldClassName.trim()
221+
newClassName = newClassName.trim()
222+
DOMUtils.removeClass(element, oldClassName)
223+
DOMUtils.addClass(element, newClassName)
224+
}
225+
}
226+
227+
static getScrollTop(el: ScrollElement): number {
228+
const top = 'scrollTop' in el ? el.scrollTop : el.pageYOffset
229+
230+
// iOS scroll bounce cause minus scrollTop
231+
return Math.max(top, 0)
232+
}
233+
234+
static setScrollTop(el: ScrollElement, value: number): void {
235+
if ('scrollTop' in el) {
236+
el.scrollTop = value
237+
} else {
238+
el.scrollTo(el.scrollX, value)
239+
}
240+
}
241+
242+
static getRootScrollTop(): number {
243+
return window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
244+
}
245+
246+
static setRootScrollTop(value: number): void {
247+
DOMUtils.setScrollTop(window, value)
248+
DOMUtils.setScrollTop(document.body, value)
249+
}
250+
251+
static getElementTop(el: ScrollElement, scroller?: HTMLElement): number {
252+
if (DOMUtils.isWindow(el)) {
253+
return 0
254+
}
255+
256+
const scrollTop = scroller ? DOMUtils.getScrollTop(scroller) : DOMUtils.getRootScrollTop()
257+
return el.getBoundingClientRect().top + scrollTop
258+
}
259+
260+
static getVisibleHeight(el: ScrollElement): number {
261+
if (DOMUtils.isWindow(el)) {
262+
return el.innerHeight
263+
}
264+
return el.getBoundingClientRect().height
265+
}
266+
267+
static isHidden(el: HTMLElement): boolean {
268+
if (!el) {
269+
return false
270+
}
271+
272+
const style = window.getComputedStyle(el)
273+
const hidden = style.display === 'none'
274+
275+
// 在以下情况下,offsetParent返回null:
276+
// 1. 元素或其父元素的display属性设置为none.
277+
// 2. 元素的position属性设置为fixed
278+
const parentHidden = el.offsetParent === null && style.position !== 'fixed'
279+
280+
return hidden || parentHidden
281+
}
282+
283+
/**
284+
* 触发事件
285+
*
286+
* @param el
287+
* @param type
288+
*/
289+
static triggerEvent(el: Element, type: string): void {
290+
if ('createEvent' in document) {
291+
// modern browsers, IE9+
292+
const e = document.createEvent('HTMLEvents')
293+
e.initEvent(type, false, true)
294+
el.dispatchEvent(e)
295+
}
296+
}
297+
298+
/**
299+
* 计算相对于中心点的旋转角度
300+
* @param element
301+
* @param event
302+
*/
303+
static calcAngle(element: HTMLElement, event: MouseEvent): number {
304+
const rect = element.getBoundingClientRect()
305+
306+
const originX = rect.left + rect.width / 2
307+
const originY = rect.top + rect.height / 2
308+
309+
//获得中心点和鼠标坐标连线,与y轴正半轴之间的夹角
310+
const x = Math.abs(originX - event.clientX)
311+
const y = Math.abs(originY - event.clientY)
312+
const z = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))
313+
const cos = y / z
314+
const rad = Math.acos(cos) //用反三角函数求弧度
315+
let angle = Math.floor(180 / (Math.PI / rad)) //将弧度转换成角度
316+
317+
if (event.clientX > originX && event.clientY > originY) {
318+
//鼠标在第四象限
319+
angle = 180 - angle
320+
}
321+
322+
if (event.clientX == originX && event.clientY > originY) {
323+
//鼠标在y轴负方向上
324+
angle = 180
325+
}
326+
327+
if (event.clientX > originX && event.clientY == originY) {
328+
//鼠标在x轴正方向上
329+
angle = 90
330+
}
331+
332+
if (event.clientX < originX && event.clientY > originY) {
333+
//鼠标在第三象限
334+
angle = 180 + angle
335+
}
336+
337+
if (event.clientX < originX && event.clientY == originY) {
338+
//鼠标在x轴负方向
339+
angle = 270
340+
}
341+
342+
if (event.clientX < originX && event.clientY < originY) {
343+
//鼠标在第二象限
344+
angle = 360 - angle
345+
}
346+
347+
return angle
348+
}
349+
350+
/**
351+
* querySelector
352+
*
353+
* @param selectors
354+
* @param parentElement
355+
*/
356+
static querySelector<E extends Element = Element>(
357+
selectors: string,
358+
parentElement?: HTMLElement
359+
): E | null {
360+
if (parentElement) {
361+
return parentElement.querySelector(selectors)
362+
}
363+
return document.querySelector(selectors)
364+
}
365+
}

0 commit comments

Comments
 (0)