拖拽实现(原生JS+Vue)
·
文章目录
基础拖拽功能实现(原生JS)
- e.offsetX是鼠标事件对象的一个属性,表示鼠标相对于触发事件的元素左上角的水平坐标
- e.clientX是鼠标事件对象的一个属性,表示 鼠标指针相对于浏览器视口左上角的水平坐标
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
div {
width: 100px;
height: 100px;
background-color: red;
box-shadow: 10px 0 0 0 grey;
position: fixed;
}
</style>
</head>
<body>
<div></div>
<script>
let div = document.querySelector("div");
let flag = false; //引入这个变量,是为了让鼠标松开时小方块不在跟着移动
div.addEventListener("mousedown", (e) => {
flag = true;
console.log(flag)
let x = e.offsetX;
let y = e.offsetY;
document.addEventListener("mousemove", (e) => {
console.log("移动中")
//这个值表示窗口的总高度-小方块的高度后的留白区域的高度
let h = window.innerHeight - div.offsetHeight;
let w = window.innerWidth - div.offsetWidth;
let div_left = e.clientX - x;
let div_top = e.clientY - y;
//限定边界,元素只能在视口内移动,不会超出边界
div_top = Math.min(Math.max(0, div_top), h);
div_left=Math.min(Math.max(0, div_left), w);
if (flag) {
div.style.top = div_top + "px";
div.style.left = div_left + "px";
}
});
});
div.addEventListener("mouseup", () => {
flag = false;
console.log(flag)
});
</script>
</body>
</html>
Vue中封装拖拽组件
在我的项目中,写了一个ai小助手,有一个可拖拽的小图标,点击后是一个小屏幕的聊天窗口,这个小窗口也是可以拖拽的,类似于豆包,点击全屏可展示全屏聊天。因此封装了一个组件。
- v-bind=“$attrs” 表示事件透传,会将所有未在组件 props 中定义的属性和事件自动绑定到根元素上。 因为在悬浮小图变种又一个鼠标移入显示一个小提示,鼠标移出不显示提示的小功能,但是小屏聊天框不需要该功能
- 添加hasDragged变量来控制拖拽结束后是否触发点击事件
组件代码
<template>
//绑定鼠标按下,松开,点击的事件。
<div class="draggable-container"
:style="{ position: 'fixed', top: `${position.y}px`, left: `${position.x}px`, ...containerStyle }"
@mousedown="handleDragStart" @mouseup="handleDragEnd" @click="handleClick" v-bind="$attrs">
<slot></slot>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue'
const props = defineProps<{
initialPosition: {
x: number,
y: number
}
containerStyle: Record<string, string>
preventClickOnDrag: boolean
}>()
const emit = defineEmits<{ (e: 'click'): void }>()
//判断是否是图拽事件,防止拖拽时触发点击事件
const hasDragged = ref(false)
const handleClick = () => {
console.log(isDragging.value, hasDragged.value, props.preventClickOnDrag)
if (!hasDragged.value || !props.preventClickOnDrag) {
emit('click')
}
}
//初始位置
const position = ref({ ...props.initialPosition })
//是否在拖拽
const isDragging = ref(false)
//鼠标相对于元素左上角的位置(拖拽偏移量)
const dragOffset = ref({ x: 0, y: 0 })
//初始化位置
const initPosition = () => {
position.value = { ...props.initialPosition }
}
//开始拖拽
const handleDragStart = (e) => {
console.log("开始拖拽")
isDragging.value = true;
hasDragged.value = false;
dragOffset.value = {
x: e.offsetX,
y: e.offsetY,
}
}
//正在拖拽,同步元素的位置
const handleDrag = (e) => {
if (isDragging.value) {
console.log("正在拖拽")
hasDragged.value = true;
console.log(parseInt(props.containerStyle.width))
position.value = {
x: Math.min(Math.max(0, e.clientX - dragOffset.value.x), window.innerWidth - parseInt(props.containerStyle.width)),
y: Math.min(Math.max(0, e.clientY - dragOffset.value.y), window.innerHeight - parseInt(props.containerStyle.height)),
}
console.log(position.value.x)
}
}
//拖拽结束
const handleDragEnd = () => {
isDragging.value = false;
}
onMounted(() => {
initPosition()
document.addEventListener('mousemove', handleDrag)
})
</script>
<style scoped>
.draggable-container {
z-index: 9999;
}
</style>
引用组件
<!-- 悬浮按钮 -->
<DraggableContainer v-if="!isOpen" @click="openChat" @mouseenter="showTooltip = true"
@mouseleave="showTooltip = false" :initialPosition="buttonPosition" :preventClickOnDrag="true"
:container-style="{ width: '60px', height: '60px' }" class="chat-button">
<div class="content"></div>
</DraggableContainer>
<!--小屏聊天框-->
<DraggableContainer v-else-if="!isFullscreen" @click.stop :initialPosition="dialogPosition"
:preventClickOnDrag="false" :container-style="{ width: '600px', height: '720px' }" class="chat-dialog">
<!-- 小屏对话框 -->
<div>
</div>
</DraggableContainer>
// 拖动功能
const buttonPosition = ref({
x: window.innerWidth - 90,
y: window.innerHeight - 90
})
const dialogPosition = ref({
x: (window.innerWidth - 700) / 2,
y: (window.innerHeight - 620) / 2
})
// // 初始化按钮位置
const initPosition = () => {
buttonPosition.value = {
x: window.innerWidth - 90,
y: window.innerHeight - 90
}
dialogPosition.value = {
x: (window.innerWidth - 600) / 2,
y: (window.innerHeight - 720) / 2
}
}
onMounted(() => {
console.log(window.innerWidth)
initPosition()
})
const openChat = () => {
isOpen.value = true
}
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)