Vue3气泡卡片(Popover)

2024-03-28 13:36

本文主要是介绍Vue3气泡卡片(Popover),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

效果如下图:在线预览

在这里插入图片描述

APIs

参数说明类型默认值必传
title卡片标题string | slot‘’false
content卡片内容string | slot‘’false
maxWidth卡片内容最大宽度string | number‘auto’false
trigger卡片触发方式‘hover’ | ‘click’‘hover’false
overlayStyle卡片样式CSSProperties{}false

Events

事件名称说明参数
openChange显示隐藏的回调(visible: boolean) => void

注:组件引用方法 import { rafTimeout, cancelRaf } from ‘…/index’ 请参考该博客

创建气泡卡片组件Popover.vue

<script setup lang="ts">
import { ref, computed, useSlots } from 'vue'
import type { Slot, CSSProperties } from 'vue'
import { rafTimeout, cancelRaf } from '../index'
interface Props {title?: string|Slot // 卡片标题content?: string|Slot // 卡片内容maxWidth?: string|number // 卡片内容最大宽度trigger?: 'hover'|'click' // 卡片触发方式overlayStyle?: CSSProperties // 卡片样式
}
const props = withDefaults(defineProps<Props>(), {title: '',content: '',maxWidth: 'auto',trigger: 'hover',overlayStyle: () => ({})
})
const popMaxWidth = computed(() => {if (typeof props.maxWidth === 'number') {return props.maxWidth + 'px'}return props.maxWidth
})
const visible = ref(false)
const top = ref(0) // 提示框top定位
const left = ref(0) // 提示框left定位
const defaultRef = ref() // 声明一个同名的模板引用
const popRef = ref() // 声明一个同名的模板引用
function getPosition () {const defaultWidth = defaultRef.value.offsetWidth // 展示文本宽度const popWidth = popRef.value.offsetWidth // 提示文本宽度const popHeight = popRef.value.offsetHeight // 提示文本高度top.value = popHeight + 4left.value = (popWidth - defaultWidth) / 2
}
const emit = defineEmits(['openChange'])
const hideTimer = ref()
function onShow () {getPosition()cancelRaf(hideTimer.value)visible.value = trueemit('openChange', visible.value)
}
function onHide (): void {hideTimer.value = rafTimeout(() => {visible.value = falseemit('openChange', visible.value)}, 100)
}
const activeBlur = ref(false) // 是否激活 blur 事件
function onOpen () {visible.value = !visible.valueif (visible.value) {getPosition()}emit('openChange', visible.value)
}
function onEnter () {activeBlur.value = false
}
function onLeave () {activeBlur.value = truepopRef.value.focus()
}
function onBlur () {visible.value = falseemit('openChange', false)
}
</script>
<template><divclass="m-popover"@mouseenter="trigger === 'hover' ? onShow() : () => false"@mouseleave="trigger === 'hover' ? onHide() : () => false"><divref="popRef"tabindex="1"class="m-pop-content":class="{'show-pop': visible}":style="`max-width: ${popMaxWidth}; top: ${-top}px; left: ${-left}px;`"@blur="trigger === 'click' && activeBlur ? onBlur() : () => false"@mouseenter="trigger === 'hover' ? onShow() : () => false"@mouseleave="trigger === 'hover' ? onHide() : () => false"><div class="m-pop" :style="overlayStyle"><div class="m-title"><slot name="title">{{ title }}</slot></div><div class="m-content"><slot name="content">{{ content }}</slot></div></div><div class="m-pop-arrow"><span class="u-pop-arrow"></span></div></div><divref="defaultRef"@click="trigger === 'click' ? onOpen() : () => false"@mouseenter="trigger === 'click' ? onEnter() : () => false"@mouseleave="trigger === 'click' ? onLeave() : () => false"><slot></slot></div></div>
</template>
<style lang="less" scoped>
.m-popover {position: relative;display: inline-block;.m-pop-content {position: absolute;z-index: 999;width: max-content;padding-bottom: 12px;outline: none;pointer-events: none;opacity: 0;transform-origin: 50% 75%;transform: scale(.8);transition: transform .25s, opacity .25s;.m-pop {min-width: 32px;min-height: 32px;padding: 12px;font-size: 14px;color: rgba(0, 0, 0, .88);line-height: 1.5714285714285714;text-align: start;text-decoration: none;word-break: break-all;cursor: auto;user-select: text;background-color: #FFF;border-radius: 8px;box-shadow: 0 6px 16px 0 rgba(0, 0, 0, .08), 0 3px 6px -4px rgba(0, 0, 0, .12), 0 9px 28px 8px rgba(0, 0, 0, .05);.m-title {min-width: 176px;margin-bottom: 8px;color: rgba(0, 0, 0, .88);font-weight: 600;}.m-content {color: rgba(0, 0, 0, .88);}}.m-pop-arrow {position: absolute;z-index: 9;left: 50%;bottom: 12px;transform: translateX(-50%) translateY(100%) rotate(180deg);display: block;pointer-events: none;width: 16px;height: 16px;overflow: hidden;&::before {position: absolute;bottom: 0;inset-inline-start: 0;width: 16px;height: 8px;background-color: #FFF;clip-path: path('M 0 8 A 4 4 0 0 0 2.82842712474619 6.82842712474619 L 6.585786437626905 3.0710678118654755 A 2 2 0 0 1 9.414213562373096 3.0710678118654755 L 13.17157287525381 6.82842712474619 A 4 4 0 0 0 16 8 Z');content: "";}&::after {position: absolute;width: 8.970562748477143px;height: 8.970562748477143px;bottom: 0;inset-inline: 0;margin: auto;border-radius: 0 0 2px 0;transform: translateY(50%) rotate(-135deg);box-shadow: 3px 3px 7px rgba(0, 0, 0, 0.1);z-index: 0;background: transparent;content: "";}}}.show-pop {pointer-events: auto;opacity: 1;transform: scale(1);}
}
</style>

在要使用的页面引入

<script setup lang="ts">
function openChange (visible: boolean) {console.log('visible:', visible)
}
</script>
<template><div class="ml60"><h1>{{ $route.name }} {{ $route.meta.title }}</h1><h2 class="mt30 mb10">基本使用</h2><Popover title="Title" @open-change="openChange"><template #content><p>Content</p><p>Content</p></template><Button type="primary">Hover me</Button></Popover><h2 class="mt30 mb10">不同的触发方式</h2><Popover title="Title" trigger="click"><template #content><p>Content</p><p>Content</p></template><Button type="primary">Click me</Button></Popover><h2 class="mt30 mb10">自定义样式</h2><Popovertitle="TitleTitleTitleTitleTitleTitleTitleTitleTitle":max-width="240":overlayStyle="{ padding: '12px 18px', borderRadius: '12px' }"><template #content><p>Content</p><p>Content</p></template><Button type="primary">Hover me</Button></Popover></div>
</template>

这篇关于Vue3气泡卡片(Popover)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/855753

相关文章

HTML5中的Microdata与历史记录管理详解

《HTML5中的Microdata与历史记录管理详解》Microdata作为HTML5新增的一个特性,它允许开发者在HTML文档中添加更多的语义信息,以便于搜索引擎和浏览器更好地理解页面内容,本文将探... 目录html5中的Mijscrodata与历史记录管理背景简介html5中的Microdata使用M

html5的响应式布局的方法示例详解

《html5的响应式布局的方法示例详解》:本文主要介绍了HTML5中使用媒体查询和Flexbox进行响应式布局的方法,简要介绍了CSSGrid布局的基础知识和如何实现自动换行的网格布局,详细内容请阅读本文,希望能对你有所帮助... 一 使用媒体查询响应式布局        使用的参数@media这是常用的

HTML5表格语法格式详解

《HTML5表格语法格式详解》在HTML语法中,表格主要通过table、tr和td3个标签构成,本文通过实例代码讲解HTML5表格语法格式,感兴趣的朋友一起看看吧... 目录一、表格1.表格语法格式2.表格属性 3.例子二、不规则表格1.跨行2.跨列3.例子一、表格在html语法中,表格主要通过< tab

Vue3组件中getCurrentInstance()获取App实例,但是返回null的解决方案

《Vue3组件中getCurrentInstance()获取App实例,但是返回null的解决方案》:本文主要介绍Vue3组件中getCurrentInstance()获取App实例,但是返回nu... 目录vue3组件中getCurrentInstajavascriptnce()获取App实例,但是返回n

JS+HTML实现在线图片水印添加工具

《JS+HTML实现在线图片水印添加工具》在社交媒体和内容创作日益频繁的今天,如何保护原创内容、展示品牌身份成了一个不得不面对的问题,本文将实现一个完全基于HTML+CSS构建的现代化图片水印在线工具... 目录概述功能亮点使用方法技术解析延伸思考运行效果项目源码下载总结概述在社交媒体和内容创作日益频繁的

前端CSS Grid 布局示例详解

《前端CSSGrid布局示例详解》CSSGrid是一种二维布局系统,可以同时控制行和列,相比Flex(一维布局),更适合用在整体页面布局或复杂模块结构中,:本文主要介绍前端CSSGri... 目录css Grid 布局详解(通俗易懂版)一、概述二、基础概念三、创建 Grid 容器四、定义网格行和列五、设置行

前端下载文件时如何后端返回的文件流一些常见方法

《前端下载文件时如何后端返回的文件流一些常见方法》:本文主要介绍前端下载文件时如何后端返回的文件流一些常见方法,包括使用Blob和URL.createObjectURL创建下载链接,以及处理带有C... 目录1. 使用 Blob 和 URL.createObjectURL 创建下载链接例子:使用 Blob

Vuex Actions多参数传递的解决方案

《VuexActions多参数传递的解决方案》在Vuex中,actions的设计默认只支持单个参数传递,这有时会限制我们的使用场景,下面我将详细介绍几种处理多参数传递的解决方案,从基础到高级,... 目录一、对象封装法(推荐)二、参数解构法三、柯里化函数法四、Payload 工厂函数五、TypeScript

Vue3使用router,params传参为空问题

《Vue3使用router,params传参为空问题》:本文主要介绍Vue3使用router,params传参为空问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录vue3使用China编程router,params传参为空1.使用query方式传参2.使用 Histo

CSS Padding 和 Margin 区别全解析

《CSSPadding和Margin区别全解析》CSS中的padding和margin是两个非常基础且重要的属性,它们用于控制元素周围的空白区域,本文将详细介绍padding和... 目录css Padding 和 Margin 全解析1. Padding: 内边距2. Margin: 外边距3. Padd