原生js实现移动端touch事件,解决穿透问题

本文详细介绍了移动端触摸滑动的实现方法,包括如何使用touch事件进行操作,以及如何通过设置meta元标签禁止缩放,使用event.preventDefault()阻止默认滚动行为,以及解决穿透问题的策略。并通过实例展示了触摸滑动效果的实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

四种touch事件

touchstart: //手指放到屏幕上时触发

touchmove: //手指在屏幕上滑动式触发

touchend: //手指离开屏幕时触发

touchcancel: //系统取消touch事件的时候触发
每个触摸事件被触发后,会生成一个event对象,event对象里额外包括以下三个触摸列表

touches: //当前屏幕上所有手指的列表

targetTouches: //当前dom元素上手指的列表,尽量使用这个代替touches

changedTouches: //涉及当前事件的手指的列表,尽量使用这个代替touches

这些列表里的每次触摸由touch对象组成,touch对象里包含着触摸信息,主要属性如下:

clientX / clientY: //触摸点相对浏览器窗口的位置

pageX / pageY: //触摸点相对于页面的位置

screenX / screenY: //触摸点相对于屏幕的位置

identifier: //touch对象的ID

target: //当前的DOM元素

注意

手指在滑动整个屏幕时,会影响浏览器的行为,比如滚动和缩放。所以在调用touch事件时,要注意禁止缩放和滚动。

1.禁止缩放

通过meta元标签来设置。

<meta name="viewport" content="target-densitydpi=320,width=640,user-scalable=no">

2.禁止滚动

preventDefault是阻止默认行为,在touchMove事件中使用可阻止默认行为滚动

event.preventDefault();

3.解决穿透
在touchStart事件后添加touchMove事件的监听,在touchMove事件里添加touchEnd事件的监听,在touchEnd事件中移除touchMove和touchEnd的事件监听,即可解决穿透问题

4.removeEventListener
传入的处理事件函数一定是相同的函数,不能是匿名函数

案例

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" Content="text/html; charset=utf-8;">
<title>移动端触摸滑动</title>
<meta name="author" content="rainna" />
<meta name="keywords" content="rainna's js lib" />
<meta name="description" content="移动端触摸滑动" />
<meta name="viewport" content="target-densitydpi=320,width=640,user-scalable=no">
<style>
*{margin:0;padding:0;}
li{list-style:none;}

