引言

常用的鼠标事件

首先咱们还是先回顾一下鼠标的常用事件:

  • click(点击)
  • dblclick(双击)
  • mouseover(鼠标移入)
  • mouseout(鼠标移出)
  • mouseenter(鼠标移入)
  • mouseleave(鼠标移出)
  • mouseup(鼠标抬起)
  • mousedown(鼠标按下)
  • mousemove(鼠标移动)

以上就是咱们常用的鼠标事件,我们发现其中的mouseovermouseenter还有mouseoutmouseleave好像是一样的事件,但凭我们的经验虽然看似一样,其实肯定不一样,或者说有区别。

那么他们有什么区别呢?

mouseovermouseenter都是鼠标移入时触发,但区别是mouseover支持事件冒泡,而mouseenter不支持事件冒泡。简单说就是 mouseover事件在鼠标指针移入被选元素或者是被选元素的任何子元素,都会触发,而mouseenter事件只有在鼠标指针移入被选元素时,才会触发,移入被选元素的子元素不会触发。

mouseoutmouseleave都是鼠标移入时触发,但区别是mouseout支持事件冒泡,而mouseleave不支持事件冒泡。简单说就是 mouseout事件在鼠标指针离开被选元素或者是被选元素的任何子元素,都会触发,而mouseleave事件只有在鼠标指针离开被选元素时,才会触发,离开被选元素的子元素不会触发。

键盘事件
  • keydown(键盘按下)
  • keyup(键盘抬起)
  • keypress(紧接着keydown事件触发(只有按下字符键时触发))

如果用户按下了一个字符键不放,就会重复触发keydownkeypress事件,直到用户松开该键为止,如果用户按下了一个非字符键不放,就会重复触发keydown事件

keypress事件返回的是输入的字符的ASCII码(which属性),而keydown事件返回的是键盘码(keyCode属性)。

ps:keypress支持的系统功能键

**Firefox**:Esc、Enter、Backspace、Pause Break、Insert、 Delete、Home、End、Page Up、Page Down、F1 through F12、The Arrow Keys、上下左右键

Chrome / Oprea / SafariEnter

**IE**:Esc、Entery

除了 Firefox,其他chrome、oprea、safari、IE 上下左右键不会触发keypress

事件的添加和移除

给Canvas中的元素添加事件我们用的是:addEventListener()方法,移除事件用的是removeEventListener()方法

参数值

  1. event 必须。字符串,指定事件名。

    注意: 不要使用 “on” 前缀。 例如,使用 “click” ,而不是使用 “onclick”。

  2. function 必须。指定要事件触发时执行的函数。

    当事件对象会作为第一个参数传入函数。 事件对象的类型取决于特定的事件。例如, “click” 事件属于 MouseEvent(鼠标事件) 对象。

  3. useCapture 可选。布尔值,指定事件是否在捕获或冒泡阶段执行。

首先看一下添加鼠标事件

// 获取Canvas
const canvas = document.getElementById('canvas');
// 获取绘制上下文
const ctx = canvas.getContext('2d');
// 给canvas添加鼠标移动事件
canvas.addEventListener("mousemove", mouseMoving, false);
function mouseMoving(e) {
console.log(`当前鼠标在Canvas中的位置: x: ${e.clientX} y: ${e.clientY}`);//e.clientX表示鼠标相对于浏览器窗口的水平位置,e.clientY表示鼠标相对于浏览器窗口的垂直位置。
}

5.gif

Canvas支持所有的鼠标事件,但是并不支持键盘事件,那么想要让Canvas支持键盘事件我们就需要自己处理,这边有两种方法可以实现键盘事件。

方法一:让Canvas自动获取焦点,从而支持键盘事件。

<body>
<canvas
id="canvas"
width="550"
height="500"
tabindex="0"//使其可聚焦
style="box-shadow: 0px 0px 5px #ccc; border-radius: 8px;">
当前浏览器不支持canvas元素,请升级或更换浏览器!
</canvas>
<script>
// 获取Canvas
const canvas = document.getElementById('canvas');
// 获取绘制上下文
const ctx = canvas.getContext('2d');
// 给canvas添加键盘事件
canvas.addEventListener("keydown", doKeydown, false);
canvas.focus();//自动获取焦点
function doKeydown(e) {
switch(e.keyCode) {
case 37:
console.log(`按下左键`)
break;
case 38:
console.log(`按下上键`)
break;
case 39:
console.log(`按下右键`)
break;
case 40:
console.log(`按下下键`)
break;
}
}
</script>
</body>

