交互动画的一个主要目标是创建出流畅的用户体验,其中大多数的用户交互都是通过鼠标和触摸屏实现的。
在这篇博文中,我想分享一些JS对于物体移动的常见用法,包括拖拽和投掷效果。
一. 使用鼠标事件
可以将一个鼠标单击事件分解成两个事件:鼠标按下事件和按键弹起事件。通常情况下这两个事件是同时发生的。不过,有时鼠标按下后,鼠标还会移动一段时间才弹起,这种操作称为拖曳,即按下、移动、在释放。
在canvas动画中,鼠标事件只能被HTML DOM树上的canvas元素所捕获,因此,我们需要手动计算出鼠标事件在canvas上的发生位置,并判断出它是否发生在哪个绘制到canvas的物体 上。需要关注的鼠标事件有:mousedown、mousemove和mouseup。具体细节可参考我的相关博文《JavaScript动画详解(一) —— 循环与事件监听》。
二. 使用触摸事件
随着触摸屏设备的流行,我们很可能需要在动画中捕捉用户的触摸事件。虽然触摸屏与鼠标是不同的设备,但幸运的是,在DOM树上捕捉触摸事件与捕捉鼠标事件的差别不大。
与鼠标事件mousedown、mousemove和mouseup相对应的触摸事件分别是touchstart、touchend与touchmove。
使用手指与鼠标的一个比较大的区别在于,鼠标始终出现在屏幕上,而手指却不是一直处于触摸状态。常见的做法是,引入自定义属性isPressed,用来告诉我们屏幕上是否有手指在触摸。具体细节可参考我的相关博文《JavaScript动画详解(一) —— 循环与事件监听》。
三. 拖拽事件
拖拽事件包含了三个子事件:鼠标按下、移动、释放。通过不断更新物体的坐标位置使其追随鼠标指针的位置,就可以实现在canvas元素上拖拽物体。 另外还需要一个自定义属性isPressed来标示当前鼠标是否按下,默认为false表示鼠标为弹起状态。实现代码包含以下过程:
1 . 捕捉mousedown事件,判断当前鼠标是否在物体内。当鼠标在物体内按下时,设置isPressed = true;
2 . 捕捉mousemove事件,在处理程序内判断当isPressed = true时,通过不断更新物体的坐标位置使其追随鼠标指针的位置来模拟出鼠标拖拽效果;
3 . 捕捉mouseup事件,将isPressed设置为false;
HTML代码如下:
<canvas id="canvas" width="400" height="400"></canvas>
JavaScript代码如下:
// 创建画球函数 function Ball() { this.x = 0; this.y = 0; this.radius = 20; this.fillStyle = "#f85455"; this.draw = function(cxt) { cxt.fillStyle = this.fillStyle; cxt.beginPath(); cxt.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, true); cxt.closePath(); cxt.fill(); } } // 获得当前鼠标位置 function getMouse(ev) { var mouse = { x: 0, y: 0 }; var event = ev || window.event; if(event.pageX || event.pageY) { x = event.x; y = event.y; }else { var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft; var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; x = event.clientX + scrollLeft; y = event.clientY + scrollTop; } mouse.x = x; mouse.y = y; return mouse; } var canvas = document.getElementById("canvas"), context = canvas.getContext("2d"), ball = new Ball(), mouse = {x: 0, y: 0}, isPressed = false; ball.x = 20; ball.y = 20; // 渲染小球 ball.draw(context); // 小球拖拽事件 canvas.addEventListener("mousedown", mouseDown, false); canvas.addEventListener("mousemove", mouseMove, false); canvas.addEventListener("mouseup", mouseUp, false); function mouseDown(ev) { // 判断当前鼠标是否在小球内 mouse = getMouse(ev); if(Math.pow(mouse.x - ball.x, 2) + Math.pow(mouse.y - ball.y, 2) <= Math.pow(ball.radius, 2)) { isPressed = true; } } function mouseMove(ev) { if(isPressed) { mouse = getMouse(ev); ball.x = mouse.x; ball.y = mouse.y; context.clearRect(0, 0, canvas.width, canvas.height); ball.draw(context); } } function mouseUp(ev) { // 标示鼠标弹起事件 isPressed = false; }
但是,这个例子是有bug的!很快你就能发现,在拖拽的时候,小球的中心位置都在鼠标位置上,特别是当鼠标单击小球边缘时,会看见小球的中心点突然就跳动到了鼠标光标的位置上了。显然,这显得有点唐突。
我们可以稍作改良:
在鼠标按下的时候记录当前鼠标位置与小球中心点位置的偏移量;
// 记录鼠标按下时,鼠标与小球圆心的偏移量 dx = mouse.x - ball.x; dy = mouse.y - ball.y;
在鼠标移动时,用鼠标的当前位置减去鼠标按下时记录的偏移量
ball.x = mouse.x - dx; ball.y = mouse.y - dy;
四. 投掷事件
在动画中如何表现投掷呢?用鼠标选中一个物体,拖拽着它向某个方向移动,松开鼠标后,物体沿着拖拽的方向继续移动。
在投掷物体时,必须在拖拽物体的过程中计算物体的速度向量,并在释放物体时将这个速度向量赋给物体。实际上,计算拖拽时物体的速度向量的过程,恰好 与对物体应用速度向量的过程相反。在对物体应用速度向量时,将速度追加到物体原来所在的位置上,从而计算出物体的新位置,这个公式可以写成:旧的位置 + 速度向量 = 新的位置,即速度向量 = 新的位置 – 旧的位置。
为了实现投掷行为,需要对前面的代码做一些改动。首先,检查鼠标是否按下,如果按下,用oldX和oldY变量保存小球旧的x、y坐标位置,并更新小球的拖拽速度。
具体JavaScript代码实现如下:
// 创建画球函数 function Ball() { this.x = 0; this.y = 0; this.radius = 20; this.fillStyle = "#f85455"; this.draw = function(cxt) { cxt.fillStyle = this.fillStyle; cxt.beginPath(); cxt.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, true); cxt.closePath(); cxt.fill(); } } // 获得当前鼠标位置 function getMouse(ev) { var mouse = { x: 0, y: 0 }; var event = ev || window.event; if(event.pageX || event.pageY) { x = event.x; y = event.y; }else { var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft; var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; x = event.clientX + scrollLeft; y = event.clientY + scrollTop; } mouse.x = x; mouse.y = y; return mouse; } var canvas = document.getElementById("canvas"), context = canvas.getContext("2d"), ball = new Ball(), mouse = {x: 0, y: 0}, isPressed = false, oldX = 0, oldY = 0, currentX = 0, currentY = 0, vx = 0, vy = 0; ball.x = 200; ball.y = 200; // 声明鼠标按下时,鼠标与小球圆心的距离 var dx = 0, dy = 0; // 渲染小球 ball.draw(context); // 小球拖拽事件 canvas.addEventListener("mousedown", mouseDown, false); canvas.addEventListener("mousemove", mouseMove, false); canvas.addEventListener("mouseup", mouseUp, false); function mouseDown(ev) { // 判断当前鼠标是否在小球内 mouse = getMouse(ev); if(Math.pow(mouse.x - ball.x, 2) + Math.pow(mouse.y - ball.y, 2) <= Math.pow(ball.radius, 2)) { isPressed = true; // 记录鼠标按下时,鼠标与小球圆心的距离 dx = mouse.x - ball.x; dy = mouse.y - ball.y; // 获得小球拖拽前的位置 mouse = getMouse(ev); oldX = mouse.x; oldY = mouse.y; } } function mouseMove(ev) { if(isPressed) { mouse = getMouse(ev); ball.x = mouse.x - dx; ball.y = mouse.y - dy; context.clearRect(0, 0, canvas.width, canvas.height); ball.draw(context); } } function mouseUp(ev) { // 标示鼠标弹起事件 isPressed = false; // 把鼠标与圆心的距离位置恢复初始值 dx = 0; dy = 0; // 获得小球拖拽后的位置 mouse = getMouse(ev); currentX = mouse.x; currentY = mouse.y; // 更新速度向量:速度向量 = 新的位置 - 旧的位置 vx = (currentX - oldX) * 0.05; vy = (currentY - oldY) * 0.05; drawFrame(); } // 缓动动画 function drawFrame() { animRequest = window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, canvas.width, canvas.height); if(ball.x >= canvas.width - 30 || ball.x <= 30 || ball.y >= canvas.height - 30 || ball.y <= 30) { window.cancelAnimationFrame(animRequest); } ball.x += vx; ball.y += vy; ball.draw(context); }
这个Demo的边界判断还有一些bug,过些日子再修复。
五. 总结
物体移动事件可以有很多总运动形式,但是都可以分解为三个单独的事件来控制:按下、移动、释放,在鼠标事件中分别对应的是mousedown、 mousemove和mouseup,在触摸事件中分别对应的是touchstart、touchmove和touchend。通过不断更新物体的坐标位 置使其追随鼠标指针的位置,就可以实现在canvas元素上拖拽和投掷的效果。
以上是通过JavaScript实现移动物体代码的详细内容。更多信息请关注PHP中文网其他相关文章!

