uniapp 封装 带箭头的气泡框

659 阅读2分钟

效果

image.png

组件属性

属性名描述示例
width组件的宽度字符串:'300rpx'
text文本内容字符串:'这是气泡提示'
arrowSize箭头大小,单位rpx数字:16
arrowDistance箭头距离气泡框的距离字符串:'16rpx'
position箭头在气泡框的位置字符串:'top-left',第一个值是在气泡框的哪条边、第二个值是边的那个位置
background气泡框背景色字符串:'#fff'
shadowColor气泡框阴影颜色字符串:'#eee'
borderColor气泡框边框颜色字符串:'#ddd'
tipStyle气泡框的样式对象:{}

注意

  • tipStyle中设置的背景色、阴影颜色、边框颜色都无法生效,请使用组件提供的属性单独设置(这是为了保证箭头的颜色与气泡框保持一致)
  • 气泡框的内容可以使用默认插槽插入自定义的内容,但是如果使用了插槽,参数text会失效

position 属性值

  • top-left
  • top-center
  • top-right
  • bottom-left
  • bottom-center
  • bottom-right
  • left-top
  • left-center
  • left-bottom
  • right-top
  • right-center
  • right-bottom

代码

<template>
	<div class="tooltip" :style="tipStyle">
		<slot>
			<text>{{ props.text }}</text>
		</slot>
		<div class="before" :style="beforeStyle"></div>
		<div class="after" :style="afterStyle"></div>
	</div>
</template>

<script setup>
	import { ref, onMounted, computed, watchEffect, getCurrentInstance } from 'vue';
	import { onLoad } from '@dcloudio/uni-app';
	const { proxy } = getCurrentInstance();
	const example = proxy;
	const props = defineProps({
		width: {
			type: String,
			default: 'max-content',
		},
		text: {
			type: String,
			default: '这是气泡提示',
		},
		arrowSize: {
			type: Number,
			default: 16,
		},
		arrowDistance: {
			type: String,
			default: '16px',
		},
		position: {
			type: String,
			default: 'top-left',
		},
		background: {
			type: String,
			default: '#fff',
		},
		shadowColor: {
			type: String,
			default: '#eee',
		},
		borderColor: {
			type: String,
			default: '#ddd',
		},
		tipStyle: {
			type: Object,
			default: {},
		},
	});

	onMounted(() => {
		tipStyle.value = {
			width: props.width,
			padding: '20rpx',
			'border-radius': '20rpx',
			border: '2rpx solid',
			...props.tipStyle,
			background: props.background,
			borderColor: props.borderColor,
			filter: `drop-shadow(0 0 10rpx ${props.shadowColor})`,
		};

		let publicStyle = {
			border: `${props.arrowSize}rpx solid transparent`,
			content: '',
			display: 'block',
			width: 0,
			height: 0,
			overflow: 'hidden',
			position: 'absolute',
			'z-index': 101,
		};
		let mainStyle = {
			'z-index': 99,
		};
		if (
			props.position == 'top-left' ||
			props.position == 'top-center' ||
			props.position == 'top-right'
		) {
			publicStyle = {
				...publicStyle,
				top: `-${props.arrowSize}rpx`,
				'border-top': 0,
				'border-bottom-color': props.background,
			};
			switch (props.position) {
				case 'top-left':
					publicStyle = {
						...publicStyle,
						left: props.arrowDistance,
					};
					break;
				case 'top-center':
					publicStyle = {
						...publicStyle,
						left: '50%',
						transform: 'translateX(-50%)',
					};
					break;
				case 'top-right':
					publicStyle = {
						...publicStyle,
						right: props.arrowDistance,
					};
					break;
			}
			mainStyle = {
				...publicStyle,
				...mainStyle,
				top: `-${props.arrowSize + 2}rpx`,
				'border-bottom-color': props.borderColor,
			};
		}
		if (
			props.position == 'bottom-left' ||
			props.position == 'bottom-center' ||
			props.position == 'bottom-right'
		) {
			publicStyle = {
				...publicStyle,
				bottom: `-${props.arrowSize}rpx`,
				'border-bottom': 0,
				'border-top-color': props.background,
			};
			switch (props.position) {
				case 'bottom-left':
					publicStyle = {
						...publicStyle,
						left: props.arrowDistance,
					};
					break;
				case 'bottom-center':
					publicStyle = {
						...publicStyle,
						left: '50%',
						transform: 'translateX(-50%)',
					};
					break;
				case 'bottom-right':
					publicStyle = {
						...publicStyle,
						right: props.arrowDistance,
					};
					break;
			}
			mainStyle = {
				...publicStyle,
				...mainStyle,
				bottom: `-${props.arrowSize + 2}rpx`,
				'border-top-color': props.borderColor,
			};
		}
		if (
			props.position == 'left-top' ||
			props.position == 'left-center' ||
			props.position == 'left-bottom'
		) {
			publicStyle = {
				...publicStyle,
				left: `-${props.arrowSize}rpx`,
				'border-left': 0,
				'border-right-color': props.background,
			};
			switch (props.position) {
				case 'left-top':
					publicStyle = {
						...publicStyle,
						top: props.arrowDistance,
					};
					break;
				case 'left-center':
					publicStyle = {
						...publicStyle,
						top: '50%',
						transform: 'translateY(-50%)',
					};
					break;
				case 'left-bottom':
					publicStyle = {
						...publicStyle,
						bottom: props.arrowDistance,
					};
					break;
			}
			mainStyle = {
				...publicStyle,
				...mainStyle,
				left: `-${props.arrowSize + 2}rpx`,
				'border-right-color': props.borderColor,
			};
		}
		if (
			props.position == 'right-top' ||
			props.position == 'right-center' ||
			props.position == 'right-bottom'
		) {
			publicStyle = {
				...publicStyle,
				right: `-${props.arrowSize}rpx`,
				'border-right': 0,
				'border-left-color': props.background,
			};
			switch (props.position) {
				case 'right-top':
					publicStyle = {
						...publicStyle,
						top: props.arrowDistance,
					};
					break;
				case 'right-center':
					publicStyle = {
						...publicStyle,
						top: '50%',
						transform: 'translateY(-50%)',
					};
					break;
				case 'right-bottom':
					publicStyle = {
						...publicStyle,
						bottom: props.arrowDistance,
					};
					break;
			}
			mainStyle = {
				...publicStyle,
				...mainStyle,
				right: `-${props.arrowSize + 2}rpx`,
				'border-left-color': props.borderColor,
			};
		}

		afterStyle.value = publicStyle;
		beforeStyle.value = mainStyle;
	});
	let tipStyle = ref({});
	let afterStyle = ref('');
	let beforeStyle = ref('');
</script>

<style lang="scss" scoped>
	.tooltip {
		position: relative;
		.before,
		.after {
			position: absolute;
		}
	}
</style>

页面使用

<myTooltip
				position="left-top"
				:text="tipText"
				width="300rpx"
				:arrowSize="10"
				background="#E91E63"
				:tipStyle="{
					'border-radius': '30rpx',
					'font-size': '36rpx',
					'font-weight': 700,
					color: '#fff',
				}"
			>
				<!-- 默认插槽,自定义内容 -->
				<div class="tip-box">
					<image
						src="https://douleoss.oss-cn-shenzhen.aliyuncs.com/test/66236de1e4b0fb16c4b0c307.jpg"
						mode="scaleToFill"
					/>
					<text>{{ tipText }}</text>
				</div>
			</myTooltip>

let tipText=ref('')

气泡框的内容可以使用默认插槽插入自定义的内容