Skip to content

Commit c12fca4

Browse files
committed
feat: optional Smooth scroll behavior
1 parent e5ffcbf commit c12fca4

File tree

2 files changed

+32
-10
lines changed

2 files changed

+32
-10
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ghost-cursor",
3-
"version": "1.3.0",
3+
"version": "1.4.0",
44
"description": "Move your mouse like a human in puppeteer or generate realistic movements on any 2D plane",
55
"repository": "https://github.com/Xetera/ghost-cursor",
66
"main": "lib/spoof.js",

src/spoof.ts

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@ export interface MoveOptions extends BoxOptions, Pick<PathOptions, 'moveSpeed'>
5252
* @default 500
5353
*/
5454
readonly overshootThreshold?: number
55+
/**
56+
* Scroll behavior when target element is outside the visible window.
57+
* @default undefined (default)
58+
*/
59+
readonly scrollBehavior?: ScrollBehavior
60+
/**
61+
* Time to wait after scrolling (when scrolling occurs due to target element being outside the visible window)
62+
* @default 2000
63+
*/
64+
readonly scrollWait?: number
5565
}
5666

5767
export interface ClickOptions extends MoveOptions {
@@ -66,7 +76,7 @@ export interface ClickOptions extends MoveOptions {
6676
*/
6777
readonly waitForClick?: number
6878
/**
69-
* @default 2000
79+
* @default 200
7080
*/
7181
readonly moveDelay?: number
7282
}
@@ -472,6 +482,7 @@ export const createCursor = (
472482
maxTries: 10,
473483
overshootThreshold: 500,
474484
randomizeMoveDelay: true,
485+
scrollWait: 200,
475486
...defaultOptions?.move,
476487
...options
477488
} satisfies MoveOptions
@@ -516,17 +527,28 @@ export const createCursor = (
516527
// Make sure the object is in view
517528
const objectId = elem.remoteObject().objectId
518529
if (objectId !== undefined) {
519-
try {
520-
await getCDPClient(page).send('DOM.scrollIntoViewIfNeeded', {
521-
objectId
522-
})
523-
} catch (e) {
530+
const scrollElemIntoView = async (): Promise<void> => await elem.evaluate((e, scrollBehavior) => e.scrollIntoView({
531+
block: 'center',
532+
behavior: scrollBehavior
533+
}), optionsResolved.scrollBehavior)
534+
535+
if (optionsResolved.scrollBehavior !== undefined) {
536+
await scrollElemIntoView()
537+
} else {
538+
try {
539+
await getCDPClient(page).send('DOM.scrollIntoViewIfNeeded', {
540+
objectId
541+
})
542+
} catch (e) {
524543
// use regular JS scroll method as a fallback
525-
log('Falling back to JS scroll method', e)
526-
await elem.evaluate((e) => e.scrollIntoView({ block: 'center' }))
527-
await new Promise((resolve) => setTimeout(resolve, 2000)) // Wait a bit until the scroll has finished
544+
log('Falling back to JS scroll method', e)
545+
await scrollElemIntoView()
546+
}
528547
}
548+
549+
await delay(optionsResolved.scrollWait)
529550
}
551+
530552
const box = await boundingBoxWithFallback(page, elem)
531553
const { height, width } = box
532554
const destination = getRandomBoxPoint(box, optionsResolved)

0 commit comments

Comments
 (0)