前端开发:创建可拖动的固定位置 `<div>` 和自动隐藏悬浮按钮

13 篇文章 0 订阅
11 篇文章 1 订阅

在前端开发中,实现一个可拖动的固定位置 <div>,并且根据拖动的状态控制其显示和隐藏,同时在特定条件下显示悬浮按钮,涉及以下技术和原理:

技术细节和实现步骤:

1. HTML 结构:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Draggable Fixed Div with Auto Hide and Floating Button</title>
<style>
  /* CSS 样式,定义拖拽元素和悬浮按钮的样式 */
</style>
</head>
<body>

<!-- 拖拽的固定位置的 div -->
<div id="draggable">
  <p>Drag me!</p>
  <button>Click me</button>
</div>

<!-- 默认显示的悬浮按钮 -->
<div id="dragButton">Show</div>

<!-- JavaScript 脚本,实现拖拽、自动隐藏和按钮显示逻辑 -->
<script>
  // JavaScript 代码部分
</script>

</body>
</html>

2. CSS 样式:

#draggable {
  width: 200px;
  height: 200px;
  background-color: #ccc;
  position: fixed;
  top: 50px;
  left: 50px;
  touch-action: none; /* 禁止浏览器默认行为,如滚动 */
  transition: transform 0.3s ease;
}

#draggable.hidden {
  transform: scale(0);
  visibility: hidden;
}

#dragButton {
  position: fixed;
  bottom: 20px;
  right: 20px;
  background-color: #007bff;
  color: white;
  padding: 10px 20px;
  cursor: pointer;
}

        • #draggable 样式:定义拖拽元素的基本样式,包括固定位置、尺寸、背景色等。

        • .hidden 类样式:用于控制拖拽元素隐藏时的动画效果。

        • #dragButton 样式:定义悬浮按钮的基本样式,包括位置、背景色、文字颜色等。

3. JavaScript 交互:

const draggable = document.getElementById('draggable');
const dragButton = document.getElementById('dragButton');
let offsetX, offsetY;
let isDragging = false;
let autoHideTimeout;
let lastVisiblePosition = { top: 50, left: 50 }; // 初始位置

// 触摸事件处理
draggable.addEventListener('touchstart', touchStart);
draggable.addEventListener('touchmove', touchMove);
draggable.addEventListener('touchend', touchEnd);

// 鼠标事件处理
draggable.addEventListener('mousedown', mouseDown);
document.addEventListener('mousemove', mouseMove);
document.addEventListener('mouseup', mouseUp);

// 初始隐藏拖拽元素
draggable.classList.add('hidden');

function touchStart(e) {
  const touch = e.touches[0];
  offsetX = touch.clientX - draggable.getBoundingClientRect().left;
  offsetY = touch.clientY - draggable.getBoundingClientRect().top;
  isDragging = true;
}

function touchMove(e) {
  if (!isDragging) return;

  e.preventDefault(); // 阻止默认的滚动行为

  const touch = e.touches[0];
  let newX = touch.clientX - offsetX;
  let newY = touch.clientY - offsetY;

  // 限制拖动范围在可视区域内
  const maxX = document.documentElement.clientWidth - draggable.offsetWidth;
  const maxY = document.documentElement.clientHeight - draggable.offsetHeight;
  newX = Math.max(0, Math.min(newX, maxX));
  newY = Math.max(0, Math.min(newY, maxY));

  draggable.style.left = `${newX}px`;
  draggable.style.top = `${newY}px`;

  // 判断靠近边缘并自动贴边
  if (newX < 10) {
    draggable.style.left = '0';
  } else if (newX > maxX - 10) {
    draggable.style.left = `${maxX}px`;
  }
  if (newY < 10) {
    draggable.style.top = '0';
  } else if (newY > maxY - 10) {
    draggable.style.top = `${maxY}px`;
  }

  // 更新最后显示位置
  lastVisiblePosition = { top: parseInt(draggable.style.top), left: parseInt(draggable.style.left) };

  // 自动隐藏计时器重置
  clearTimeout(autoHideTimeout);
  draggable.classList.remove('hidden');
  dragButton.style.display = 'none';
}

