前言:
本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽。
本篇文章为您分析一下 原生JS写拖拽运动
层层深入,到封装插件,请先查阅js 运动函数篇(二)
建议:
如果您是直接查看的本篇博文
请您先翻阅我上一篇关于JS运动的 【js运动函数篇(二)】 里面有详细的关于加速度运动、弹性运动、重力场运动(多方向+碰撞检测+重力加速度+能量损失运动)的解析。
- js运动函数篇(二)链接: https://www.cnblogs.com/qq4297751/p/12812996.html
最好是能把【js运动函数篇(一)】也查看一遍,当然您不看js运动函数篇一 只看js运动函数篇二对您查阅本篇的博文影响也不大
- js运动函数篇(一)链接: https://www.cnblogs.com/qq4297751/p/12790676.html
HTML结构
<div id="demo"></div>
CSS样式
#demo {
position: absolute;
left: 0;
top: 0;
background-color: red;
width: 100px;
height: 100px;
border-radius: 50%;
}
页面效果如下
JS行为
JS分析
- 我们想要鼠标按下移动小球时,再抬起鼠标,他会顺着你移动的方向滚动
- 主要是在js运动函数篇(二)中重力场运动中改变iSpeedX和iSpeedY中的两个速度方向
- 因此函数还是那个startMove函数,只是我们现在需要给他传入两个方向的速度值(iSpeedX,iSpeedY),而不是直接写死
function startMove(dom) {
clearInterval(dom.timer);
// 第一步: 定义横向运动速度
// var iSpeedX = 6;
// 1.1 定义纵向运动速度
// var iSpeedY = 8;
// 第五步: 定义一个重力加速度
var g = 3;
// 第六步: 定义一个损耗
var u = 0.8;
// 开启定时器
dom.timer = setInterval(function () {
// 第五步: 5.1 纵向速度每次加当前的重力
iSpeedY += g;
// 第二步: 新newLeft的位置 = 物体offsetLeft的当前位置 + 横向速度
var newLeft = dom.offsetLeft + iSpeedX;
// 2.1 新newLeft的位置 = 物体offsetLeft的当前位置 + 横向速度
var newTop = dom.offsetTop + iSpeedY;
// 第四步: 判断边界
// 如果当前的 newTop值 >= 浏览器窗口 - 当前元素的高度 (触碰到界面边框)
if (newTop >= document.documentElement.clientHeight - dom.clientHeight) {
// 4.1 方向要改变
iSpeedY *= -1;
// 第七步: 每次碰撞都会受到能量的损耗 不管横向纵向都会受到能量损耗
iSpeedY *= u;
iSpeedX *= u;
// 4.2 设置当前的newTop值 如果不设置,它会超出一些边界
newTop = document.documentElement.clientHeight - dom.clientHeight;
}
if (newTop <= 0) {
// 4.3 方向要改变
iSpeedY *= -1;
// 第七步: 每次碰撞都会受到能量的损耗 不管横向纵向都会受到能量损耗
iSpeedY *= u;
iSpeedX *= u;
// 4.4 设置当前的newTop值 如果不设置,它会超出一些边界
newTop = 0;
}
// 4.5 如果当前的 newTop值 >= 浏览器窗口 - 当前元素的宽度 (触碰到界面边框)
if (newLeft >= document.documentElement.clientWidth - dom.clientWidth) {
// 4.6 方向要改变
iSpeedX *= -1;
// 第七步: 每次碰撞都会受到能量的损耗 不管横向纵向都会受到能量损耗
iSpeedY *= u;
iSpeedX *= u;
// 4.7 设置当前的newLeft值 如果不设置,它会超出一些边界
newLeft = document.documentElement.clientWidth - dom.clientWidth;
}
if (newLeft <= 0) {
// 4.8 方向要改变
iSpeedX *= -1;
// 第七步: 每次碰撞都会受到能量的损耗 不管横向纵向都会受到能量损耗
iSpeedY *= u;
iSpeedX *= u;
// 4.9 设置当前的newLeft值 如果不设置,它会超出一些边界
newLeft = 0;
}
// 第三步: 设置当前的位置
console.log(iSpeedX, iSpeedY);
dom.style.left = newLeft + 'px';
dom.style.top = newTop + 'px';
}, 30);
}
下面为它注册事件
大致原理
1.鼠标按下事获取当前鼠标位置
2.鼠标移动时获取当前元素距离浏览器窗口左边和上边的距离
3.鼠标抬起时,将获取到的元素位置传入startMove函数中
var oDiv = document.getElementById('demo');
// 新第一步: 定义初始iSpeedX值
var iSpeedX = 0;
// 新第一步: 定义初始iSpeedY值
var iSpeedY = 0;
// 新第二步: 注册鼠标按下事件,传入事件参数e
oDiv.onmousedown = function (e) {
var event = event || e;
// clientX 事件属性返回当事件被触发时鼠标指针向对于浏览器页面(或客户区)的水平坐标。alert("X 坐标: " + x + ", Y 坐标: " + y)
// offsetLeft 总结一下: 就是子盒子到定位的父盒子边框到边框的距离
// 新第四步: 事件兼容获取当前元素位置
// disX就是你鼠标当前点击元素的位置到你这个元素左边边的位置
var disX = event.clientX - this.offsetLeft;
// disY就是你鼠标当前点击元素的位置到你这个元素上边边的位置
var disY = event.clientY - this.offsetTop;
// 新第十步: 10.1 保存当前的this
var self = this;
// 新第五步: 注册鼠标移动事件 传入事件参数e
document.onmousemove = function (e) {
// 新第五步: 事件兼容
var event = event || e;
// 新第六步: 最新的newLeft距离 = 当前元素距离浏览器左边框的距离
var newLeft = event.clientX - disX;
// 新第六步: 最新的newTop距离 = 当前元素距离浏览器上边框的距离
var newTop = event.clientY - disY;
// 新第七步: 设置最新的left值
self.style.left = newLeft + 'px';
// 新第七步: 设置最新的top值
self.style.top = newTop + 'px';
}
// 新第八步: 注册鼠标抬起事件
document.onmouseup = function () {
// 新第九步: 移动事件设为null
document.onmousemove = null;
// 新第九步: 抬起事件设为null
document.onmouseup = null;
// 新第十步: 鼠标抬起之后传入参数
startMove(self, iSpeedX, iSpeedY);
}
}
页面效果如下
接下来就是最关键一点
如何获取鼠标抬起时的点
为了看清楚小球移动的轨迹,下面在小球移动时模拟一个小球的运动轨迹
在小球移动事件中添加下面的代码
为了能更清楚看清小球的运动轨迹,我们可以把小球暂时设置为正方形
如何获取鼠标抬起时的点
为了看清楚小球移动的轨迹,下面在小球移动时模拟一个小球的运动轨迹
在小球移动事件中添加下面的代码
为了能更清楚看清小球的运动轨迹,我们可以把小球暂时设置为正方形
// 第十一步
document.onmousemove = function (e) {
// 新第五步: 事件兼容
var event = event || e;
// 新第六步: 最新的newLeft距离 = 当前元素距离浏览器左边框的距离
var newLeft = event.clientX - disX;
// 新第六步: 最新的newTop距离 = 当前元素距离浏览器上边框的距离
var newTop = event.clientY - disY;
iSpeedX = newLeft - lastX;
iSpeedY = newTop - lastY;
lastX = newLeft;
lastY = newTop;
// 第十一步 开始: 模拟实现小球运动的轨迹
var oSpan = document.createElement('span');
oSpan.style.position = 'absolute';
oSpan.style.left = newLeft + 'px';
oSpan.style.top = newTop + 'px';
oSpan.style.width = '5px';
oSpan.style.height = '5px';
oSpan.style.backgroundColor = 'black';
oSpan.style.borderRadius = '50%';
document.body.appendChild(oSpan);
// 第十一步 结束: 模拟实现小球运动的轨迹
// 新第七步: 设置最新的left值
self.style.left = newLeft + 'px';
// 新第七步: 设置最新的top值
self.style.top = newTop + 'px';
}
页面效果如下
因为我们时时刻刻要记录最新的 newLeft 和 newTop
所以我们也要时刻记录一下上一个点的位置
先来看看分析图
所以我们也要时刻记录一下上一个点的位置
先来看看分析图
下面进行代码编写
// 新第十二步 到 新第十四步
var oDiv = document.getElementById('demo');
// 新第十二步: 记录上一个点的位置,默认是0; (跟你初始的定位left值一样)
var lastX = 0;
// 新第十二步: 记录上一个点的位置,默认是0; (跟你初始的定位top值一样)
var lastY = 0;
// 新第一步: 定义初始iSpeedX值
var iSpeedX = 0;
// 新第一步: 定义初始iSpeedY值
var iSpeedY = 0;
// 新第二步: 注册鼠标按下事件,传入事件参数e
oDiv.onmousedown = function (e) {
clearInterval(this.timer);
// 新第三步: 事件兼容
var event = event || e;
// clientX 事件属性返回当事件被触发时鼠标指针向对于浏览器页面(或客户区)的水平坐标。alert("X 坐标: " + x + ", Y 坐标: " + y)
// offsetLeft 总结一下: 就是子盒子到定位的父盒子边框到边框的距离
// 新第四步: 事件兼容获取当前元素位置
// disX就是你鼠标当前点击元素的位置到你这个元素左边边的位置
var disX = event.clientX - this.offsetLeft;
// disY就是你鼠标当前点击元素的位置到你这个元素上边边的位置
var disY = event.clientY - this.offsetTop;
var self = this;
// clientX 事件属性返回当事件被触发时鼠标指针向对于浏览器页面(或客户区)的水平坐标。alert("X 坐标: " + x + ", Y 坐标: " + y)
// console.log(event.clientX, event.clientY);
// offsetLeft 总结一下: 就是子盒子到定位的父盒子边框到边框的距离
// console.log(this.offsetLeft, this.offsetTop);
// console.log(disX, disY);
// 新第五步: 注册鼠标移动事件 传入事件参数e
document.onmousemove = function (e) {
// 新第五步: 事件兼容
var event = event || e;
// 新第六步: 最新的newLeft距离 = 当前元素距离浏览器左边框的距离
var newLeft = event.clientX - disX;
// 新第六步: 最新的newTop距离 = 当前元素距离浏览器上边框的距离
var newTop = event.clientY - disY;
// 新第十三步: 获取当前的横向速度
iSpeedX = newLeft - lastX;
// 新第十三步: 获取当前的纵向速度
iSpeedY = newTop - lastY;
// 新第十四步: 保存上一个点(更新一下,并不一定会用到)
lastX = newLeft;
// 新第十四步: 保存上一个点(更新一下,并不一定会用到)
lastY = newTop;
// 第十一步 开始: 模拟实现小球运动的轨迹
var oSpan = document.createElement('span');
oSpan.style.position = 'absolute';
oSpan.style.left = newLeft + 'px';
oSpan.style.top = newTop + 'px';
oSpan.style.width = '5px';
oSpan.style.height = '5px';
oSpan.style.backgroundColor = 'black';
oSpan.style.borderRadius = '50%';
document.body.appendChild(oSpan);
// 第十一步 结束: 模拟实现小球运动的轨迹
// 新第七步: 设置最新的left值
self.style.left = newLeft + 'px';
// 新第七步: 设置最新的top值
self.style.top = newTop + 'px';
}
document.onmouseup = function () {
document.onmousemove = null;
document.onmouseup = null;
startMove(self, iSpeedX, iSpeedY);
console.log(self)
}
}
页面效果如下
但是当我们在他运动的过程中再次点击他时,会发现有两股力量驱动它的运动
BUG缠身
BUG缠身
直接在onmousedown事件里添加clearInter(this.timer)
BUG缠身
BUG缠身
结语
整完!!!