C 和JavaScript通过WebAssembly实现互操作性。1)C 代码编译成WebAssembly模块,引入到JavaScript环境中,增强计算能力。2)在游戏开发中,C 处理物理引擎和图形渲染,JavaScript负责游戏逻辑和用户界面。

JavaScript在网站、移动应用、桌面应用和服务器端编程中均有广泛应用。1)在网站开发中,JavaScript与HTML、CSS一起操作DOM,实现动态效果,并支持如jQuery、React等框架。2)通过ReactNative和Ionic,JavaScript用于开发跨平台移动应用。3)Electron框架使JavaScript能构建桌面应用。4)Node.js让JavaScript在服务器端运行,支持高并发请求。

Python更适合数据科学和自动化,JavaScript更适合前端和全栈开发。1.Python在数据科学和机器学习中表现出色,使用NumPy、Pandas等库进行数据处理和建模。2.Python在自动化和脚本编写方面简洁高效。3.JavaScript在前端开发中不可或缺,用于构建动态网页和单页面应用。4.JavaScript通过Node.js在后端开发中发挥作用,支持全栈开发。

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。 1)C 用于解析JavaScript源码并生成抽象语法树。 2)C 负责生成和执行字节码。 3)C 实现JIT编译器,在运行时优化和编译热点代码,显着提高JavaScript的执行效率。

