|
1 | 1 | import type { ElementHandle, Page, BoundingBox, CDPSession, Protocol } from 'puppeteer' |
2 | 2 | import debug from 'debug' |
| 3 | +import { type PathOptions, path } from './core' |
3 | 4 | import { |
4 | 5 | type Vector, |
5 | | - type TimedVector, |
6 | | - bezierCurve, |
7 | | - bezierCurveSpeed, |
8 | 6 | direction, |
9 | 7 | magnitude, |
10 | 8 | origin, |
11 | 9 | overshoot, |
12 | 10 | add, |
13 | 11 | clamp, |
14 | | - scale, |
15 | | - extrapolate |
| 12 | + scale |
16 | 13 | } from './math' |
17 | 14 | import { installMouseHelper } from './mouse-helper' |
18 | 15 |
|
19 | 16 | // TODO: remove in next major version, is now wrapped in the GhostCursor class. |
20 | | -export { installMouseHelper } |
| 17 | +export { type PathOptions, path, installMouseHelper } |
21 | 18 |
|
22 | 19 | const log = debug('ghost-cursor') |
23 | 20 |
|
@@ -128,23 +125,6 @@ export interface ClickOptions extends MoveOptions { |
128 | 125 | readonly clickCount?: number |
129 | 126 | } |
130 | 127 |
|
131 | | -export interface PathOptions { |
132 | | - /** |
133 | | - * Override the spread of the generated path. |
134 | | - */ |
135 | | - readonly spreadOverride?: number |
136 | | - /** |
137 | | - * Speed of mouse movement. |
138 | | - * Default is random. |
139 | | - */ |
140 | | - readonly moveSpeed?: number |
141 | | - |
142 | | - /** |
143 | | - * Generate timestamps for each point in the path. |
144 | | - */ |
145 | | - readonly useTimestamps?: boolean |
146 | | -} |
147 | | - |
148 | 128 | export interface RandomMoveOptions extends Pick<MoveOptions, 'moveDelay' | 'randomizeMoveDelay' | 'moveSpeed'> { |
149 | 129 | /** |
150 | 130 | * @default 2000 |
@@ -205,18 +185,6 @@ const delay = async (ms: number): Promise<void> => { |
205 | 185 | return await new Promise((resolve) => setTimeout(resolve, ms)) |
206 | 186 | } |
207 | 187 |
|
208 | | -/** |
209 | | - * Calculate the amount of time needed to move from (x1, y1) to (x2, y2) |
210 | | - * given the width of the element being clicked on |
211 | | - * https://en.wikipedia.org/wiki/Fitts%27s_law |
212 | | - */ |
213 | | -const fitts = (distance: number, width: number): number => { |
214 | | - const a = 0 |
215 | | - const b = 2 |
216 | | - const id = Math.log2(distance / width + 1) |
217 | | - return a + b * id |
218 | | -} |
219 | | - |
220 | 188 | /** Get a random point on a box */ |
221 | 189 | const getRandomBoxPoint = ( |
222 | 190 | { x, y, width, height }: BoundingBox, |
@@ -310,81 +278,6 @@ export const getElementBox = async ( |
310 | 278 | } |
311 | 279 | } |
312 | 280 |
|
313 | | -/** Generates a set of points for mouse movement between two coordinates. */ |
314 | | -export function path ( |
315 | | - start: Vector, |
316 | | - end: Vector | BoundingBox, |
317 | | - /** |
318 | | - * Additional options for generating the path. |
319 | | - * Can also be a number which will set `spreadOverride`. |
320 | | - */ |
321 | | - // TODO: remove number arg in next major version change, fine to just allow `spreadOverride` in object. |
322 | | - options?: number | PathOptions): Vector[] | TimedVector[] { |
323 | | - const optionsResolved: PathOptions = typeof options === 'number' |
324 | | - ? { spreadOverride: options } |
325 | | - : { ...options } |
326 | | - |
327 | | - const DEFAULT_WIDTH = 100 |
328 | | - const MIN_STEPS = 25 |
329 | | - const width = 'width' in end && end.width !== 0 ? end.width : DEFAULT_WIDTH |
330 | | - const curve = bezierCurve(start, end, optionsResolved.spreadOverride) |
331 | | - const length = curve.length() * 0.8 |
332 | | - |
333 | | - const speed = optionsResolved.moveSpeed !== undefined && optionsResolved.moveSpeed > 0 |
334 | | - ? (25 / optionsResolved.moveSpeed) |
335 | | - : Math.random() |
336 | | - const baseTime = speed * MIN_STEPS |
337 | | - const steps = Math.ceil((Math.log2(fitts(length, width) + 1) + baseTime) * 3) |
338 | | - const re = curve.getLUT(steps) |
339 | | - return clampPositive(re, optionsResolved) |
340 | | -} |
341 | | - |
342 | | -const clampPositive = (vectors: Vector[], options?: PathOptions): Vector[] | TimedVector[] => { |
343 | | - const clampedVectors = vectors.map((vector) => ({ |
344 | | - x: Math.max(0, vector.x), |
345 | | - y: Math.max(0, vector.y) |
346 | | - })) |
347 | | - |
348 | | - return options?.useTimestamps === true ? generateTimestamps(clampedVectors, options) : clampedVectors |
349 | | -} |
350 | | - |
351 | | -const generateTimestamps = (vectors: Vector[], options?: PathOptions): TimedVector[] => { |
352 | | - const speed = options?.moveSpeed ?? (Math.random() * 0.5 + 0.5) |
353 | | - const timeToMove = (P0: Vector, P1: Vector, P2: Vector, P3: Vector, samples: number): number => { |
354 | | - let total = 0 |
355 | | - const dt = 1 / samples |
356 | | - |
357 | | - for (let t = 0; t < 1; t += dt) { |
358 | | - const v1 = bezierCurveSpeed(t * dt, P0, P1, P2, P3) |
359 | | - const v2 = bezierCurveSpeed(t, P0, P1, P2, P3) |
360 | | - total += (v1 + v2) * dt / 2 |
361 | | - } |
362 | | - |
363 | | - return Math.round(total / speed) |
364 | | - } |
365 | | - |
366 | | - const timedVectors: TimedVector[] = [] |
367 | | - |
368 | | - for (let i = 0; i < vectors.length; i++) { |
369 | | - if (i === 0) { |
370 | | - timedVectors.push({ ...vectors[i], timestamp: Date.now() }) |
371 | | - } else { |
372 | | - const P0 = vectors[i - 1] |
373 | | - const P1 = vectors[i] |
374 | | - const P2 = i + 1 < vectors.length ? vectors[i + 1] : extrapolate(P0, P1) |
375 | | - const P3 = i + 2 < vectors.length ? vectors[i + 2] : extrapolate(P1, P2) |
376 | | - const time = timeToMove(P0, P1, P2, P3, vectors.length) |
377 | | - |
378 | | - timedVectors.push({ |
379 | | - ...vectors[i], |
380 | | - timestamp: timedVectors[i - 1].timestamp + time |
381 | | - }) |
382 | | - } |
383 | | - } |
384 | | - |
385 | | - return timedVectors |
386 | | -} |
387 | | - |
388 | 281 | const shouldOvershoot = (a: Vector, b: Vector, threshold: number): boolean => |
389 | 282 | magnitude(direction(a, b)) > threshold |
390 | 283 |
|
|
0 commit comments