纯JS,无插件
需求:页面指定位置放一个悬浮小图标。可以点击拖拽改变位置。
封装了一个悬浮小组件,slot插入要显示的内容
PC端:
javascript
<template>
<div
class="draggable-div"
ref="draggable"
:style="{
width: width || null,
height: height || null,
top: top || null,
right: right || null,
bottom: bottom || null,
left: left || null,
}"
>
<slot></slot>
</div>
</template>
<script>
export default {
props: {
left: {
type: Number | String
},
right: {
type: Number | String
},
bottom: {
type: Number | String
},
top: {
type: Number | String
},
width: {
type: Number | String
},
height: {
type: Number | String
}
},
data () {
return {
isDragOver : false,
transform: {
offsetX: 0,
offsetY: 0
},
}
},
mounted() {
this.initDraggable()
},
methods:{
initDraggable(){
document.addEventListener('mousedown', this.onMousedown);
},
offDraggable() {
document.removeEventListener('mousedown', null);
},
onMousedown(e) {
if( !this.$refs.draggable || !this.$refs.draggable.contains(e.target) ){
return
}
this.isDragOver = false
const downX = e.clientX;
const downY = e.clientY;
const { offsetX, offsetY } = this.transform;
const targetRect = this.$refs.draggable.getBoundingClientRect();
const targetLeft = targetRect.left;
const targetTop = targetRect.top;
const targetWidth = targetRect.width;
const targetHeight = targetRect.height;
const clientWidth = document.documentElement.clientWidth;
const clientHeight = document.documentElement.clientHeight;
const minLeft = -targetLeft + offsetX;
const minTop = -targetTop + offsetY;
const maxLeft = clientWidth - targetLeft - targetWidth + offsetX;
const maxTop = clientHeight - targetTop - targetHeight + offsetY;
const onMousemove = (e) => {
const moveX = Math.min(
Math.max(offsetX + e.clientX - downX, minLeft),
maxLeft
);
const moveY = Math.min(
Math.max(offsetY + e.clientY - downY, minTop),
maxTop
);
this.isDragOver = Math.max(Math.abs(moveX),Math.abs(moveY))>=5
this.transform = {
offsetX: moveX,
offsetY: moveY
};
this.$refs.draggable.style.transform = `translate(${this.addUnit(
moveX
)}, ${this.addUnit(moveY)})`;
};
const onMouseup = (e) => {
// console.log('onMouseup' ,this.isDragOver,e)
document.removeEventListener('mousemove', onMousemove);
document.removeEventListener('mouseup', onMouseup);
};
const onMouseClick = (e) => {
if (!this.isDragOver) {
this.$emit('click')
}
e.target.removeEventListener('click', onMouseClick);
};
document.addEventListener('mousemove', onMousemove, { passive: true });
document.addEventListener('mouseup', onMouseup);
e.target.addEventListener('click', onMouseClick);
e.preventDefault();
e.stopPropagation();
},
addUnit(value, defaultUnit = 'px') {
if (!value) return '';
if (typeof value === 'string') {
return value;
} else if (typeof value === 'number') {
return `${value}${defaultUnit}`;
}
},
},
destroyed() {
this.offDraggable()
},
};
</script>
<style scoped lang="scss">
.draggable-div {
position: fixed;
width: fit-content;
height: fit-content;
z-index: 10000;
user-select: none;
}
</style>
移动端:
javascript
<template>
<div
ref="floatDrag"
class="float-position"
:style="{
width: Width + 'px',
height: Height + 'px',
left: left + 'px',
top: top + 'px',
right: right + 'px !important',
zIndex: zIndex
}"
@touchmove.prevent
@mousemove.prevent
>
<slot></slot>
</div>
</template>
<script>
export default {
name: "floatButton",
data () {
return {
clientWidth: null,
clientHeight: null,
left: null,
top: null,
right: null,
timer: null,
currentTop: 0
}
},
props: {
distanceRight: {
type: Number,
default: 20
},
distanceBottom: {
type: Number,
default: 200
},
distanceTop: {
type: Number,
default: 20
},
isScrollHidden: {
type: Boolean,
default: false
},
isCanDraggable: {
type: Boolean,
default: true
},
Width: {
type: Number,
default: 48
},
Height: {
type: Number,
default: 48
},
zIndex: {
type: Number,
default: 50
},
value: {
type: String,
default: '悬浮按钮'
}
},
mounted () {
this.isCanDraggable &&
this.$nextTick(() => {
this.floatDrag = this.$refs.floatDrag;
// 获取元素位置属性
this.floatDragDom = this.floatDrag.getBoundingClientRect();
// 设置初始位置
// this.left = this.clientWidth - this.floatDragDom.width - this.distanceRight;
this.right = this.distanceRight
this.top = this.distanceTop
// this.top = this.clientHeight - this.floatDragDom.height - this.distanceBottom;
this.initDraggable();
});
// this.isScrollHidden && window.addEventListener('scroll', this.handleScroll);
window.addEventListener('resize', this.handleResize);
},
created () {
this.clientWidth = document.documentElement.clientWidth;
this.clientHeight = document.documentElement.clientHeight;
},
BeforeDestroy () {
window.removeEventListener('resize', this.handleResize);
},
methods: {
/**
* 窗口resize监听
*/
handleResize () {
this.checkDraggablePosition();
},
/**
* 初始化draggable
*/
initDraggable () {
this.floatDrag.addEventListener('touchstart', this.toucheStart);
this.floatDrag.addEventListener('touchmove', (e) => this.touchMove(e));
this.floatDrag.addEventListener('touchend', this.touchEnd);
},
toucheStart () {
this.canClick = false;
this.floatDrag.style.transition = 'none';
},
touchMove (e) {
this.canClick = true;
if (e.targetTouches.length === 1) {
// 单指拖动
let touch = event.targetTouches[0];
this.left = touch.clientX - this.floatDragDom.width / 2;
this.top = touch.clientY - this.floatDragDom.height / 2;
}
},
touchEnd () {
if (!this.canClick) return; // 解决点击事件和touch事件冲突的问题
this.floatDrag.style.transition = 'all 0.3s';
this.checkDraggablePosition();
},
/**
* 判断元素显示位置
* 在窗口改变和move end时调用
*/
checkDraggablePosition () {
this.clientWidth = document.documentElement.clientWidth;
this.clientHeight = document.documentElement.clientHeight;
if (this.left + this.floatDragDom.width / 2 >= this.clientWidth / 2) {
// 判断位置是往左往右滑动
this.left = this.clientWidth - this.floatDragDom.width - 20;
} else {
this.left = 20;
}
if (this.top < 0) {
// 判断是否超出屏幕上沿
this.top = 20;
}
if (this.top + this.floatDragDom.height >= this.clientHeight) {
// 判断是否超出屏幕下沿
this.top = this.clientHeight - this.floatDragDom.height - 20;
}
}
}
}
</script>
<style scoped lang="scss">
.float-position {
position: fixed;
right: 0;
top: 60%;
/*width: 170px;*/
/*height: 180px;*/
display: flex;
align-items: center;
justify-content: center;
user-select: none;
.content {
border-radius: 50%;
position: relative;
padding: 0.8em;
display: flex;
align-content: center;
justify-content: center;
}
.close {
width: 20px;
height: 20px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
position: absolute;
right: 30px;
top: -12px;
cursor: pointer;
}
}
.cart {
border-radius: 50%;
width: 5em;
height: 5em;
display: flex;
align-items: center;
justify-content: center;
}
.header-notice {
display: inline-block;
transition: all 0.3s;
span {
vertical-align: initial;
}
.notice-badge {
color: inherit;
.header-notice-icon {
font-size: 16px;
padding: 4px;
}
}
}
.drag-ball .drag-content {
overflow-wrap: break-word;
font-size: 14px;
color: #fff;
letter-spacing: 2px;
}
</style>