6.gif

需要注意,当鼠标点击别的元素的时候,canvas元素会失去焦点,从而失去键盘事件

方法二:通过为windows对象添加键盘事件,从而控制canvas元素(更常用)

<body>
<canvas
id="canvas"
width="500"
height="500"
tabindex="0"
style="box-shadow: 0px 0px 5px #ccc; border-radius: 8px;">
当前浏览器不支持canvas元素,请升级或更换浏览器!
</canvas>
<script>
// 获取Canvas
const canvas = document.getElementById('canvas');
// 获取绘制上下文
const ctx = canvas.getContext('2d');
// 设置填充的颜色为橘色
ctx.fillStyle="orange";
// 获取x,y的值
let x = canvas.width / 2 - 50;
let y = canvas.height / 2 - 50;

// 绘制一个矩形
ctx.fillRect(x, y, 100, 50);
// 给canvas添加鼠标移动事件
window.addEventListener("keydown", doKeydown, false);

function doKeydown(e) {
ctx.clearRect(0, 0, 500, 500)//绘制新图像之前先擦除之前的图像
let keyID = e.keyCode ? e.keyCode :e.which;
switch(keyID) {
case 37:
console.log(`按下左键`)
x = x - 10;
ctx.fillRect(x, y, 100, 50);
break;
case 38:
console.log(`按下上键`)
y = y - 10;
ctx.fillRect(x, y, 100, 50);
break;
case 39:
console.log(`按下右键`)
x = x + 10;
ctx.fillRect(x, y, 100, 50);
break;
case 40:
console.log(`按下下键`)
y = y + 10;
ctx.fillRect(x, y, 100, 50);
break;
}
}
</script>
</body>

7.gif

内部元素添加事件

上面的所有事件其实都是添加到canvas元素上的,但往往在平常的需求中我们需要针对canvas元素内部的子元素做单独的事件交互,那么我们就需要考虑如何给canvas元素的内部元素添加事件。

canvas元素本身并没有提供给内部元素添加事件的Api,所以我们直接使用原生的方式和canvas元素的内部元素进行交互

这里就以拖拽为例,假如canvas元素内部有多个子元素,那么想拖拽其中一个子元素,我们首先得知道,在鼠标按下的时候是否按在canvas元素的子元素上,只有按在canvas元素的子元素上我们才能对它进行拖拽。

1.准备工作
<script>
// 获取Canvas
const canvas = document.getElementById('canvas');
const width = canvas.width;
const height = canvas.height;
// 获取绘制上下文
const ctx = canvas.getContext('2d');
const images = [
{
name: "白月魁",
url: "../images/bailaoban.jpg"
},
{
name: "鸣人",
url: "../images/mingren.jpg",
},
{
name: "路飞",
url: "../images/lufei.jpg",
},
{
name: "哪吒",
url: "../images/nazha.jpg",
},
{
name: "千寻",
url: "../images/qianxun.jpg",
},
];
//遍历
images.forEach((item)=>{
// 创建image元素
const image = new Image()
image.src = item.url;
const name = item.name;
image.onload = () => {
// 控制宽度为200(等宽)
const w = 200;
// 高度按宽度200的比例缩放
const h = 200 / image.width * image.height;
const x = Math.random() * (width - w) ;//保证能在画布里面正常显示
const y = Math.random() * (height - h);
const imageObj = { image, name, x, y, w, h }
draw(imageObj)
}
})

// 渲染图片
function draw(imageObj) {
ctx.drawImage(imageObj.image, imageObj.x, imageObj.y, imageObj.w, imageObj.h);
}
</script>

image.png

2.添加事件
// 为canvas添加鼠标按下事件
canvas.addEventListener("mousedown", mousedownFn, false)

// 鼠标按下触发的方法
function mousedownFn(e) {
// 为canvas添加鼠标移动和鼠标抬起事件
canvas.addEventListener("mousemove", mousemoveFn, false)
canvas.addEventListener("mouseup", mouseupFn, false)
}