function touchEnd() {
  isDragging = false;

  // 设置自动隐藏
  autoHideTimeout = setTimeout(() => {
    draggable.classList.add('hidden');
    // 固定悬浮按钮位置
    dragButton.style.top = '20px';
    dragButton.style.right = '20px';
    dragButton.style.display = 'block';
  }, 5000); // 5秒后自动隐藏
}

function mouseDown(e) {
  offsetX = e.clientX - draggable.getBoundingClientRect().left;
  offsetY = e.clientY - draggable.getBoundingClientRect().top;
  isDragging = true;
}

function mouseMove(e) {
  if (!isDragging) return;

  e.preventDefault(); // 阻止默认的拖动行为

  let newX = e.clientX - offsetX;
  let newY = e.clientY - offsetY;

  // 限制拖动范围在可视区域内
  const maxX = document.documentElement.clientWidth - draggable.offsetWidth;
  const maxY = document.documentElement.clientHeight - draggable.offsetHeight;
  newX = Math.max(0, Math.min(newX, maxX));
  newY = Math.max(0, Math.min(newY, maxY));

  draggable.style.left = `${newX}px`;
  draggable.style.top = `${newY}px`;

  // 判断靠近边缘并自动贴边
  if (newX < 10) {
    draggable.style.left = '0';
  } else if (newX > maxX - 10) {
    draggable.style.left = `${maxX}px`;
  }
  if (newY < 10) {
    draggable.style.top = '0';
  } else if (newY > maxY - 10) {
    draggable.style.top = `${maxY}px`;
  }

  // 更新最后显示位置
  lastVisiblePosition = { top: parseInt(draggable.style.top), left: parseInt(draggable.style.left) };

  // 自动隐藏计时器重置
  clearTimeout(autoHideTimeout);
  draggable.classList.remove('hidden');
  dragButton.style.display = 'none';
}

function mouseUp() {
  isDragging = false;

  // 设置自动隐藏
  autoHideTimeout = setTimeout(() => {
    draggable.classList.add('hidden');
    // 固定悬浮按钮位置
    dragButton.style.top = '20px';
    dragButton.style.right = '20px';
    dragButton.style.display = 'block';
  }, 5000); // 5秒后自动隐藏
}

dragButton.addEventListener('click', () => {
  draggable.classList.remove('hidden');
  dragButton.style.display = 'none';
});

技术和原理分析:

• HTML 结构:使用 <div> 元素创建拖拽的固定位置容器和悬浮按钮,配合 CSS 控制其样式。

• CSS 样式:定义了拖拽元素和悬浮按钮的基本样式,包括位置、背景色等,利用 transition 属性实现元素显示隐藏的动画效果。

• JavaScript 交互:

• 拖拽实现:通过 touchstart, touchmove, touchend, mousedown, mousemove, mouseup 等事件监听器,实现移动设备和桌面设备上的拖拽功能。在拖动过程中,更新拖拽元素的位置,并根据距离屏幕边缘的距离自动贴边。

• 自动隐藏和按钮显示逻辑:利用 setTimeout 实现拖拽元素在一定时间内无操作后自动隐藏,并显示固定位置的悬浮按钮。点击悬浮按钮则重新显示拖拽元素。可以根据需要选择显示固定位置或者根据最后显示位置动态调整的悬浮按钮。

最后完整代码:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Draggable Fixed Div with Auto Hide and Floating Button</title>
<style>
  #draggable {
    width: 200px;
    height: 200px;
    background-color: #ccc;
    position: fixed;
    top: 50px;
    left: 50px;
    touch-action: none; /* 禁止浏览器默认行为,如滚动 */
    transition: transform 0.3s ease;
  }

  #draggable.hidden {
    transform: scale(0);
    visibility: hidden;
  }

  /* 默认固定悬浮按钮 */
  #dragButton {
    position: fixed;
    bottom: 20px;
    right: 20px;
    background-color: #007bff;
    color: white;
    padding: 10px 20px;
    cursor: pointer;
  }

  /* 根据最后显示位置调整的悬浮按钮 */
  /*
  #dragButton.dynamic {
    position: fixed;
    background-color: #007bff;
    color: white;
    padding: 10px 20px;
    cursor: pointer;
    display: none;
  }
  */
