
本文介绍如何在 WebDriverIO 自动化测试中模拟并可视化鼠标移动轨迹,解决 browser.click() 不触发原生 onclick 事件的问题,并提供基于 JavaScript 动画库(如 Anime.js)的可录制、可维护的鼠标动画方案。
本文介绍如何在 webdriverio 自动化测试中模拟并可视化鼠标移动轨迹,解决 `browser.click()` 不触发原生 `onclick` 事件的问题,并提供基于 javascript 动画库(如 anime.js)的可录制、可维护的鼠标动画方案。
WebDriverIO 的 click() 方法是通过浏览器自动化协议(如 Chrome DevTools Protocol)直接操作 DOM 节点完成点击,不会触发 document.onclick 或 element.addEventListener('click') 等原生 JavaScript 事件监听器。因此,你当前注入的鼠标图像无法响应 document.onclick —— 因为该事件根本未被触发。
要实现“可见的鼠标动画”,需采用主动控制策略:在每次交互前,显式将自定义鼠标图标移动到目标元素坐标,并执行平滑动画。以下是完整、可靠且可复用的实现方案:
✅ 正确做法:结合 browser.getLocation() + anime.js 实现动画
首先,在测试初始化阶段注入鼠标图标与动画库(推荐使用轻量级 Anime.js):
// 在 beforeEach 或具体 test 中执行
await browser.execute(() => {
// 1. 创建并注入鼠标图标(base64 PNG)
const mouse = document.createElement('img');
mouse.id = 'webdriver-mouse';
mouse.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAQAAACGG/bgAAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAAHsYAAB7GAZEt8iwAAAAHdElNRQfgAwgMIwdxU/i7AAABZklEQVQ4y43TsU4UURSH8W+XmYwkS2I09CRKpKGhsvIJjG9giQmliHFZlkUIGnEF7KTiCagpsYHWhoTQaiUUxLixYZb5KAAZZhbunu7O/PKfe+fcA+/pqwb4DuximEqXhT4iI8dMpBWEsWsuGYdpZFttiLSSgTvhZ1W/SvfO1CvYdV1kPghV68a30zzUWZH5pBqEui7dnqlFmLoq0gxC1XfGZdoLal2kea8ahLoqKXNAJQBT2yJzwUTVt0bS6ANqy1gaVCEq/oVTtjji4hQVhhnlYBH4WIJV9vlkXLm+10R8oJb79Jl1j9UdazJRGpkrmNkSF9SOz2T71s7MSIfD2lmmfjGSRz3hK8l4w1P+bah/HJLN0sys2JSMZQB+jKo6KSc8vLlLn5ikzF4268Wg2+pPOWW6ONcpr3PrXy9VfS473M/D7H+TLmrqsXtOGctvxvMv2oVNP+Av0uHbzbxyJaywyUjx8TlnPY2YxqkDdAAAAABJRU5ErkJggg==';
mouse.style.cssText = 'position: absolute; z-index: 999999; pointer-events: none; width: 24px; height: 24px; transform: translate(-50%, -50%);';
document.body.appendChild(mouse);
// 2. 动态加载 Anime.js(CDN 方式,确保异步加载完成)
const script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js';
script.onload = () => window.animeLoaded = true;
document.head.appendChild(script);
});
然后,在每次点击前,获取目标元素位置并执行动画:
async function animateClick(selector, duration = 800) {
// 获取目标元素位置(相对于 viewport)
const el = await $(selector);
const { x, y } = await el.getLocation();
const { width, height } = await el.getSize();
// 计算中心点坐标(适配鼠标图标居中)
const targetX = x + width / 2;
const targetY = y + height / 2;
// 执行动画(需等待 anime.js 加载完成)
await browser.waitUntil(
async () => await browser.execute(() => typeof window.anime !== 'undefined'),
{ timeout: 5000, timeoutMsg: 'Anime.js failed to load' }
);
await browser.execute((x, y, d) => {
anime({
targets: '#webdriver-mouse',
left: `${x}px`,
top: `${y}px`,
duration: d,
easing: 'easeOutQuad'
});
}, targetX, targetY, duration);
// 短暂等待动画完成后再点击(避免点击过早)
await browser.pause(duration * 0.8);
await el.click();
}
// 使用示例
await animateClick('#login-submit');
await browser.pause(1000);
await animateClick('.canvas-element');
⚠️ 注意事项与最佳实践
- 不要依赖 document.onclick:WebDriverIO 的所有交互(click, moveTo, keys)均绕过事件系统,这是设计使然,非 bug。
- browser.moveTo() 仅定位,无动画:它可将真实鼠标指针移至元素,但不产生视觉反馈,且部分 Headless 模式下不可见;仅适用于调试,不适用于录屏演示。
- 确保 z-index 和定位正确:鼠标图标必须使用 position: absolute + z-index: 999999,并添加 transform: translate(-50%, -50%) 实现精准居中对齐。
- 录屏兼容性:若配合 ffmpeg 或 wdio-video-reporter 录制视频,请启用浏览器真实光标(Chrome 启动参数加 --enable-logging --v=1 并禁用 --headless),否则动画仅在 DOM 中可见。
- 清理资源(可选):测试结束时可通过 browser.execute(() => document.getElementById('webdriver-mouse')?.remove()) 移除图标,避免干扰后续用例。
通过该方案,你不仅能生成直观、专业的操作演示视频,还能随 UI 变更(ID/class 更新)仅修改 selector 即可同步更新动画路径,真正实现「代码即脚本、测试即文档」的可持续交付目标。