// 鼠标移动触发
function mousemoveFn() {}

// 鼠标抬起触发
function mouseupFn() {}
3.判断选中元素

定义完事件以后,我们就需要判断每次点击的元素是其中的哪一个,这样我们才能针对这个元素做交互。

通过计算,如上面布局的代码,每个图片绘制的x、y、width和height我们都是知道的,那么当我们每次点击下去的时候就可以遍历图片的数据,看我们是否点击到元素上。

// 获取Canvas
const canvas = document.getElementById('canvas');
const width = canvas.width;
const height = canvas.height;
// 获取绘制上下文
const ctx = canvas.getContext('2d');
const images = [
{
name: "白月魁",
url: "../images/bailaoban.jpg"
},
{
name: "鸣人",
url: "../images/mingren.jpg",
},
{
name: "路飞",
url: "../images/lufei.jpg",
},
{
name: "哪吒",
url: "../images/nazha.jpg",
},
{
name: "千寻",
url: "../images/qianxun.jpg",
},
];

let imagesData = []//存储图片信息对象
let clickCoordinate = { x: 0, y: 0 } //存储鼠标按下时的坐标信息
let target;//存储index

images.forEach((item)=>{
// 创建image元素
const image = new Image()
image.src = item.url;
const name = item.name;
image.onload = () => {
// 控制宽度为200(等宽)
const w = 200;
// 高度按宽度200的比例缩放
const h = 200 / image.width * image.height;
const x = Math.random() * (width - w) ;
const y = Math.random() * (height - h);
const imageObj = { image, name, x, y, w, h }
imagesData.push(imageObj)
draw(imageObj)
}
})

// 渲染图片
function draw(imageObj) {
ctx.drawImage(imageObj.image, imageObj.x, imageObj.y, imageObj.w, imageObj.h);
}

// 为canvas添加鼠标按下事件
canvas.addEventListener("mousedown", mousedownFn, false)

// 鼠标按下触发的方法
function mousedownFn(e) {
// 获取元素按下时的坐标
clickCoordinate.x = e.pageX - canvas.offsetLeft;//e.pageX 鼠标相对于左侧边缘的水平偏移量
clickCoordinate.y = e.pageY - canvas.offsetTop;
// 判断选中的元素是哪一个
checkElement()
// 为canvas添加鼠标移动和鼠标抬起事件
canvas.addEventListener("mousemove", mousemoveFn, false)
canvas.addEventListener("mouseup", mouseupFn, false)
}
// 鼠标移动触发
function mousemoveFn() {}

// 鼠标抬起触发
function mouseupFn() {}

function checkElement() {
//遍历去一一对应
imagesData.forEach((item, index)=>{
const minX = item.x
const maxX = item.x + item.w
const minY = item.y
const maxY = item.y + item.h
if(minX <= clickCoordinate.x && clickCoordinate.x <= maxX && minY <= clickCoordinate.y && clickCoordinate.y <= maxY) {
target = index
console.log("点击的元素是:", item.name)
}
})

}

1.gif

4.移动

知道选中的元素以后,我们就需要在移动的时候把移动的坐标赋值给选中的元素,让选中的元素跟着鼠标移动。

// 鼠标移动触发
function mousemoveFn(e) {
const moveX = e.pageX //鼠标移动结束的位置
const moveY = e.pageY //鼠标移动结束的位置
console.log(moveX,moveY)
// 计算移动元素的坐标
imagesData[target].x = imagesData[target].x + ( moveX - clickCoordinate.x );//移动位移
imagesData[target].y = imagesData[target].y + ( moveY - clickCoordinate.y );
// 清空画布
ctx.clearRect(0, 0, width, height);
// 清空画布以后重新绘制
imagesData.forEach((i) => draw(i))
// 赋值 更新clickCoordinate的坐标
clickCoordinate.x = moveX;
clickCoordinate.y = moveY;
}

// 鼠标抬起触发
function mouseupFn() {
// 鼠标抬起以后移除事件
canvas.removeEventListener("mousemove", mousemoveFn, false)
canvas.removeEventListener("mouseup", mouseupFn, false)
// 销毁选中元素
target = undefined
}

2.gif