要先引入 “vue3-draggable-resizable”: “^1.6.5”
1.创建DragComponent组件
<template>
<!-- 抽屉组件 -->
<div class="drag-container" id="dragBox" :style="{ zIndex: zIndex }">
<Vue3DraggableResizable :initW="64" :initH="64" v-model:x="x" v-model:y="y"
:parent="false" :resizable="false" :draggable="true" class="drag-item" @drag-start="handleDragStart"
@drag-end="handleDragEnd">
<div class="home-icon-box" ref="draggableElement">
<img :src="market_set.image" class="img" alt="" />
</div>
</Vue3DraggableResizable>
</div>
</template>
<script setup>
import {
ref,
onMounted,
onUnmounted
} from 'vue';
import Vue3DraggableResizable from 'vue3-draggable-resizable';
import 'vue3-draggable-resizable/dist/Vue3DraggableResizable.css';
import {
getMarket
} from "@/api/global.js"
const popupRef = ref(null)
const x = ref(20);
const y = ref();
const initialY = ref(0); // 基准Y坐标,用于计算滚动偏移
const isDragging = ref(false); //是否正在拖拽
const dragStartPosition = ref({
x: 0,
y: 0
}); // 拖拽起始位置
const isLock = ref(true);
const isShow = ref(false)
// 初始化基准Y坐标
const initBaseY = () => {
initialY.value = y.value - window.scrollY;
};
function isElementInViewport(el) {
const rect = el.getBoundingClientRect();
// 获取视口的高度和宽度
const windowHeight = (window.innerHeight || document.documentElement.clientHeight) - 50;
const windowWidth = (window.innerWidth || document.documentElement.clientWidth);
// 判断元素是否在视口内
const vertInView = (rect.top <= windowHeight) && ((rect.top + rect.height) >= 0);
const horInView = (rect.left <= windowWidth) && ((rect.left + rect.width) >= 0);
return (vertInView && horInView);
}
// 拖动结束后更新基准Y坐标
const handleDragEnd = (e) => {
// initialY.value = y.value - window.scrollY;
// 判断拖拽还是点击事件
if (isDragging.value) {
const dragDistance = Math.sqrt(
Math.pow(e.x - dragStartPosition.value.x, 2) +
Math.pow(e.y - dragStartPosition.value.y, 2)
);
if (dragDistance < 5) {
// 如果拖拽距离小于 5px,则认为是点击事件
console.log('点击事件', e);
isLock.value = true
} else {
console.log('拖拽结束', e);
isLock.value = false
}
}
isDragging.value = false;
// 判断悬浮球是靠近左边或右边
const {
x: newX,
y: newY
} = e; // 假设位置属性是 x 和
const page = document.querySelector('#app');
const iconBox = document.querySelector('.home-icon-box');
// alert(page.offsetWidth)
if (newX + (iconBox.offsetWidth / 2) < page.offsetWidth / 2) {
x.value = 0; // 靠左
} else {
x.value = page.offsetWidth - 64; // 靠右
}
const myElement = document.querySelector('.home-icon-box');
if (isElementInViewport(myElement)) {
console.log('元素在视口内');
} else {
console.log('元素不在视口内');
y.value = window.innerHeight / 2
}
};
const handleDragStart = (e) => {
// 可根据需要添加拖动开始时的逻辑
isDragging.value = true;
dragStartPosition.value = {
x: e.x,
y: e.y
};
};
// 计算按钮初始y位置
const containerWidth = ref(0); // 容器宽度
const width = ref(100); // 组件宽度
// 计算初始 x 坐标(从右对齐)
const calculateInitialX = () => {
x.value = containerWidth.value - width.value;
y.value = (window.innerHeight || document.documentElement.clientHeight) - 200
};
// 生命周期
onMounted(() => {
initBaseY();
const container = document.querySelector('#app');
if (container) {
containerWidth.value = container.offsetWidth; //活动区域的宽度范围
calculateInitialX();
}
})
</script>
<style lang="scss" scoped>
.dragBox {
width: 960rpx;
height: 100vh;
position: fixed;
top: 0;
left: 50%;
z-index: 99;
transform: translateX(-50%);
pointer-events: none;
color: red;
}
.home-icon-box {
position: relative;
// left: 23px;
// top: 23px;
width: 128rpx;
height: 128rpx;
display: flex;
justify-content: center;
align-items: center;
z-index: 999;
color: #fff;
cursor: grab;
/* 鼠标样式为抓取 */
user-select: none;
/* 防止拖拽时选中文本 */
animation: pulse 1.5s infinite ease-in-out;
transition: opacity 0.3s ease, left 0.3s ease, right 0.3s ease, top 0.3s ease;
/* 按钮动画 */
@keyframes pulse {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.2);
opacity: 0.7;
}
100% {
transform: scale(1);
opacity: 1;
}
}
/* 进度条容器 */
.progress-container-icon {
position: absolute;
width: 80%;
height: 10px;
background: rgba(255, 255, 255, 0.5);
border-radius: 5px;
overflow: hidden;
text-align: center;
cursor: pointer;
bottom: -14px;
}
/* 进度条 */
.progress-bar-icon {
width: 0%;
height: 100%;
background: #ff5722;
border-radius: 5px;
}
/* 进度文本 */
.progress-text-icon {
position: absolute;
width: 100%;
text-align: center;
top: 50%;
transform: translateY(-50%);
color: black;
font-size: 10px;
font-weight: bold;
}
.img {
width: 100%;
height: 100%;
}
}
.drag-item {
width: 64px;
height: 64px !important;
touch-action: none;
border: none !important;
}
.drag-container {
display: inline-block;
position: fixed;
top: 0;
z-index: 99;
}
.dragBoxPopup {
position: fixed;
top: 0;
left: calc(50% - min(50%, 240px));
width: min(100%, 480px);
z-index: 999;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
}
}
</style>
2.createDrag.js 将组件dom创建到app根组件下
import { createVNode, render } from 'vue';
import DragComponent from '@/components/DragComponent/DragComponent.vue';
// 修改后的函数,负责渲染 DragComponent 和 DragComponent2
export default function createDrags() {
// 创建一个组件的 DOM 容器
const container1 = document.createElement('div');
document.getElementById('app').appendChild(container1);
// 创建一个组件的虚拟节点
const vnode1 = createVNode(DragComponent, {
// 如果需要,可以在这里传递其他 props 给 DragComponent
});
// 渲染一个组件
render(vnode1, container1);
const instance1 = vnode1.component?.proxy;
// 返回两个组件的实例(如果需要后续操作)
return {
dragComponentInstance: instance1
};
}
在App.vue中执行js
import createDrag from "@/utils/createDrag.js"
onLaunch: function(options) {
createDrag()
}