.m-slider{width:600px;margin:50px 20px;overflow:hidden;}
.m-slider .cnt{position:relative;left:0;width:3000px;}
.m-slider .cnt li{float:left;width:600px;}
.m-slider .cnt img{display:block;width:100%;height:450px;}
.m-slider .cnt p{margin:20px 0;}
.m-slider .icons{text-align:center;color:#000;}
.m-slider .icons span{margin:0 5px;}
.m-slider .icons .curr{color:red;}
.f-anim{-webkit-transition:left .2s linear;}
</style>
</head>

<body>
<div class="m-slider">
    <ul class="cnt" id="slider">
        <li>
            <img src="http://imglf1.ph.126.net/qKodH3sZoVbPalKFtHS9mw==/6608946691259322175.jpg">
            <p>20140813镜面的世界,终究只是倒影。看得到你的身影,却触摸不到你的未来</p>
        </li>
        <li>
            <img src="http://imglf1.ph.126.net/40-jqH_j6EoCWnZOixY2pA==/4798022453110310215.jpg">
            <p>20140812锡林浩特前往东乌旗S101必经之处,一条极美的铁路。铁路下面是个小型的盐沼,淡淡的有了一丝天空之境的感觉。可惜在此玩了一个小时也没有看见一列火车经过,只好继续赶往东乌旗。</p>
        </li>
        <li>
            <img src="http://imglf0.ph.126.net/Jnmi2y51zVdjKAYlibtpFw==/3068640196117481166.jpg">
            <p>20140811水的颜色为什么那么蓝,我也纳闷,反正自然饱和度和对比度拉完就是这个颜色的</p>
        </li>
        <li>
            <img src="http://imglf1.ph.126.net/79GPsjhwiIj8e-0nP5MsEQ==/6619295294699949331.jpg">
            <p>海洋星球3重庆天气热得我想卧轨自杀</p>
        </li>
        <li>
            <img src="http://imglf1.ph.126.net/40-jqH_j6EoCWnZOixY2pA==/4798022453110310215.jpg">
            <p>以上这些作品分别来自两位设计师作为观者,您能否通过设计风格进行区分</p>
        </li>
    </ul>
    <div class="icons" id="icons">
        <span class="curr">1</span>
        <span>2</span>
        <span>3</span>
        <span>4</span>
        <span>5</span>
    </div>
</div>

<script>
var slider = {
    //判断设备是否支持touch事件
    touch:('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch,
    slider:document.getElementById('slider'),

    //事件
    events:{
        index:0,     //显示元素的索引
        slider:this.slider,     //this为slider对象
        icons:document.getElementById('icons'),
        icon:this.icons.getElementsByTagName('span'),
        handleEvent:function(event){
            var self = this;     //this指events对象
            if(event.type == 'touchstart'){
                self.start(event);
            }else if(event.type == 'touchmove'){
                self.move(event);
            }else if(event.type == 'touchend'){
                self.end(event);
            }
        },
        //滑动开始
        start:function(event){
            var touch = event.targetTouches[0];     //touches数组对象获得屏幕上所有的touch,取第一个touch
            startPos = {x:touch.pageX,y:touch.pageY,time:+new Date};    //取第一个touch的坐标值
            isScrolling = 0;   //这个参数判断是垂直滚动还是水平滚动
            this.slider.addEventListener('touchmove',this,false);
            this.slider.addEventListener('touchend',this,false);
        },
        //移动
        move:function(event){
            //当屏幕有多个touch或者页面被缩放过,就不执行move操作
            if(event.targetTouches.length > 1 || event.scale && event.scale !== 1) return;
            var touch = event.targetTouches[0];
            endPos = {x:touch.pageX - startPos.x,y:touch.pageY - startPos.y};
            isScrolling = Math.abs(endPos.x) < Math.abs(endPos.y) ? 1:0;    //isScrolling为1时,表示纵向滑动,0为横向滑动
            if(isScrolling === 0){
                event.preventDefault();      //阻止触摸事件的默认行为,即阻止滚屏
                this.slider.className = 'cnt';
                this.slider.style.left = -this.index*600 + endPos.x + 'px';
            }
        },
        //滑动释放
        end:function(event){
            var duration = +new Date - startPos.time;    //滑动的持续时间
            if(isScrolling === 0){    //当为水平滚动时
                this.icon[this.index].className = '';
                if(Number(duration) > 10){     
                    //判断是左移还是右移,当偏移量大于10时执行
                    if(endPos.x > 10){
                        if(this.index !== 0) this.index -= 1;
                    }else if(endPos.x < -10){
                        if(this.index !== this.icon.length-1) this.index += 1;
                    }
                }
                this.icon[this.index].className = 'curr';
                this.slider.className = 'cnt f-anim';
                this.slider.style.left = -this.index*600 + 'px';
            }
            //解绑事件
            this.slider.removeEventListener('touchmove',this,false);
            this.slider.removeEventListener('touchend',this,false);
        }
    },

    //初始化
    init:function(){
        var self = this;     //this指slider对象
        if(!!self.touch) self.slider.addEventListener('touchstart',self.events,false);    //addEventListener第二个参数可以传一个对象,会调用该对象的handleEvent属性
    }
};

slider.init();
</script>
</body>
</html>
<think>好的,我现在需要帮助用户将之前提供的视频播放器进度条代码适配到移动端。首先,我得回顾一下用户之前提供的代码,看看已经实现了哪些功能,以及用户现在具体需要什么。用户提到已经有一个包含进度条、按钮和时间显示的播放器,并且JavaScript部分已经处理了鼠标事件和部分移动端适配,比如触摸事件的支持。但用户现在希望进一步优化移动端的体验,可能包括更好的触摸响应、UI调整以及性能优化。 首先,我需要考虑移动端与桌面端的区别。移动端主要使用触摸事件,而桌面端是鼠标事件。虽然用户已经添加了touchstarttouchmove和touchend事件,但可能需要进一步优化,比如防止页面滚动在拖动进度条时触发,或者处理触摸点的准确性。 接下来,UI方面,移动端的控件通常需要更大的点击区域,避免误触。比如进度条的高度可能需要增加,拖拽手柄的大小也要调整,方便手指操作。此外,隐藏默认的控制条,避免与自定义控件冲突,这在移动端尤其重要,因为不同浏览器可能有不同的默认样式。 然后,考虑事件处理。在移动端,触摸事件会有多个触点(touches),需要确保只处理第一个触点。同时,在拖动过程中,可能会触发浏览器的默认行为,比如页面滚动,所以需要阻止默认事件,使用e.preventDefault()。 另外,视频的全屏播放在移动端处理方式不同,可能需要添加全屏按钮,并处理相应的全屏API。由于移动端浏览器对全屏API的支持可能有限,可能需要使用不同的方法,比如使用webkitEnterFullscreen等。 性能优化方面,移动端设备可能资源有限,使用requestAnimationFrame来更新进度条可以减少性能消耗。同时,避免频繁的DOM操作,比如在timeupdate事件中使用requestAnimationFrame来节流更新频率。 还需要考虑缓冲显示的优化,移动端网络可能不稳定,缓冲进度提示对用户体验很重要。可能需要调整样式,使其在移动端更明显。 最后,测试常见的移动端问题,比如点击延迟。引入fastclick库或者使用touch-action: manipulation的CSS属性来减少点击延迟,提升响应速度。 总结需要做的步骤: 1. 完善触摸事件处理,确保准确性和防止默认行为。 2. 调整UI元素大小,适应手指操作。 3. 处理全屏播放,添加移动端适用的全屏功能。 4. 优化性能,使用requestAnimationFrame。 5. 防止拖动时的页面滚动。 6. 解决点击延迟问题。 7. 测试在不同移动设备和浏览器上的兼容性。 现在需要将这些思考转化为具体的代码修改和建议,确保用户能够逐步实施这些改动,并理解每个步骤的目的。同时,需要提醒用户注意移动端的特殊情况和可能遇到的兼容性问题,提供相应的解决方案。</think>以下是针对移动端适配的完整优化方案,包含关键修改和新增代码: 1. **触摸事件增强处理** ```javascript // 修改原有的触摸事件处理(防止默认行为和冒泡) $progressBar.on('touchstart', function(e) { e.preventDefault(); e.stopPropagation(); isDragging = true; updateProgress(e.originalEvent.touches[0]); $(document).on('touchmove', function(e) { e.preventDefault(); updateProgress(e.originalEvent.touches[0]); }, { passive: false }); $(document).on('touchend', function(e) { e.preventDefault(); isDragging = false; $(document).off('touchmove'); }); }); // 添加点击穿透保护 document.addEventListener('touchstart', function(e) { if (isDragging) e.preventDefault(); }, { passive: false }); ``` 2. **移动端专属样式优化** ```css /* 移动端触控优化 */ .progress-bar { height: 6px; /* 增加触控区域 */ touch-action: none; /* 禁用浏览器默认触摸行为 */ } .progress-handle { width: 16px; height: 16px; /* 放大拖拽手柄 */ box-shadow: 0 2px 4px rgba(0,0,0,0.2); /* 提升可视性 */ } /* 移动端隐藏原生控件 */ video::-webkit-media-controls { display: none !important; } /* 防止点击时出现蓝色高亮 */ * { -webkit-tap-highlight-color: transparent; } ``` 3. **设备方向适配** ```javascript // 添加屏幕方向监听 window.addEventListener('orientationchange', () => { video.style.width = '100%'; video.style.height = 'auto'; }); ``` 4. **移动端全屏解决方案** ```javascript // 添加全屏按钮 $('.controls').append('<button class="fullscreen">⤢</button>'); // 全屏控制逻辑 $('.fullscreen').on('click touchstart', function() { if (video.requestFullscreen) { video.requestFullscreen(); } else if (video.webkitRequestFullscreen) { /* Safari */ video.webkitRequestFullscreen(); } else if (video.webkitEnterFullscreen) { /* iOS */ video.webkitEnterFullscreen(); } }); ``` 5. **性能优化增强** ```javascript // 节流处理进度更新 let lastUpdate = 0; video.addEventListener('timeupdate', () => { const now = Date.now(); if (now - lastUpdate > 50 || !isDragging) { // 50ms节流 requestAnimationFrame(() => { const progress = (video.currentTime / video.duration) * 100; $progress.css('width', progress + '%'); }); lastUpdate = now; } }); ``` 6. **移动端专属功能扩展** ```javascript // 双击切换播放/暂停 let lastTap = 0; $('.video-container').on('touchstart', function(e) { const currentTime = Date.now(); if (currentTime - lastTap < 300) { // 双击检测 video.paused ? video.play() : video.pause(); } lastTap = currentTime; }); // 滑动调节音量/亮度(示例为亮度调节) let startY = 0; $('.video-container').on('touchstart', function(e) { startY = e.touches[0].clientY; }).on('touchmove', function(e) { const delta = startY - e.touches[0].clientY; const brightness = Math.min(1, Math.max(0, delta / 100)); video.style.filter = `brightness(${1 + brightness})`; }); ``` **移动端适配要点说明**: 1. **触控精准度优化**: - 使用`touch-action: none`禁用浏览器默认行为 - 添加坐标转换校准逻辑 - 引入触摸节流处理 2. **手势交互增强**: - 双击播放/暂停 - 边缘滑动调节音量/亮度 - 长按加速播放(需添加对应逻辑) 3. **渲染性能保障**: ```css /* 开启GPU加速 */ .video-container { transform: translateZ(0); } .progress { will-change: width; } ``` 4. **网络环境适配**: ```javascript // 根据网络状况调整画质 window.addEventListener('online', updateStreamQuality); window.addEventListener('offline', () => { video.src = 'low-quality.mp4'; }); function updateStreamQuality() { const connection = navigator.connection || navigator.mozConnection; if (connection) { const threshold = connection.effectiveType === '4g' ? 'high' : 'low'; video.src = threshold + '-quality.mp4'; } } ``` 5. **移动端专属问题修复**: ```javascript // iOS微信浏览器兼容处理 if (navigator.userAgent.match(/MicroMessenger/i)) { video.play().catch(() => { document.addEventListener('WeixinJSBridgeReady', () => { video.play(); }, false); }); } // Android Chrome自动播放策略 video.setAttribute('muted', ''); video.play().then(() => { video.muted = false; }); ``` **验证方案**: ```javascript // 添加移动端调试面板 const debugInfo = document.createElement('div'); debugInfo.style.position = 'fixed'; debugInfo.style.bottom = '0'; debugInfo.style.background = 'rgba(0,0,0,0.7)'; debugInfo.style.color = 'white'; document.body.appendChild(debugInfo); setInterval(() => { debugInfo.innerHTML = ` 触控状态: ${isDragging ? '拖拽中' : '空闲'}<br> 缓冲进度: ${(video.buffered.end(0) || 0).toFixed(2)}s<br> 网络质量: ${navigator.connection ? navigator.connection.effectiveType : '未知'} `; }, 500); ``` 完整适配后的移动端播放器将具备: - 精准的触控进度调节 - 智能网络适应能力 - 完整的手势操作支持 - 跨平台兼容性保障 - 高性能渲染表现 - 详细的调试信息展示 建议在真机环境下进行多维度测试,可使用Chrome开发者工具的Device Mode模拟不同移动设备,同时注意以下测试重点: - 横竖屏切换时的布局保持 - 低电量模式下的性能表现 - 弱网环境中的缓冲表现 - 多指触控的冲突处理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值