</style>
</head>
<body>

<div id="draggable">
  <p>Drag me!</p>
  <button>Click me</button>
</div>

<div id="dragButton" class="fixed">Show</div>
<!-- <div id="dragButton" class="dynamic">Show</div> -->

<script>
  const draggable = document.getElementById('draggable');
  const dragButton = document.getElementById('dragButton');
  let offsetX, offsetY;
  let isDragging = false;
  let autoHideTimeout;
  let lastVisiblePosition = { top: 50, left: 50 }; // 初始位置

  // 触摸事件处理
  draggable.addEventListener('touchstart', touchStart);
  draggable.addEventListener('touchmove', touchMove);
  draggable.addEventListener('touchend', touchEnd);

  // 鼠标事件处理
  draggable.addEventListener('mousedown', mouseDown);
  document.addEventListener('mousemove', mouseMove);
  document.addEventListener('mouseup', mouseUp);

  draggable.classList.add('hidden');
  
  function touchStart(e) {
    const touch = e.touches[0];
    offsetX = touch.clientX - draggable.getBoundingClientRect().left;
    offsetY = touch.clientY - draggable.getBoundingClientRect().top;
    isDragging = true;
  }

  function touchMove(e) {
    if (!isDragging) return;

    e.preventDefault(); // 阻止默认的滚动行为

    const touch = e.touches[0];
    let newX = touch.clientX - offsetX;
    let newY = touch.clientY - offsetY;

    // 限制拖动范围在可视区域内
    const maxX = document.documentElement.clientWidth - draggable.offsetWidth;
    const maxY = document.documentElement.clientHeight - draggable.offsetHeight;
    newX = Math.max(0, Math.min(newX, maxX));
    newY = Math.max(0, Math.min(newY, maxY));

    draggable.style.left = `${newX}px`;
    draggable.style.top = `${newY}px`;

    // 判断靠近边缘并自动贴边
    if (newX < 10) {
      draggable.style.left = '0';
    } else if (newX > maxX - 10) {
      draggable.style.left = `${maxX}px`;
    }
    if (newY < 10) {
      draggable.style.top = '0';
    } else if (newY > maxY - 10) {
      draggable.style.top = `${maxY}px`;
    }

    // 更新最后显示位置
    lastVisiblePosition = { top: parseInt(draggable.style.top), left: parseInt(draggable.style.left) };

    // 自动隐藏计时器重置
    clearTimeout(autoHideTimeout);
    draggable.classList.remove('hidden');
    dragButton.style.display = 'none';
  }

  function touchEnd() {
    isDragging = false;

    // 设置自动隐藏
    autoHideTimeout = setTimeout(() => {
      draggable.classList.add('hidden');
      // 固定悬浮按钮位置
      dragButton.style.top = '20px';
      dragButton.style.right = '20px';
      dragButton.style.display = 'block';
      // 动态悬浮按钮位置
      /*
      dragButton.classList.add('dynamic');
      dragButton.style.top = `${lastVisiblePosition.top}px`;
      dragButton.style.left = `${lastVisiblePosition.left}px`;
      */
    }, 5000); // 5秒后自动隐藏
  }

  function mouseDown(e) {
    offsetX = e.clientX - draggable.getBoundingClientRect().left;
    offsetY = e.clientY - draggable.getBoundingClientRect().top;
    isDragging = true;
  }

  function mouseMove(e) {
    if (!isDragging) return;

    e.preventDefault(); // 阻止默认的拖动行为

    let newX = e.clientX - offsetX;
    let newY = e.clientY - offsetY;

    // 限制拖动范围在可视区域内
    const maxX = document.documentElement.clientWidth - draggable.offsetWidth;
    const maxY = document.documentElement.clientHeight - draggable.offsetHeight;
    newX = Math.max(0, Math.min(newX, maxX));
    newY = Math.max(0, Math.min(newY, maxY));

    draggable.style.left = `${newX}px`;
    draggable.style.top = `${newY}px`;

    // 判断靠近边缘并自动贴边
    if (newX < 10) {
      draggable.style.left = '0';
    } else if (newX > maxX - 10) {
      draggable.style.left = `${maxX}px`;
    }
    if (newY < 10) {
      draggable.style.top = '0';
    } else if (newY > maxY - 10) {
      draggable.style.top = `${maxY}px`;
    }

    // 更新最后显示位置
    lastVisiblePosition = { top: parseInt(draggable.style.top), left: parseInt(draggable.style.left) };

    // 自动隐藏计时器重置
    clearTimeout(autoHideTimeout);
    draggable.classList.remove('hidden');
    dragButton.style.display = 'none';
  }

  function mouseUp() {
    isDragging = false;

    // 设置自动隐藏
    autoHideTimeout = setTimeout(() => {
      draggable.classList.add('hidden');
      // 固定悬浮按钮位置      dragButton.style.right = '20px';
      dragButton.style.display = 'block';
      // 动态悬浮按钮位置
      /*
      dragButton.classList.add('dynamic');
      dragButton.style.top = `${lastVisiblePosition.top}px`;
      dragButton.style.left = `${lastVisiblePosition.left}px`;
      */
    }, 5000); // 5秒后自动隐藏
  }

  dragButton.addEventListener('click', () => {
    draggable.classList.remove('hidden');
    dragButton.style.display = 'none';
    // 动态悬浮按钮位置的显示控制
    // dragButton.classList.remove('dynamic');
  });
