1/** 2 * Custom Cursor Module 3 * ES2022 version with performance optimizations 4 */ 5 6;(() => { 7 "use strict" 8 9 /** 10 * çº¿æ§æå¼å½æ° 11 */ 12 const lerp = (a, b, n) => (1 - n) * a + n * b 13 14 /** 15 * è·åå ç´ è®¡ç®æ ·å¼ 16 */ 17 const getComputedStyleValue = (element, property) => { 18 try { 19 return window.getComputedStyle(element)[property] || "" 20 } catch { 21 return "" 22 } 23 } 24 25 /** 26 * æ£æ¥å ç´ æ¯å¦åºè¯¥æ¾ç¤º hover ææ 27 */ 28 const shouldShowHover = (element) => { 29 if (!element || element === document) { 30 return false 31 } 32 33 // æ£æ¥å ç´ æå ¶ç¶å ç´ æ¯å¦æ pointer cursor 34 let current = element 35 while (current && current !== document.body) { 36 const cursor = getComputedStyleValue(current, "cursor") 37 if (cursor === "pointer") { 38 return true 39 } 40 current = current.parentElement 41 } 42 43 return false 44 } 45 46 /** 47 * èªå®ä¹å æ ç±» 48 */ 49 class CustomCursor { 50 constructor() { 51 this.pos = { curr: null, prev: null } 52 this.cursor = null 53 this.styleElement = null 54 this.animationFrameId = null 55 this.handlers = {} 56 57 this.create() 58 this.bindEvents() 59 this.startRender() 60 } 61 62 /** 63 * ç§»å¨å æ å°æå®ä½ç½® 64 */ 65 move(left, top) { 66 if (!this.cursor) return 67 68 this.cursor.style.left = `${left}px` 69 this.cursor.style.top = `${top}px` 70 } 71 72 /** 73 * åå»ºå æ å ç´ åæ ·å¼ 74 */ 75 create() { 76 // åå»ºå æ å ç´ 77 if (!this.cursor) { 78 this.cursor = document.createElement("div") 79 this.cursor.id = "cursor" 80 this.cursor.classList.add("hidden") 81 document.body.appendChild(this.cursor) 82 } 83 84 // å建èªå®ä¹å æ æ ·å¼ 85 if (!this.styleElement) { 86 this.styleElement = document.createElement("style") 87 this.styleElement.innerHTML = ` 88 * { 89 cursor: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8' width='8px' height='8px'><style>circle {fill: white;}</style><circle cx='4' cy='4' r='4' opacity='.5'/></svg>") 4 4, auto !important; 90 } 91 ` 92 document.body.appendChild(this.styleElement) 93 } 94 } 95 96 /** 97 * ç»å®äºä»¶çå¬å¨ï¼ä¿®å¤ï¼Bug #6 - æ§è½ä¼åï¼ä½¿ç¨äºä»¶å§æï¼ 98 */ 99 bindEvents() { 100 // ä¿®å¤ï¼ä¸åéåææDOMå ç´ ï¼èæ¯ä½¿ç¨äºä»¶å§æå¨ææ£æ¥ 101 this.handlers.mouseover = (event) => { 102 if (shouldShowHover(event.target)) { 103 this.cursor?.classList.add("hover") 104 } 105 } 106 107 this.handlers.mouseout = (event) => { 108 if (shouldShowHover(event.target)) { 109 this.cursor?.classList.remove("hover") 110 } 111 } 112 113 this.handlers.mousemove = (event) => { 114 const x = event.clientX - 8 115 const y = event.clientY - 8 116 117 // åå§ä½ç½® 118 if (this.pos.curr === null) { 119 this.move(x, y) 120 } 121 122 this.pos.curr = { x, y } 123 this.cursor?.classList.remove("hidden") 124 } 125 126 this.handlers.mouseenter = () => { 127 this.cursor?.classList.remove("hidden") 128 } 129 130 this.handlers.mouseleave = () => { 131 this.cursor?.classList.add("hidden") 132 } 133 134 this.handlers.mousedown = () => { 135 this.cursor?.classList.add("active") 136 } 137 138 this.handlers.mouseup = () => { 139 this.cursor?.classList.remove("active") 140 } 141 142 // ä½¿ç¨ addEventListener ä»£æ¿ on* 屿§ 143 document.addEventListener("mouseover", this.handlers.mouseover, { 144 passive: true 145 }) 146 document.addEventListener("mouseout", this.handlers.mouseout, { 147 passive: true 148 }) 149 document.addEventListener("mousemove", this.handlers.mousemove, { 150 passive: true 151 }) 152 document.addEventListener("mouseenter", this.handlers.mouseenter, { 153 passive: true 154 }) 155 document.addEventListener("mouseleave", this.handlers.mouseleave, { 156 passive: true 157 }) 158 document.addEventListener("mousedown", this.handlers.mousedown, {
159 passive: true 160 }) 161 document.addEventListener("mouseup", this.handlers.mouseup, { 162 passive: true 163 }) 164 } 165 166 /** 167 * ç§»é¤äºä»¶çå¬å¨ 168 */ 169 unbindEvents() { 170 if (this.handlers.mouseover) { 171 document.removeEventListener("mouseover", this.handlers.mouseover) 172 document.removeEventListener("mouseout", this.handlers.mouseout) 173 document.removeEventListener("mousemove", this.handlers.mousemove) 174 document.removeEventListener("mouseenter", this.handlers.mouseenter) 175 document.removeEventListener("mouseleave", this.handlers.mouseleave) 176 document.removeEventListener("mousedown", this.handlers.mousedown) 177 document.removeEventListener("mouseup", this.handlers.mouseup) 178 } 179 this.handlers = {} 180 } 181 182 /** 183 * å¼å§æ¸²æå¾ªç¯ 184 */ 185 startRender() { 186 const render = () => { 187 if (this.pos.curr && this.pos.prev) { 188 // å¹³æ»ç§»å¨ 189 this.pos.prev.x = lerp(this.pos.prev.x, this.pos.curr.x, 0.15) 190 this.pos.prev.y = lerp(this.pos.prev.y, this.pos.curr.y, 0.15) 191 this.move(this.pos.prev.x, this.pos.prev.y) 192 } else if (this.pos.curr) { 193 this.pos.prev = { ...this.pos.curr } 194 } 195 196 this.animationFrameId = requestAnimationFrame(render) 197 } 198 199 render() 200 } 201 202 /** 203 * åæ¢æ¸²æå¾ªç¯ 204 */ 205 stopRender() { 206 if (this.animationFrameId !== null) { 207 cancelAnimationFrame(this.animationFrameId) 208 this.animationFrameId = null 209 } 210 } 211 212 /** 213 * å·æ°å æ 214 */ 215 refresh() { 216 // ä¸éè¦éæ°éåDOMï¼äºä»¶å§æä¼èªå¨å¤çæ°å ç´ 217 this.cursor?.classList.remove("hover", "active") 218 this.pos = { curr: null, prev: null } 219 } 220 221 /** 222 * éæ¯å æ 223 */ 224 destroy() { 225 this.stopRender() 226 this.unbindEvents() 227 228 if (this.styleElement?.parentNode) { 229 this.styleElement.remove() 230 this.styleElement = null 231 } 232 233 if (this.cursor?.parentNode) { 234 this.cursor.remove() 235 this.cursor = null 236 } 237 238 this.pos = { curr: null, prev: null } 239 } 240 } 241 242 // åå§åèªå®ä¹å æ 243 let cursorInstance = null 244 245 const init = () => { 246 // æ¸ çæ§å®ä¾ 247 if (cursorInstance) { 248 cursorInstance.destroy() 249 } 250 251 // å建æ°å®ä¾ 252 cursorInstance = new CustomCursor() 253 254 // 导åºå°å ¨å±ï¼ä¿æååå ¼å®¹ï¼ 255 window.CURSOR = cursorInstance 256 } 257 258 const cleanup = () => { 259 if (cursorInstance) { 260 cursorInstance.destroy() 261 cursorInstance = null 262 } 263 } 264 265 lifecycle.once(init) 266 lifecycle.onReady(() => { 267 cursorInstance?.refresh() 268 }) 269 lifecycle.onCleanup(cleanup) 270})()
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.