vue封装一个气泡框,气泡角可自定义位置

54 阅读1分钟

因为很简单就不介绍了,直接上代码,直接可以用。

<template>
    <div class="bubble-box">
        <slot></slot>
    </div>
</template>

<script setup>
import { onMounted } from 'vue';

const { position, stroke, angle, color } = defineProps({
    position: { type: Array, default: ['bottom', '50%'] }, // [位置, 偏移量(百分比)]
    stroke: { type: String, default: '0.05' }, // 描边粗细(rem)
    angle: { type: String, default: '0.5' }, // 角的大小(rem)
    color: { type: Array, default: ['#ffecd0', 'linear-gradient(180deg, #fffdfc 0%, #fffaf0 100%)'] } // [描边颜色, 背景颜色]
});

const getBubbleStyle = () => {
    const getCornerStyle = (direction) => {
        const styles = {
            bottom: `
                top: 100%;
                left: ${position[1]};
                transform: translateX(-50%);
                border-color: ${color[0]} transparent transparent transparent;
            `,
            top: `
                bottom: 100%;
                left: ${position[1]};
                transform: translateX(-50%);
                border-color: transparent transparent ${color[0]} transparent;
            `,
            left: `
                right: 100%;
                top: ${position[1]};
                transform: translateY(-50%);
                border-color: transparent ${color[0]} transparent transparent;
            `,
            right: `
                left: 100%;
                top: ${position[1]};
                transform: translateY(-50%);
                border-color: transparent transparent transparent ${color[0]};
            `
        };
        return styles[direction] || '';
    };

    const getAfterStyle = (direction) => {
        const borderColor = color[1].match(/(#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{3})([0-9A-Fa-f]{2})?|rgb(a?)\(\d{1,3}, \d{1,3}, \d{1,3}(?:, ?\d?(\.\d+)?)?\)|hsl(a?)\(\d{1,3}, \d{1,3}%, \d{1,3}%(?:, ?\d?(\.\d+)?)?\))/i)[0];
        const borderWidth = Number(stroke) + 0.03;
        const styles = {
            bottom: `
                margin-top: -${borderWidth}rem;
                border-color: ${borderColor} transparent transparent transparent;
            `,
            top: `
                margin-bottom: -${borderWidth}rem;
                border-color: transparent transparent ${borderColor} transparent;
            `,
            left: `
                margin-right: -${borderWidth}rem;
                border-color: transparent ${borderColor} transparent transparent;
            `,
            right: `
                margin-left: -${borderWidth}rem;
                border-color: transparent transparent transparent ${borderColor};
            `
        };
        return styles[direction] || '';
    };

    return `
        .bubble-box {
            position: relative;
            background: ${color[1]};
            border: ${stroke}rem solid ${color[0]};
            border-radius: 0.4rem;
        }

        .bubble-box::before,
        .bubble-box::after {
            content: '';
            position: absolute;
            border-width: ${angle}rem;
            border-style: solid;
            pointer-events: none;
        }

        .bubble-box::before {
            ${getCornerStyle(position[0])}
        }

        .bubble-box::after {
            ${getCornerStyle(position[0])}
            ${getAfterStyle(position[0])}
        }
    `;
};

onMounted(() => {
    const style = document.createElement('style');
    style.innerHTML = getBubbleStyle();
    document.head.appendChild(style);
});
</script>