Touch事件分类
- touchstart:当手指触摸屏幕时触发。不管有多少个手指放在了屏幕上,只要再触摸一下屏幕就会触发
- touchmove:当手指在屏幕上滑动的时候触发该是事件,在这期间可以通过
event.preventDefault()
来阻止滚动 - touchend:手指从屏幕中移开的时候触发
- touchcancel:当系统停止跟踪触摸时触发(例如:创建了太多的触控点)
例如
代码语言:javascript复制this.canvas.addEventListener("touchstart", this._touchStart);
Touch事件相关对象属性
Event对象属性
属性均为只读。
Property | Type | Description |
---|---|---|
target | EventTarget | 事件目标 (DOM 树最上方的目标). |
type | DOMString | 事件类型 |
bubbles | Boolean | 事件是否正常冒泡 |
cancelable | Boolean | 事件是否可以取消 |
view | WindowProxy | document.defaultView (window of the document) |
detail | long (float) | 常量 0 |
touches | TouchList | Touch 列表,由触摸平面当前的接触点组成 |
targetTouches | TouchList | Touch 列表,是包含了如下触点的 Touch 对象:触摸起始于当前事件的目标 element 上,并且仍然没有离开触摸平面的触点。 |
changedTouches | TouchList | Touch 列表,由从触摸平面移除了的接触点组成 |
ctrlKey | boolean | 如果事件发生时按下了 ctrl 键则为 true,否则为 false |
shiftKey | boolean | 如果事件发生时按下了 shift 键则为 true,否则为 false |
altKey | boolean | 如果事件发生时按下了 alt 键则为 true,否则为 false |
metaKey | boolean | 如果事件发生时按下了 meta 键则为 true,否则为 false |
其中有三个相似的属性touches
、targetTouches
及changedTouches
,它们有什么不同呢?
- touches:表示屏幕上触摸操作的touch对象的属性;
- targetTouches:表示对应DOM上触摸操作的Touch对象的数组。
- changeTouches:表示从上一次触摸以来,发生了改变的touch对象的数组。
通过一个例子来区分一下触摸事件中的这三个属性:
- 用一个手指接触屏幕,触发事件,此时这三个属性有相同的值。
- 用第二个手指接触屏幕,此时,
touches
有两个元素,每个手指触摸点为一个值。 当两个手指触摸相同元素时,targetTouches
和touches
的值相同,否则targetTouches
只有一个值。changedTouches
此时只有一个值,为第二个手指的触摸点。 - 用两个手指同时接触屏幕,此时
changedTouches
有两个值,每一个手指的触摸点都有一个值 - 手指滑动时,三个值都会发生变化
- 一个手指离开屏幕,
touches
和targetTouches
中对应的元素会同时移除, 而changedTouches
仍然会存在元素。 - 手指都离开屏幕之后,
touches
和targetTouches
中将不会再有值,changedTouches
还会有一个值,此值为最后一个离开屏幕的手指的接触点。
Touch对象属性
所有属性均为只读属性。
Touch.identifier
此 Touch 对象的唯一标识符
. 一次触摸动作(我们指的是手指的触摸)在平面上移动的整个过程中, 该标识符不变. 可以根据它来判断跟踪的是否是同一次触摸过程.
Touch.screenX
触点相对于屏幕左边沿的的X坐标.
Touch.screenY
触点相对于屏幕上边沿的的Y坐标.
Touch.clientX
触点相对于可见视区(visual viewport)左边沿的的X坐标. 不包括任何滚动偏移.
Touch.clientY
触点相对于可见视区(visual viewport)上边沿的的Y坐标. 不包括任何滚动偏移.
Touch.pageX
触点相对于HTML文档左边沿的的X坐标. 当存在水平滚动的偏移时, 这个值包含了水平滚动的偏移
.
Touch.pageY
触点相对于HTML文档上边沿的的Y坐标. 当存在垂直滚动的偏移时, 这个值包含了垂直滚动的偏移
.
Touch.radiusX
能够包围用户和触摸平面的接触面的最小椭圆的水平轴(X轴)半径. 这个值的单位和screenX 相同.
Touch.radiusY
能够包围用户和触摸平面的接触面的最小椭圆的垂直轴(Y轴)半径. 这个值的单位和screenY 相同.
Touch.rotationAngle
它是这样一个角度值:由radiusX
和 radiusY
描述的正方向的椭圆,需要通过顺时针旋转这个角度值,才能最精确地覆盖住用户和触摸平面的接触面.
Touch.force
手指挤压触摸平面的压力大小, 从0.0(没有压力)到1.0(最大压力)的浮点数.
Touch.target
当这个触点最开始被跟踪时(在 touchstart
事件中), 触点位于的HTML元素. 哪怕在触点移动过程中, 触点的位置已经离开了这个元素的有效交互区域, 或者这个元素已经被从文档中移除. 需要注意的是, 如果这个元素在触摸过程中被移除, 这个事件仍然会指向它, 但是不会再冒泡这个事件到 window
或 document
对象. 因此, 如果有元素在触摸过程中可能被移除, 最佳实践是将触摸事件的监听器绑定到这个元素本身, 防止元素被移除后, 无法再从它的上一级元素上侦测到从该元素冒泡的事件.
接触点
- screenX是相对于屏幕左上角的坐标
- clientX是相对于窗口可视区的左上角坐标
- pageX是相对于窗口内页面的左上角的坐标(常用)
所以相对于绘制区域的坐标可以这样取到
代码语言:javascript复制{
x: event.pageX - this.canvas.offsetLeft,
y: event.pageY - this.canvas.offsetTop
}
接触面
Touch.radiusX
, Touch.radiusY
, 和 Touch.rotationAngle
描述了用户和触摸平面的接触面. 这在面向非精确触摸设备(由手指直接操作的触摸屏)开发时非常有用. 这些值描述了一个尽可能接近实际接触面(例如用户的指尖)的椭圆.
MouseEvent属性
属性/方法 | 描述 |
---|---|
clientX | 触发鼠标事件时,返回鼠标指针相对于当前窗口的水平坐标 |
clientY | 触发鼠标事件时,返回鼠标指针相对于当前窗口的垂直坐标 |
pageX | 触发鼠标事件时,返回鼠标指针相对于文档的水平坐标 |
pageY | 触发鼠标事件时,返回鼠标指针相对于文档的垂直坐标 |
screenX | 触发事件时,返回鼠标指针相对于屏幕的水平坐标 |
screenY | 触发事件时,返回鼠标指针相对于屏幕的垂直坐标 |
offsetX | 返回鼠标指针相对于目标元素边缘位置的水平坐标 |
offsetY | 返回鼠标指针相对于目标元素边缘位置的垂直坐标 |
movementX | 返回鼠标指针相对于上一个mousemove事件位置的水平坐标 |
movementY | 返回鼠标指针相对于上一个mousemove事件位置的垂直坐标 |
target | 返回与触发鼠标事件的元素相关的元素 |
which | 返回触发鼠标事件时按下的鼠标按钮 |
altKey | 返回触发鼠标事件时是否按下ALT键 |
ctrlKey | 返回触发鼠标事件时是否按下CTRL键 |
shiftKey | 返回触发事件时是否按下SHIFT键 |
metaKey | 返回触发事件时是否按下META键 |
和Touch对象相比screenX
,clientX
,pageX
两者都有。
Cavas绘图
画线常用的有两种方式lineTo
和quadraticCurveTo
用quadraticCurveTo
绘制的线比较圆滑,但是每次都要全图绘制,
所以我先用的方式就是在画线的过程中用lineTo
,鼠标抬起或者触屏离开时重新进行全屏绘制,但是会突然一变,最后还是决定在光标移动中就不停的全部quadraticCurveTo
绘制,这样也没有明显的慢,就决定用后来的这种方式了。
ZJSketchpad.prototype.drawStroke = function(stroke) {
this.context.beginPath();
this.context.lineJoin = "round";
this.context.lineCap = "round";
this.context.strokeStyle = stroke.color;
this.context.lineWidth = stroke.size;
if (stroke.type === this.typelist.pen) {
this.context.globalCompositeOperation = "source-over";
} else {
this.context.globalCompositeOperation = "destination-out";
}
let startpot = stroke.points[0];
this.context.moveTo(startpot.x, startpot.y);
if (stroke.type === this.typelist.erase) {
for (let i = 0; i < stroke.points.length; i ) {
let potthis = stroke.points[i];
this.context.lineTo(potthis.x, potthis.y);
}
} else {
for (let i = 1; i < stroke.points.length - 2; i ) {
let potthis = stroke.points[i];
let potnext = stroke.points[i 1];
let ctrlPoint = {};
ctrlPoint.x = (potthis.x potnext.x) / 2;
ctrlPoint.y = (potthis.y potnext.y) / 2;
this.context.quadraticCurveTo(
potthis.x,
potthis.y,
ctrlPoint.x,
ctrlPoint.y
);
}
if (stroke.points.length >= 2) {
let lastpoint1 = stroke.points[stroke.points.length - 2];
let lastpoint2 = stroke.points[stroke.points.length - 1];
this.context.quadraticCurveTo(
lastpoint1.x,
lastpoint1.y,
lastpoint2.x,
lastpoint2.y
);
}
}
this.context.stroke();
this.context.restore();
};
触屏时是不显示光标的,所以我们没法通过光标来模拟黑板擦图标,所以只能用图片来模拟,移动时调整top和left的值来展现,但是touch事件会被图片给挡掉,最简单的方法就是按照下面最后两行来设置样式
代码语言:javascript复制.m_erase {
position: absolute;
top: 0;
left: 0;
width: 64px;
height: 64px;
margin-left: -32px;
margin-top: -32px;
z-index: 999;
display: none;
pointer-events: none;
touch-action: none;
}
绘制注意项
如果我们在视网膜屏幕上绘制图像,会发现按像素1:1绘制出来的效果会不清晰,这就要用到devicePixelRatio
属性。
devicePixelRatio属性
该 Window 属性 devicePixelRatio 能够返回当前显示设备的物理像素分辨率与 CSS 像素分辨率的比率。此值也可以解释为像素大小的比率:一个 CSS 像素的大小与一个物理像素的大小的比值。简单地说,这告诉浏览器应该使用多少个屏幕的实际像素来绘制单个 CSS 像素。
devicePixelRatio属性语法
代码语言:javascript复制var scale = window.devicePixelRatio;
devicePixelRatio 属性值为一个 double。
devicePixelRatio属性示例
一个 canvas 在视网膜屏幕上可能显得太模糊。
使用 window.devicePixelRatio
以确定应该添加多少额外的像素密度以允许更清晰的图像。
HTML
代码语言:javascript复制<canvas id="canvas" style="width:200px;height:200px;"></canvas>
JavaScript
代码语言:javascript复制var canvas = document.getElementById('canvas');
// Set actual size in memory (scaled to account for extra pixel density).
var devicePixelRatio = window.devicePixelRatio;
var dom_width = canvas.clientWidth;
var dom_height = canvas.clientHeight;
canvas.width = dom_width * devicePixelRatio;
canvas.height = dom_height * devicePixelRatio;
var ctx = canvas.getContext('2d');
// Normalize coordinate system to use css pixels.
ctx.scale(devicePixelRatio, devicePixelRatio);
ctx.fillStyle = "#bada55";
ctx.fillRect(0, 0, dom_width, dom_height);
ctx.fillStyle = "#ffffff";
ctx.font = '18px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
var x = dom_width / 2;
var y = dom_height / 2;
var textString = "剑行者";
ctx.fillText(textString, x, y);
浏览器中查看到的DOM如下:
代码语言:javascript复制<canvas id="canvas" style="width:200px;height:200px;" width="400" height="400"></canvas>
这样上面的代码就绘制了一个背景为绿色,中间显示剑行者的图片了,并且十分的清晰。
其中有这么一个方法
代码语言:javascript复制ctx.scale(scalewidth,scaleheight);
scale()
方法缩放当前绘图,更大或更小。
如果您对绘图进行缩放,所有之后的绘图都会被缩放。定位、宽高和画笔的大小都会被缩放。 如果您 scale(2,2),那么绘图将定位于距离画布左上角两倍远的位置。
假设我们获取的window.devicePixelRatio
为2,为了显示清晰我们把cavas的宽高也放大了两倍,但是我们通过touch拿到的坐标是相对于页面中cavas大小(和cavas内部的大小不一致)的定位,我们就需要把所有的坐标都放大两倍,就比较麻烦,我们就可以先调scale()
方法,那么之后绘制的坐标都会自动放大后再绘制,相当的方便。