首先,在HTML文件中添加一个浮窗按钮的元素,如下所示:
<div class="float-button">浮窗按钮</div>
然后,在CSS文件中设置该元素的样式,使其显示在页面的固定位置,如右下角:
.float-button {
position: fixed;
bottom: 20px;
right: 20px;
width: 100px;
height: 40px;
background-color: #f00;
color: #fff;
text-align: center;
line-height: 40px;
cursor: pointer;
}
接下来,使用JavaScript来实现浮窗按钮的长按移动效果。首先,监听按钮元素的touchstart
事件,当长按时开始监听touchmove
事件;当手指离开屏幕时,停止监听touchmove
事件
var floatButton = document.querySelector('.float-button');
var isMoving = false;
var startX = 0;
var startY = 0;
floatButton.addEventListener('touchstart', function(e) {
e.preventDefault();
isMoving = true;
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
});
floatButton.addEventListener('touchmove', function(e) {
e.preventDefault();
if (!isMoving) return;
var offsetX = e.touches[0].clientX - startX;
var offsetY = e.touches[0].clientY - startY;
floatButton.style.transform = 'translate(' + offsetX + 'px, ' + offsetY + 'px)';
});
floatButton.addEventListener('touchend', function(e) {
e.preventDefault();
isMoving = false;
});
但是,这么写得话会有一个bug,第二次移动时按钮回到了原来的初始位置,主要问题是因为在touchmove
事件中,计算的偏移量是相对于起始位置的偏移量,并且每次移动都是基于上一次的位置进行计算。所以,在第二次移动时,起始位置是上一次移动结束后的位置,而不是初始位置。
为解决这个问题,可以将起始位置设为固定值,而不是每次移动结束后的位置。即,在touchstart
事件中,将起始位置设置为固定值,如按钮元素的初始位置。
var floatButton = document.querySelector('.float-button');
var floatButtonRect = floatButton.getBoundingClientRect(); // 获取按钮元素的矩形信息
var isMoving = false;
var btnWidth = floatButtonRect.width;
var btnHeight = floatButtonRect.height;
// 让元素每次处于中心位置
var startX = floatButtonRect.left + btnWidth / 2;
var startY = floatButtonRect.top + btnHeight / 2;
var startX = floatButtonRect.left;
var startY = floatButtonRect.top;
floatButton.addEventListener('touchstart', function(e) {
e.preventDefault();
isMoving = true;
});
floatButton.addEventListener('touchmove', function(e) {
e.preventDefault();
if (!isMoving) return;
var offsetX = e.touches[0].clientX - startX;
var offsetY = e.touches[0].clientY - startY;
floatButton.style.transform = 'translate(' + offsetX + 'px, ' + offsetY + 'px)';
});
floatButton.addEventListener('touchend', function(e) {
e.preventDefault();
isMoving = false;
});
通过这样修改后,无论进行多次移动,按钮的起始位置都是固定的初始位置,而不会发生偏移。这样,按钮就能在长按移动时始终跟随手指移动。
优化:解决点击事件失效问题;解决超过边缘范围问题
新增需求:浮窗按钮移动结束后,回到边缘位置
var floatButton = document.querySelector('.float-button');
var floatButtonRect = floatButton.getBoundingClientRect(); // 获取按钮元素的矩形信息
var isMoving = false;
var btnWidth = floatButtonRect.width; // 浮窗的宽度
var btnHeight = floatButtonRect.height; // 浮窗的高度
// 获取屏幕宽度和高度
var screenWidth = window.innerWidth;
var screenHeight = window.innerHeight;
//让浮窗按钮跟着手指居中显示
var startX = floatButtonRect.left + btnWidth / 2;
var startY = floatButtonRect.top + btnHeight / 2;
var pageX, pageY, offsetX, offsetY
floatButton.addEventListener('touchstart', (e) => {
// e.preventDefault();
isMoving = true;
});
floatButton.addEventListener('touchmove', (e) => {
e.preventDefault();
if (!isMoving) return;
pageX = e.touches[0].clientX
pageY = e.touches[0].clientY
// 处理浮窗按钮左右边缘位置
offsetX = pageX + btnWidth / 2 >= screenWidth ? screenWidth - btnWidth / 2 - startX :
pageX < btnWidth / 2 ? btnWidth / 2 - startX : pageX - startX;
// 处理浮窗按钮上下边缘位置
offsetY = pageY + btnHeight / 2 >= screenHeight ? screenHeight - btnHeight / 2 -
startY : pageY < btnHeight / 2 ? btnHeight / 2 - startY : pageY - startY;
floatButton.style.transform = 'translate(' + offsetX + 'px, ' + offsetY + 'px)';
});
floatButton.addEventListener('touchend', (e) => {
// e.preventDefault();
isMoving = false;
// 计算按钮回到屏幕边缘的偏移量
offsetX = pageX > screenWidth / 2 ? screenWidth - btnWidth / 2 - startX : btnWidth / 2 - startX
// 设置按钮的最终位置
floatButton.style.transform = 'translate(' + offsetX + 'px, ' + offsetY + 'px)';
});
注意:touchstart
、touchmove
和touchend
事件在移动端触发,对于PC端可以使用mousedown
、mousemove
和mouseup
事件替代。