JavaScript在现实世界中的应用包括前端和后端开发。1)通过构建TODO列表应用展示前端应用,涉及DOM操作和事件处理。2)通过Node.js和Express构建RESTfulAPI展示后端应用。

JavaScript在Web开发中的主要用途包括客户端交互、表单验证和异步通信。1)通过DOM操作实现动态内容更新和用户交互;2)在用户提交数据前进行客户端验证,提高用户体验;3)通过AJAX技术实现与服务器的无刷新通信。

理解JavaScript引擎内部工作原理对开发者重要,因为它能帮助编写更高效的代码并理解性能瓶颈和优化策略。1)引擎的工作流程包括解析、编译和执行三个阶段;2)执行过程中,引擎会进行动态优化,如内联缓存和隐藏类;3)最佳实践包括避免全局变量、优化循环、使用const和let,以及避免过度使用闭包。

Python更适合初学者,学习曲线平缓,语法简洁;JavaScript适合前端开发,学习曲线较陡,语法灵活。1.Python语法直观,适用于数据科学和后端开发。2.JavaScript灵活,广泛用于前端和服务器端编程。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

EditPlus 中文破解版
体积小,语法高亮,不支持代码提示功能

ZendStudio 13.5.1 Mac
功能强大的PHP集成开发环境

DVWA
Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中

螳螂BT
Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

mPDF
mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),