本文主要利用touch事件,实现了在触屏上的触摸滚动页面功能,类似手机浏览器上原生的触屏滚动事件。
目前在PC和移动端的浏览器环境中,在有原生的鼠标滚轮滚动和触屏滚动功能的情况下,这个功能的使用场景应该是极其有限的。
写这个小功能的起因是:上周临时接手一个集成了chromium内核的客户端CS项目,完成demo后部署到surface上,鼠标滚轮滚动页面效果正常,但触屏不能滚动页面,于是手写一组touch事件做测试。
本文包括下边三部分:
touch
事件- 各种
x
,y
功能实现
1.
touch
事件touch
事件组(我取的名字),实际上是Touch
对象的Event
属性,包括了touchstart
,touchmove
,touchend
和touchcancel
四个事件:touchstart
事件:手指(或触摸笔一类的设备)与触屏设备表面接触时触发;touchend
事件:手指离开触屏设备表面时触发;touchmove
事件:手指在触屏设备表面移动时触发;touchcancel
事件:当接触点被某些方式打乱时触发 (例如双指/多指操作等,本文不涉及touchcancel
事件)。
实现这个功能的主要思路就是:在
touchstart
事件中记录DOM
元素和接触点的初始位置;在touchmove
事件中记录接触点的当前位置,根据以上记录的信息计算出接触点移动前后的x
,y
方向的偏移量,最后利用这个偏移量对DOM
元素进行重新定位。
在上代码前,先复习一下 DOM
事件涉及的各种 x
,y
。
2. 各种 x
,y
clientX
,clientY
:事件接触点相对于窗口的X
、Y
坐标。例如窗口初始状态时点击页面的左上角,这时对应的值为event.clientX = 0
、event.clientY = 0
;页面纵向滚动100px
,这时再点击页面左上角仍然得到(0,0),而上次点击的接触点已经是(0,-100)了。pageX
,pageY
:事件接触点相对于HTML
文档的X
、Y
坐标。在窗口初始状态,这两个值与clientX
,clientY
是相同的,区别在于当窗口发生滚动时。如上例的事件,第一次事件的pageX
,pageY
值同样为(0,0),而第二次事件中窗口左上角pageX
,pageY
的值则是(0,100)。screenX
,screenY
:事件接触点相对于显示器边沿的X
、Y
坐标。嗯,应该已经很好理解了吧。layerX
,layerY
:事件接触点相对于position
为absolute
或relative
的父元素外边缘的距离(如果最近的一级父元素没有相应的position
值,则继续往上级对应),这个外边缘指的是boder
的最外沿,至于padding
、boder
和margin
的位置这里就不复习了。offsetX
,offsetY
:事件接触点相对于触发事件元素的左上角的偏移,这两个值在不同浏览器的表现有一些区别,使用的时候需要注意,比如在chrome
、opera
,、safari
中是指外边缘,即把从边框的外边沿起算,firefox
和ie
中则从边框的内边沿起算。另外还有
x
,y
等一些不同浏览器支持不足的属性,不再一一复习。本例中每次触摸滑动时需要计算接触点在屏幕上的偏移量,因为移动端一般是固定窗口,这里我们又只计算相对位置,因此具体使用的属性可以有多个选择,实际开发中使用的是
pageY
(纵向滚动)。
上代码前最后需要注意一点,就是当已经滚动到顶部和底部的时候,一定要及时提醒它悬崖勒马啊!(误)
3. 功能实现
// 获取要滚动的元素对象
var infoObj = document.getElementById("content-info-body");
infoObj.addEventListener("touchstart", function (event) {
event.preventDefault();
// 获取该元素相对于父级元素的top值(父级元素需要有position,当前元素position:relative)
var startTop = this.offsetTop;
//touches是屏幕上所有的touch,取第一个
var touchstart = event.targetTouches[0];
//取触点的初始
startPos = {
x: touchstart.pageX,
y: touchstart.pageY,
};
infoObj.addEventListener('touchmove', function (event) {
// 这句视情况而定,是为了屏蔽原生的touch滚动事件,而我的项目里没有这个,尴尬
event.preventDefault();
var touchmove = event.targetTouches[0];
// 偏移量
var offsetPos = {
x: touchmove.pageX - startPos.x,
y: touchmove.pageY - startPos.y
};
// 重新定位需要的目标top值
var endTop = startTop + offsetPos.y;
// 需要判断滚动到顶和到底的情况
if (endTop < 10 && endTop > this.parentElement.offsetHeight - this.offsetHeight) {
this.style.top = endTop + 'px';
} else if (endTop >= 10) {
this.style.top = '0px';
} else {
// this.style.top = '-536px';
this.style.top = (this.parentElement.offsetHeight - this.offsetHeight - 10) + 'px';
}
});
infoObj.addEventListener('touchend', function (event) {
infoObj.removeEventListener('touchmove', this, false);
infoObj.removeEventListener('touchend', this, false);
});
})
到此这个功能就实现了,在手机上测试良好,在这个基础上可以优化一下,在touchend
里加一个小的后续偏移,这样就很接近原生的表现了。
说一千道一万,这个功能最后还是没有用到,这涉及C#
与JS
交互的cefsharp
的某些设置问题,在这里就不讨论了,借这个机会加深一下对DOM
事件的理解,也是一个不错的过程。