current position:Home>Come on, Vue move the end suspension ball assembly

Come on, Vue move the end suspension ball assembly

2022-05-15 02:14:03Maple moving forward

Description of each parameter

##  attribute 

dragableRange:  Drag range .parent  Parent  window  Visual window 

dragable:  Can I drag . Default  true

position: initial position .[object Object] top、left、right、bottom

indent: Whether you need to indent . Default  false

indentDelayTime: Delay indent . Company :ms,  by  0  Do not indent 

indentDistance: Indent distance . Company :px

needNearEdge: Whether trimming is required after dragging the floating ball . Default :false

nearEdgeTransition:  Welt transition animation ,transition  Property value . Default :'all ease 0.3s'

nearEdgeDirection: The welt direction after dragging the suspension ball . The default trimming direction is the nearest direction .

indentNearEdge: Whether the suspension ball needs to be indented after trimming ( At this time, the indent direction is the direction of the welt ). Default  false

indentNearEdgeDelay:  Delayed retraction after floating ball welt . Company :ms, Default  1000, by  0  Then there is no delay 

##  event 

clickFunc: Callback event after clicking the floating ball 

## slots

slotsDirection: Slot content orientation , By default, it is placed to the right of the floating ball ( If it is not set and turned on  needNearEdge, Then the direction will be changed automatically according to the trimming condition )

Component implementation

<template>
	<div
		class="suspend_ball_wrapper"
		:style="{
			position: dragableRange === 'window' ? 'fixed' : 'absolute',
			...position,
		}"
	>
		<div
			v-if="dragable"
			class="suspend_ball"
			@touchstart="touchstart"
			@touchmove="touchmove"
			@touchend="touchend"
		>
			<div class="slots" :class="[slotsDirectionData]">
				<slot />
			</div>
		</div>
		<div v-else class="suspend_ball">
			<div class="slots" :class="[slotsDirectionData]">
				<slot />
			</div>
		</div>
	</div>
