本文实现了一个PC端触摸屏滑动滚动的全局解决方案,通过监听touch和mouse事件实现流畅的滚动效果。主要功能包括:
- 检测可滚动容器;
- 处理单指触摸滑动;
- 支持水平和垂直方向滚动;
- 防止文本选择干扰;
- 兼容鼠标拖动。代码通过状态管理跟踪触摸位置、滚动元素和拖动状态,并优化了事件处理(移除旧监听、阻止默认行为)。当检测到有效滑动(>5px)时,会动态调整滚动位置并禁用文本选择,确保流畅的滚动体验。
- 根据业务需求实现PC端游览器在触摸屏幕设备滑动滚动(全局)
// 全局 PC 端触摸屏滑动滚动实现
export function initTouchScroll() {
// 存储触摸状态
let touchState = {
startX: 0,
startY: 0,
scrollEl: null as HTMLElement | null,
isScrolling: false,
isDragging: false,
startTime: 0
};
// 移除可能存在的旧事件监听,避免重复绑定
document.removeEventListener('touchstart', handleTouchStart);
document.removeEventListener('touchmove', handleTouchMove);
document.removeEventListener('touchend', handleTouchEnd);
document.removeEventListener('touchcancel', handleTouchEnd);
// 添加新的事件监听
document.addEventListener('touchstart', handleTouchStart, { passive: false });
document.addEventListener('touchmove', handleTouchMove, { passive: false });
document.addEventListener('touchend', handleTouchEnd);
document.addEventListener('touchcancel', handleTouchEnd);
// 鼠标拖动也需要处理的话可以添加鼠标事件
document.removeEventListener('mousedown', handleMouseDown);
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
document.addEventListener('mousedown', handleMouseDown);
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
function handleTouchStart(e: TouchEvent) {
if (e.touches.length !== 1) return;
const touch = e.touches[0];
const scrollEl = findScrollContainer(touch.target as HTMLElement);
touchState = {
startX: touch.clientX,
startY: touch.clientY,
scrollEl: scrollEl,
isScrolling: !!scrollEl,
isDragging: false,
startTime: Date.now()
};
// 提前阻止可能的选择行为
if (scrollEl) {
e.preventDefault();
}
}
function handleTouchMove(e: TouchEvent) {
if (!touchState.isScrolling || !touchState.scrollEl) return;
if (e.touches.length !== 1) return;
const touch = e.touches[0];
const scrollEl = touchState.scrollEl;
// 计算移动距离
const deltaX = touchState.startX - touch.clientX;
const deltaY = touchState.startY - touch.clientY;
// 判断是否是有效滑动(超过5px视为滑动)
const moveDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (moveDistance > 5) {
touchState.isDragging = true;
// 禁用文本选择
document.body.style.userSelect = 'none';
document.body.style.webkitUserSelect = 'none';
// 处理纵向滚动
if (scrollEl.scrollHeight > scrollEl.clientHeight) {
scrollEl.scrollTop += deltaY;
scrollEl.scrollTop = Math.max(0, Math.min(scrollEl.scrollTop, scrollEl.scrollHeight - scrollEl.clientHeight));
}
// 处理横向滚动
if (scrollEl.scrollWidth > scrollEl.clientWidth) {
scrollEl.scrollLeft += deltaX;
scrollEl.scrollLeft = Math.max(0, Math.min(scrollEl.scrollLeft, scrollEl.scrollWidth - scrollEl.clientWidth));
}
// 更新起点位置
touchState.startX = touch.clientX;
touchState.startY = touch.clientY;
// 阻止默认行为
e.preventDefault();
e.stopPropagation();
}
}
function handleTouchEnd() {
// 恢复文本选择
document.body.style.userSelect = '';
document.body.style.webkitUserSelect = '';
touchState.isScrolling = false;
touchState.isDragging = false;
touchState.scrollEl = null;
}
// 处理鼠标拖动(可选,解决鼠标拖动选中文本问题)
function handleMouseDown(e: MouseEvent) {
const scrollEl = findScrollContainer(e.target as HTMLElement);
if (!scrollEl) return;
touchState = {
startX: e.clientX,
startY: e.clientY,
scrollEl: scrollEl,
isScrolling: true,
isDragging: false,
startTime: Date.now()
};
}
function handleMouseMove(e: MouseEvent) {
if (!touchState.isScrolling || !touchState.scrollEl) return;
const deltaX = touchState.startX - e.clientX;
const deltaY = touchState.startY - e.clientY;
const moveDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (moveDistance > 5) {
touchState.isDragging = true;
const scrollEl = touchState.scrollEl;
// 禁用文本选择
document.body.style.userSelect = 'none';
// 处理滚动
if (scrollEl.scrollHeight > scrollEl.clientHeight) {
scrollEl.scrollTop += deltaY;
scrollEl.scrollTop = Math.max(0, Math.min(scrollEl.scrollTop, scrollEl.scrollHeight - scrollEl.clientHeight));
}
if (scrollEl.scrollWidth > scrollEl.clientWidth) {
scrollEl.scrollLeft += deltaX;
scrollEl.scrollLeft = Math.max(0, Math.min(scrollEl.scrollLeft, scrollEl.scrollWidth - scrollEl.clientWidth));
}
touchState.startX = e.clientX;
touchState.startY = e.clientY;
e.preventDefault();
e.stopPropagation();
}
}
function handleMouseUp() {
document.body.style.userSelect = '';
touchState.isScrolling = false;
touchState.isDragging = false;
touchState.scrollEl = null;
}
// 查找可滚动容器
function findScrollContainer(el: HTMLElement | null): HTMLElement | null {
while (el && el !== document.body && el.nodeType === 1) {
const style = window.getComputedStyle(el);
const overflowY = style.overflowY;
const overflowX = style.overflowX;
const canScrollY = (overflowY === 'auto' || overflowY === 'scroll') &&
el.scrollHeight > el.clientHeight;
const canScrollX = (overflowX === 'auto' || overflowX === 'scroll') &&
el.scrollWidth > el.clientWidth;
if (canScrollY || canScrollX) {
return el;
}
el = el.parentElement;
}
// 检查body是否可滚动
if (document.body.scrollHeight > window.innerHeight) {
return document.body;
}
return null;
}
// 调试信息,确认函数已执行
console.log('Touch scroll initialized successfully');
}
main.ts引入此函数即可,无需更改,粘贴可用
750

被折叠的 条评论
为什么被折叠?



