vue3 - popper.js的简单使用
popper.js
官网:https://popper.js.org/docs/v2/modifiers/offset/
npm 安装:
npm i @popperjs/core
element-ui 弹出层底层用的就是这玩意儿
背景:
不见得所有库,都会兼容 vue3,总有一些场景,不会被 vue3 覆盖。
场景1:GIS 开发,open-layer 是一个纯 js 的库,代码封装的时候,更倾向于通过 document.createElement() 直接构建弹出层。
场景2:设计全局的相册功能,希望全局复用同一个弹出层,不希望增减 dom 节点。
需要设计的内容
popper.js 已经封装了很多内容,使用的时候需要考虑下列内容:
1、设计气泡窗口的角标,自己不会写没事,直接使用 element-ui 的设计;
2、添加更合理的点击事件,窗口弹出后,点击任意位置需要关闭窗口;
Javascript代码封装
封装了大部分需要注意的内容。
ts 语法实现,javascript 环境下代码通用。
import {createPopper} from "@popperjs/core";
import {Instance, OptionsGeneric} from "@popperjs/core/lib/types";
/**
* 可复用的气泡窗口
*
* 用更直接的方式操作 dom,而不使用 vue 进行控制
*
* 场景:
* - 窗口内容不变,但是依附组件会发生变化的情况下使用
*
* 原理:
* - 基于 popover.js,与 el-popover 底层一致
*/
export class Popper {
/**
* 窗口实例
*/
public instance: Instance | undefined;
/**
* 气泡窗口 dom 对象
*/
public popover: HTMLElement | undefined;
/**
* 气泡窗口依附的 dom 对象
*/
public reference: HTMLElement | undefined;
/**
* 气泡窗口参数设置
*/
public option: OptionsGeneric = {};
/**
* 简单的调测日志,用于检测组件的状态变化,popper.js 高级应用会用到
*/
private post_placement: string | undefined;
private logger: TModifier = {
phase: 'main',
enabled: true,
name: 'logger',
fn: ({state}: { state: any }) => {
// 方向的变化
if (this.post_placement !== state.placement) {
this.post_placement = state.placement;
console.log(this.post_placement);
}
// 角标的位置信息
const {arrow} = state.elements;
if (arrow) {
console.log(state.modifiersData.arrow);
if (state.modifiersData.arrow.centerOffset !== 0) {
arrow.setAttribute('data-hide', '');
} else {
arrow.removeAttribute('data-hide');
}
}
},
};
/**
* 增加日志检测
*/
public pushLogger(): void {
if (!this.options.modifiers) {
this.options.modifiers = [];
}
this.option.modifiers.push(this._logger);
}
/**
* 显示气泡窗口
*/
public display(): void {
if (this.popover) {
// don't worry about the issue of attributes cannot be obtained when displaying.
this.popover.classList.remove('hidden');
}
}
/**
* 隐藏气泡窗口
*/
public hidden(): void {
if (this.popover) {
this.popover.classList.add('hidden');
}
}
/**
* 初始化气泡窗口,重新绑定窗口和引用之间的关系
*/
public create(): Instance {
this.display();
// destroy the popover if it exists.
if (this.instance) {
this.instance.destroy();
}
// Pass the button, the tooltip, and some options, and Popper will do the
// magic positioning for you:
this.instance = createPopper(this.reference!, this.popover!, this.option);
return this.instance;
}
/**
* 窗口点击事件,除了触发组件和弹窗组件,任何点击事件都要关闭窗口
* @param evt 事件对象
*/
public windowsEvent = (evt: Event): void => {
if (this.instance) {
const target: EventTarget = evt.target;
if (!this.popover.contains(target as Node) && !this.reference?.contains(target as Node)) {
this.hidden();
}
}
}
}
简单 vue3 组件封装
使用场景:
在各类 change、click 事件中,将 evt.target 传递到当前组件,使窗口自动吸附到触发事件的对象。
样式说明:
- sea-popper* 样式可以替换成 .el-popper .el-popper__arrow;
- hidden 效果就是隐藏组件:display: none;
<template>
<div ref="tooltipRef" role="tooltip" class="sea-popper hidden">
<i class="sea-popper__arrow" data-popper-arrow></i>
<slot></slot>
</div>
</template>
<script setup lang="ts">
import {onMounted, onUnmounted, ref, watch} from "vue";
import type {Placement} from "@popperjs/core/lib/enums";
import {OptionsGeneric} from "@popperjs/core/lib/types";
import {Popper} from "./use-popover"
/**
* 气泡弹窗
*
* 场景:
* - 窗口内容不变,但是依附组件会发生变化的情况下使用
*/
interface Props {
// 需要吸附的组件,注意这是 dom 组件,使用时将 evt.target 传递到当前组件
reference?: HTMLElement;
// 窗口位置
placement?: Placement;
// 窗口偏移量,一般无需调整,eg:[0, 8]
offset?: number[];
}
// 使用 withDefaults 设置默认值
const props = withDefaults(defineProps<Props>(), {placement: 'bottom-start', offset: [0, 8]})
// reference
const tooltipRef = ref();
// popover-options
const option: OptionsGeneric = {}
option.placement = props.placement;
if (props.offset != null) {
let arr = option.modifiers;
if (arr == null) {
arr = [];
option.modifiers = arr;
}
arr.push({name: 'offset', options: {offset: props.offset}});
}
const popper: Popper = new Popper();
popper.option = option;
// 依附组件发生变化的时候,重新绑定组件之间的关系
watch(() => props.reference, (val) => {
popper.reference = val;
popper.create();
})
onMounted(() => {
popper.popover = tooltipRef.value
window.addEventListener('click', popper.windowsEvent);
})
onUnmounted(() => {
window.removeEventListener('click', popper.windowsEvent);
})
</script>
疯狂的妞妞 :每一天,做什么都好,不要什么都不做!
浙公网安备 33010602011771号