@skrillex1224/playwright-toolkit 2.1.58 → 2.1.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -18
- package/dist/index.cjs +47 -44
- package/dist/index.cjs.map +2 -2
- package/dist/index.js +47 -44
- package/dist/index.js.map +2 -2
- package/index.d.ts +0 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -582,6 +582,17 @@ var Humanize = {
|
|
|
582
582
|
throw error;
|
|
583
583
|
}
|
|
584
584
|
},
|
|
585
|
+
/**
|
|
586
|
+
* 渐进式滚动到元素可见(仅处理 Y 轴滚动)
|
|
587
|
+
* 返回 restore 方法,用于将滚动容器恢复到原位置
|
|
588
|
+
*
|
|
589
|
+
* @param {import('playwright').Page} page
|
|
590
|
+
* @param {string|import('playwright').ElementHandle} target - CSS 选择器或元素句柄
|
|
591
|
+
* @param {Object} [options]
|
|
592
|
+
* @param {number} [options.maxSteps=25] - 最大滚动步数
|
|
593
|
+
* @param {number} [options.minStep=80] - 单次滚动最小步长
|
|
594
|
+
* @param {number} [options.maxStep=220] - 单次滚动最大步长
|
|
595
|
+
*/
|
|
585
596
|
/**
|
|
586
597
|
* 渐进式滚动到元素可见(仅处理 Y 轴滚动)
|
|
587
598
|
* 返回 restore 方法,用于将滚动容器恢复到原位置
|
|
@@ -596,7 +607,7 @@ var Humanize = {
|
|
|
596
607
|
async humanScroll(page, target, options = {}) {
|
|
597
608
|
const { maxSteps = 30, minStep = 150, maxStep = 400 } = options;
|
|
598
609
|
const targetDesc = typeof target === "string" ? target : "ElementHandle";
|
|
599
|
-
logger5.
|
|
610
|
+
logger5.info(`humanScroll | \u5F00\u59CB\u6EDA\u52A8\u76EE\u6807: ${targetDesc}`);
|
|
600
611
|
let element;
|
|
601
612
|
if (typeof target === "string") {
|
|
602
613
|
element = await page.$(target);
|
|
@@ -609,23 +620,27 @@ var Humanize = {
|
|
|
609
620
|
}
|
|
610
621
|
const cursor = $GetCursor(page);
|
|
611
622
|
let didScroll = false;
|
|
623
|
+
let lastRect = null;
|
|
624
|
+
let samePositionCount = 0;
|
|
612
625
|
const checkVisibility = async () => {
|
|
613
626
|
return await element.evaluate((el) => {
|
|
614
627
|
const rect = el.getBoundingClientRect();
|
|
615
628
|
if (!rect || rect.width === 0 || rect.height === 0) {
|
|
616
629
|
return { code: "ZERO_DIMENSIONS", reason: "\u5C3A\u5BF8\u4E3A\u96F6" };
|
|
617
630
|
}
|
|
618
|
-
const cx = rect.left + rect.width / 2;
|
|
619
|
-
const cy = rect.top + rect.height / 2;
|
|
620
631
|
const viewH = window.innerHeight;
|
|
621
632
|
const viewW = window.innerWidth;
|
|
633
|
+
const resultBase = { rect, viewH, viewW };
|
|
634
|
+
const cx = rect.left + rect.width / 2;
|
|
635
|
+
const cy = rect.top + rect.height / 2;
|
|
622
636
|
if (cy < 0 || cy > viewH || cx < 0 || cx > viewW) {
|
|
623
637
|
const direction = cy < 0 ? "up" : cy > viewH ? "down" : "unknown";
|
|
624
|
-
return { code: "OUT_OF_VIEWPORT", reason: "\u4E0D\u5728\u89C6\u53E3\u5185", direction, cy
|
|
638
|
+
return { ...resultBase, code: "OUT_OF_VIEWPORT", reason: "\u4E0D\u5728\u89C6\u53E3\u5185", direction, cy };
|
|
625
639
|
}
|
|
626
640
|
const pointElement = document.elementFromPoint(cx, cy);
|
|
627
641
|
if (pointElement && !el.contains(pointElement) && !pointElement.contains(el)) {
|
|
628
642
|
return {
|
|
643
|
+
...resultBase,
|
|
629
644
|
code: "OBSTRUCTED",
|
|
630
645
|
reason: "\u88AB\u906E\u6321",
|
|
631
646
|
obstruction: {
|
|
@@ -633,24 +648,39 @@ var Humanize = {
|
|
|
633
648
|
id: pointElement.id,
|
|
634
649
|
className: pointElement.className
|
|
635
650
|
},
|
|
636
|
-
cy
|
|
637
|
-
// Return Center Y for smart direction calculation
|
|
638
|
-
viewH
|
|
651
|
+
cy
|
|
639
652
|
};
|
|
640
653
|
}
|
|
641
|
-
return { code: "VISIBLE" };
|
|
654
|
+
return { ...resultBase, code: "VISIBLE" };
|
|
642
655
|
});
|
|
643
656
|
};
|
|
644
657
|
try {
|
|
645
658
|
for (let i = 0; i < maxSteps; i++) {
|
|
646
659
|
const status = await checkVisibility();
|
|
647
660
|
if (status.code === "VISIBLE") {
|
|
648
|
-
logger5.
|
|
661
|
+
logger5.info("humanScroll | \u5143\u7D20\u5DF2\u53EF\u89C1\u4E14\u65E0\u906E\u6321");
|
|
649
662
|
return { element, didScroll };
|
|
650
663
|
}
|
|
651
|
-
|
|
664
|
+
const directionStr = status.direction ? `(${status.direction})` : "";
|
|
665
|
+
logger5.info(`humanScroll | \u6B65\u9AA4 ${i + 1}/${maxSteps}: ${status.reason} ${directionStr}`);
|
|
666
|
+
const currentRect = status.rect;
|
|
667
|
+
if (i > 0 && lastRect && currentRect) {
|
|
668
|
+
const dy = Math.abs(currentRect.top - lastRect.top);
|
|
669
|
+
const dx = Math.abs(currentRect.left - lastRect.left);
|
|
670
|
+
if (dy < 2 && dx < 2) {
|
|
671
|
+
samePositionCount++;
|
|
672
|
+
logger5.info(`humanScroll | \u26A0\uFE0F \u8B66\u544A: \u6EDA\u52A8\u540E\u4F4D\u7F6E\u672A\u53D8 (${samePositionCount}/3). dy=${dy}`);
|
|
673
|
+
if (samePositionCount >= 3) {
|
|
674
|
+
logger5.warn("humanScroll | \u26A0\uFE0F \u68C0\u6D4B\u5230\u65E0\u6548\u6EDA\u52A8 (\u5143\u7D20\u53EF\u80FD\u662F Fixed \u6216\u4F4D\u4E8E\u4E0D\u53EF\u6EDA\u52A8\u7684\u5BB9\u5668)\uFF0C\u5F3A\u5236\u505C\u6B62\uFF0C\u5C1D\u8BD5\u76F4\u63A5\u4EA4\u4E92");
|
|
675
|
+
return { element, didScroll };
|
|
676
|
+
}
|
|
677
|
+
} else {
|
|
678
|
+
samePositionCount = 0;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
if (currentRect) lastRect = currentRect;
|
|
652
682
|
if (status.code === "OBSTRUCTED" && status.obstruction) {
|
|
653
|
-
logger5.
|
|
683
|
+
logger5.info(`humanScroll | \u88AB\u906E\u6321: <${status.obstruction.tag}.${status.obstruction.className}>`);
|
|
654
684
|
}
|
|
655
685
|
let deltaY = 0;
|
|
656
686
|
if (status.code === "OUT_OF_VIEWPORT") {
|
|
@@ -663,22 +693,23 @@ var Humanize = {
|
|
|
663
693
|
}
|
|
664
694
|
} else if (status.code === "OBSTRUCTED") {
|
|
665
695
|
const isBottomHalf = status.cy > status.viewH / 2;
|
|
666
|
-
const
|
|
667
|
-
deltaY =
|
|
696
|
+
const dir = isBottomHalf ? 1 : -1;
|
|
697
|
+
deltaY = dir * (minStep + Math.random() * 50);
|
|
668
698
|
}
|
|
669
699
|
if (i === 0 || Math.random() < 0.2) {
|
|
670
700
|
const viewSize = page.viewportSize();
|
|
671
701
|
if (viewSize) {
|
|
672
702
|
const safeX = viewSize.width * 0.5 + (Math.random() - 0.5) * 100;
|
|
673
703
|
const safeY = viewSize.height * 0.5 + (Math.random() - 0.5) * 100;
|
|
674
|
-
await cursor.actions.move({ x: safeX, y: safeY })
|
|
704
|
+
await cursor.actions.move({ x: safeX, y: safeY }).catch(() => {
|
|
705
|
+
});
|
|
675
706
|
}
|
|
676
707
|
}
|
|
677
708
|
await page.mouse.wheel(0, deltaY);
|
|
678
709
|
didScroll = true;
|
|
679
|
-
await delay2(this.jitterMs(
|
|
710
|
+
await delay2(this.jitterMs(150 + Math.random() * 100, 0.2));
|
|
680
711
|
}
|
|
681
|
-
logger5.warn(`humanScroll | \
|
|
712
|
+
logger5.warn(`humanScroll | \u26A0\uFE0F \u8FBE\u5230\u6700\u5927\u6B65\u6570 (${maxSteps}) \u4ECD\u65E0\u6CD5\u5B8C\u5168\u53EF\u89C1\uFF0C\u653E\u5F03\u6EDA\u52A8`);
|
|
682
713
|
return { element, didScroll };
|
|
683
714
|
} catch (error) {
|
|
684
715
|
logger5.fail("humanScroll", error);
|
|
@@ -890,34 +921,6 @@ var Humanize = {
|
|
|
890
921
|
logger5.fail("warmUpBrowsing", error);
|
|
891
922
|
throw error;
|
|
892
923
|
}
|
|
893
|
-
},
|
|
894
|
-
/**
|
|
895
|
-
* 自然滚动 - 带惯性、减速效果和随机抖动
|
|
896
|
-
* @param {import('playwright').Page} page
|
|
897
|
-
* @param {'up' | 'down'} [direction='down'] - 滚动方向
|
|
898
|
-
* @param {number} [distance=300] - 总滚动距离基础值 (px),±15% 抖动
|
|
899
|
-
* @param {number} [baseSteps=5] - 分几步完成基础值,±1 随机
|
|
900
|
-
*/
|
|
901
|
-
async naturalScroll(page, direction = "down", distance = 300, baseSteps = 5) {
|
|
902
|
-
const steps = Math.max(3, baseSteps + Math.floor(Math.random() * 3) - 1);
|
|
903
|
-
const actualDistance = this.jitterMs(distance, 0.15);
|
|
904
|
-
logger5.start("naturalScroll", `dir=${direction}, dist=${actualDistance}, steps=${steps}`);
|
|
905
|
-
const sign = direction === "down" ? 1 : -1;
|
|
906
|
-
const stepDistance = actualDistance / steps;
|
|
907
|
-
try {
|
|
908
|
-
for (let i = 0; i < steps; i++) {
|
|
909
|
-
const factor = 1 - i / steps * 0.5;
|
|
910
|
-
const jitter = 0.9 + Math.random() * 0.2;
|
|
911
|
-
const scrollAmount = stepDistance * factor * sign * jitter;
|
|
912
|
-
await page.mouse.wheel(0, scrollAmount);
|
|
913
|
-
const baseDelay = 60 + i * 25;
|
|
914
|
-
await delay2(this.jitterMs(baseDelay, 0.3));
|
|
915
|
-
}
|
|
916
|
-
logger5.success("naturalScroll");
|
|
917
|
-
} catch (error) {
|
|
918
|
-
logger5.fail("naturalScroll", error);
|
|
919
|
-
throw error;
|
|
920
|
-
}
|
|
921
924
|
}
|
|
922
925
|
};
|
|
923
926
|
|