</template>
<script>
export default {
	data() {
		return {
			left: 0,
			top: 0,
			touchType: 0, // Touch type  1 Drag the levitation ball  0 Click on the levitation ball 
			indentTimer: null,
			indentNearEdgeTimer: null,
			slotsDirectionData: "right",
		};
	},
	props: {
		dragable: {
			type: Boolean,
			default: true,
		},
		dragableRange: {
			type: String,
			default: "parent",
		},
		position: {
			type: Object,
			default: function () {
				return {
					left: 0,
					top: 0,
				};
			},
		},
		indent: {
			type: Boolean,
			default: false,
		},
		indentDelayTime: {
			type: Number,
			default: 1000,
		},
		indentDistance: {
			type: Number,
			default: 30,
		},
		indentNearEdge: {
			type: Boolean,
			default: false,
		},
		needNearEdge: {
			type: Boolean,
			default: false,
		},
		nearEdgeTransition: {
			type: String,
			default: "all ease 0.3s",
		},
		nearEdgeDirection: String,
		indentNearEdgeDelay: {
			type: Number,
			default: 1000,
		},
		slotsDirection: {
			type: String,
			default: "right",
		},
	},
	created() {
		this.slotsDirection && (this.slotsDirectionData = this.slotsDirection);
	},
	mounted() {
		if (this.indent && this.indentDelayTime) {
			this.setIndent();
		}
	},
	methods: {
		touchstart(e) {
			const el = this.$el;

			e.preventDefault();
			// Touch type  1 Drag the levitation ball  0 Click on the levitation ball 
			this.touchType = 0;
			this.$el.style.transition = "none";
			this.clearAllTimeout();

			// The coordinates when the finger is pressed 
			const starX = e.touches[0].clientX;
			const starY = e.touches[0].clientY;

			this.starX = starX;
			this.starY = starY;

			// The position of the finger relative to the element itself 
			this.distanceX = starX - el.offsetLeft;
			this.distanceY = starY - el.offsetTop;
		},
		touchmove(e) {
			// The coordinates where the finger moves 
			const moveX = e.touches[0].clientX;
			const moveY = e.touches[0].clientY;

			// Finger movement distance 
			const moveXDistance = moveX - this.starX;
			const moveYDistance = moveY - this.starY;

			// Set location 
			let left = moveX - this.distanceX;
			let top = moveY - this.distanceY;

			// Handle the slight movement caused by clicking 
			if (Math.abs(moveXDistance) > 15 || Math.abs(moveYDistance) > 15) {
				this.touchType = 1;
			}

			this.validMove(left, top);
		},
		touchend() {
			if (this.touchType === 0) {
				this.$emit("touchFunc");
			}

			// Whether you need to indent 
			if (this.needNearEdge) {
				const {
					self_offsetWidth,
					self_offsetHeight,
					parent_offsetWidth,
					parent_offsetHeight,
				} = this.getSelfAndParentOffset();

				const obj = {
					left: parseFloat(this.$el.style.left),
					top: parseFloat(this.$el.style.top),
				};

				obj.right = parent_offsetWidth - obj.left - self_offsetWidth;
				obj.bottom = parent_offsetHeight - obj.top - self_offsetHeight;

				// Minimum value in each direction 
				let minKey = "";

				// Calculate trim direction 
				if (this.nearEdgeDirection) {
					minKey = this.nearEdgeDirection;
				} else {
					let min = Math.min();
					for (const key in obj) {
						const val = parseFloat(obj[key]);
						if (val < min) {
							min = val;
							minKey = key;
						}
					}
				}

				// Set welt transition 
				if (this.nearEdgeTransition) {
					this.$el.style.transition = this.nearEdgeTransition;
				}

				// Trim settings 
				this.$el.style[minKey] = 0;

				// Set the indent after trimming 
				if (this.indentNearEdge) {
					if (this.indentNearEdgeDelay !== 0) {
						clearTimeout(this.indentNearEdgeTimer);

						this.indentNearEdgeTimer = setTimeout(() => {
							this.$el.style[minKey] = -this.indentDistance + "px";
							this.setOtherDirectionVal(minKey, this.indentDistance);
						}, this.indentNearEdgeDelay);
					} else {
						this.$el.style[minKey] = -this.indentDistance + "px";
					}
				}

				this.setOtherDirectionVal(minKey);
			}
		},

		// Move 
		validMove(left, top) {
			const {
				self_offsetWidth,
				self_offsetHeight,
				parent_offsetWidth,
				parent_offsetHeight,
			} = this.getSelfAndParentOffset();

			if (left + self_offsetWidth > parent_offsetWidth) {
				left = parent_offsetWidth - self_offsetWidth;
			}
			if (top + self_offsetHeight > parent_offsetHeight) {
				top = parent_offsetHeight - self_offsetHeight;
			}
			if (left <= 0) {
				left = 0;
			}
			if (top <= 0) {
				top = 0;
			}

			this.$el.style.left = left + "px";
			this.$el.style.top = top + "px";
			this.left = left;
			this.top = top;
			this.right = "auto";
			this.bottom = "auto";
		},

		// Set indent 
		setIndent(delayTime) {
			clearTimeout(this.indentTimer);
			this.indentTimer = setTimeout(() => {
				let direction;
				let el = this.$el;
				const positionObj = this.getPositionComputedStyle();
				for (const key in positionObj) {
					if (parseFloat(positionObj[key]) === 0) {
						direction = key;
					}
				}

				el.style[direction] = el.style[direction] - this.indentDistance + "px";
			}, delayTime || this.indentDelayTime);
		},

		// Set other directions position value 
		setOtherDirectionVal(minKey, indentDistance = 0) {
			const {
				parent_offsetWidth,
				parent_offsetHeight,
				self_offsetWidth,
				self_offsetHeight,
			} = this.getSelfAndParentOffset();

			let slotsDirection = "";

			switch (minKey) {
				case "left":
					this.$el.style["right"] =
						parent_offsetWidth - self_offsetWidth + indentDistance + "px";
					slotsDirection = "right";
					break;
				case "right":
					this.$el.style["left"] =
						parent_offsetWidth - self_offsetWidth + indentDistance + "px";
					slotsDirection = "left";
					break;
				case "top":
					this.$el.style["bottom"] =
						parent_offsetHeight - self_offsetHeight + indentDistance + "px";
					slotsDirection = "bottom";
					break;
				case "bottom":
					this.$el.style["top"] =
						parent_offsetHeight - self_offsetHeight + indentDistance + "px";
					slotsDirection = "top";
					break;
			}

			// Set the slot orientation ( Not set slotsDirection Then automatically judge )
			if (!this.slotsDirection) {
				this.slotsDirectionData = slotsDirection;
			}
		},

		// Calculation offset value 
		getSelfAndParentOffset() {
			const ifwindowRange = this.dragableRange === "window";
			const parent_offsetWidth = !ifwindowRange
				? this.$el.parentNode.offsetWidth
				: window.innerWidth;
			const parent_offsetHeight = !ifwindowRange
				? this.$el.parentNode.offsetHeight
				: window.innerHeight;
			const self_offsetWidth = this.$el.offsetWidth;
			const self_offsetHeight = this.$el.offsetHeight;
			return {
				parent_offsetWidth,
				parent_offsetHeight,
				self_offsetWidth,
				self_offsetHeight,
			};
		},

		getPositionComputedStyle() {
			let el = this.$el;
			let style = getComputedStyle(el);
			const positionObj = {
				top: style.top,
				bottom: style.bottom,
				left: style.left,
				right: style.right,
			};
			return positionObj;
		},

		clearAllTimeout() {
			clearTimeout(this.indentTimer);
			clearTimeout(this.indentNearEdgeTimer);
		},
	},
};
</script>
<style lang="less" scoped>
.suspend_ball_wrapper {
	position: absolute;
	// top: 0;
	// left: 0;
	width: 60px;
	height: 60px;
	z-index: 1000;
	.suspend_ball {
		width: 100%;
		height: 100%;
		border-radius: 50%;
		background-size: cover;
		background-image: url("https://img0.baidu.com/it/u=272572278,3446974957&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500");
		.slots {
			position: absolute;
			width: fit-content;
			height: 100%;
			&.right {
				left: calc(100% + 10px);
			}
			&.left {
				right: calc(100% + 10px);
			}
			&.top {
				bottom: calc(100% + 10px);
			}
			&.bottom {
				top: calc(100% + 10px);
			}
		}
	}
}
</style>

effect

 

copyright notice
author[Maple moving forward],Please bring the original link to reprint, thank you.
https://en.qdmana.com/2022/132/202205120524479702.html

Random recommended