</script>

</body>
</html>

这篇博客文章介绍了如何利用 HTML、CSS 和 JavaScript 实现一个具有拖拽、自动隐藏和悬浮按钮的交互效果,适用于创建可定制的用户界面体验。

  • 12
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
非常抱歉,我之前的修正还是有问题。下面是修正后的代码,可以解决放开鼠标后创建多余 `<div>` 的问题: ```html <!DOCTYPE html> <html> <head> <title>MathJax Example</title> <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script> <script src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML"></script> <style> .mathjax-formula { font-size: 2.8em; background-color: green; position: absolute; left: 0; top: 0; cursor: move; padding: 5px; border-radius: 5px; color: white; } </style> </head> <body> <div id="mathjax-formulas-container"></div> <script> var formulas = []; var selectedFormula = null; var offsetX = 0; var offsetY = 0; document.addEventListener("mousedown", function(event) { var target = event.target; if (target.classList.contains("mathjax-formula")) { selectedFormula = target; offsetX = event.clientX - selectedFormula.offsetLeft; offsetY = event.clientY - selectedFormula.offsetTop; } }); document.addEventListener("mousemove", function(event) { if (selectedFormula) { var xDiff = event.clientX - selectedFormula.offsetLeft - offsetX; var yDiff = event.clientY - selectedFormula.offsetTop - offsetY; if (xDiff >= 0 && xDiff <= selectedFormula.offsetWidth && yDiff >= 0 && yDiff <= selectedFormula.offsetHeight) { selectedFormula.style.left = event.clientX - offsetX + "px"; selectedFormula.style.top = event.clientY - offsetY + "px"; } } }); document.addEventListener("mouseup", function(event) { selectedFormula = null; }); document.addEventListener("click", function(event) { var mathjaxFormulasContainer = document.getElementById("mathjax-formulas-container"); var target = event.target; if (target === mathjaxFormulasContainer) { navigator.clipboard.readText().then(function(text) { if (text) { var latexCode = text.trim(); if (latexCode) { var newFormula = document.createElement("div"); newFormula.className = "mathjax-formula"; newFormula.style.left = event.clientX + "px"; newFormula.style.top = event.clientY + "px"; newFormula.innerText = latexCode; mathjaxFormulasContainer.appendChild(newFormula); formulas.push({ latexCode: latexCode, position: { x: event.clientX, y: event.clientY } }); MathJax.Hub.Queue(["Typeset", MathJax.Hub, newFormula]); } } }); } else if (target.classList.contains("mathjax-formula")) { selectedFormula = target; offsetX = event.clientX - selectedFormula.offsetLeft; offsetY = event.clientY - selectedFormula.offsetTop; } }); </script> </body> </html> ``` 现在,当你点击空白区域时才会创建新的 `<div>` 元素,同时拖动已存在的 `<div>` 元素也不会创建多余的元素。非常抱歉之前给你带来的困扰,感谢你的耐心和理解!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值