【VUE3.0】动手做一套像素风的前端UI组件库---Message

server/2024/9/25 11:32:47/

目录

  • 引言
  • 自己整一个UI设计稿
  • 代码编写
    • 1. 设计信息窗口基础样式
    • 2. 设置打开和关闭的方法
    • 3. 编写实例化组件的js文件
    • 4. 看下最终效果
    • 5. 组件完整代码
    • 6. 组件调用方式
  • 总结

引言

本教程基于前端UI样式库 NES.css 的UI设计,自行研究复现。欢迎大家交流优化实现方法~

此次组件库开发基于vue3框架,框架基础搭建过程以及基础素材准备参考:【VUE3.0】动手做一套像素风前端UI组件库—先导篇

本篇完成的组件为Message,日常项目中较为常见的组件,主要涉及到的内容有:

  • 这次没得 借鉴了,NES.css没有提供这个组件的样式模板。
  • 参考按钮的高光和阴影设计一个具有特色的信息弹窗。
  • 利用vue的transition组件给弹窗一些动画效果。
  • 采用单例模式将信息弹窗放置在全局避免重复。
  • 参考组件库Element Plus的设计使用方式,采用js的方式调用组件并传参。

自己整一个UI设计稿

结合以前玩的一款掌机游戏的提示框

  • 我希望这里的message提示框可以像横幅一样打在公屏上,样式要夸张一些。
  • 提示框在消失的时候可以有一个渐变放大透明的效果,视觉上比较有张力。

大概长这样:
在这里插入图片描述
功能上我需要:

  • 支持传入字符串或对象去调用信息弹窗。
  • 支持successerrorwarninginfo四个子方法去调用信息弹窗。

代码编写

1. 设计信息窗口基础样式

  • 利用box-shadow的内部阴影,结合hsl颜色模型增加背景色20%的亮度来凸显高光部分的样式。
  • 利用vue的transition组件给弹窗设置渐变放大透明度为0的过度效果。
  • 利用hsl颜色模型和vue的css变量,根据传入type类型转换组件颜色的色相、饱和度、亮度,为弹窗的背景绑定参数。

编写vue文件,设置基础的组件布局、颜色以及过度效果:

<template><transition name="scale"><div v-show="state.showMessage" class="pMessage">{{ state.content }}</div></transition>
</template>
<script setup>
import { reactive, ref } from "vue";
const state = reactive({content: "这是一个 message",duration: 1500,type: "primary",showMessage: false,
});
let hue = ref("");
let saturation = ref("");
let light = ref("");
const handleColor = (type) => {switch (type) {case "primary":hue.value = "204";saturation.value = "86%";light.value = "53%";break;case "success":hue.value = "85";saturation.value = "58%";light.value = "53%";break;case "error":hue.value = "10";saturation.value = "75%";light.value = "62%";break;case "warning":hue.value = "51";saturation.value = "93%";light.value = "54%";break;case "info":hue.value = "0";saturation.value = "0%";light.value = "83%";break;}
};
</script>
<style scoped>
.pMessage {--base_hue: v-bind(hue);--base_saturation: v-bind(saturation);--base_light: v-bind(light);color: #f3f3f3;font-size: 40px;font-weight: bold;width: 100%;height: 150px;display: flex;justify-content: center;align-items: center;position: fixed;top: 35%;z-index: 999;background: hsl(var(--base_hue), var(--base_saturation), var(--base_light));box-shadow: inset 0px -30px hsl(var(--base_hue), var(--base_saturation), calc(var(--base_light) + 20%));
}
.scale-enter-active,
.scale-leave-active {transition: 0.2s ease-out;
}
.scale-enter,
.scale-leave-to {transform: scale(2);opacity: 0;
}
</style>

2. 设置打开和关闭的方法

  • 因为是通过js方法去调用组件渲染,那么肯定要对外暴露一个open方法。
  • 为了代码的健壮性,也需要提供一个关闭方法。
  • 信息弹窗打开后需要通过定时器是延时关闭,需要操作好定时器,做一个简易的防抖,保证多次触发弹窗时,只在最后一次触发的延时时间后关闭弹窗。
  • 最后记得将写好的方法以及组件内部的state对象暴露出去,这样在外部js代码中实例化这个组件后,才可以读取到这个方法。

补充第一步vue文件的js内容:

let messageTimer = null;
const open = () => {if (messageTimer) {clearTimeout(messageTimer);messageTimer = null;}handleColor(state.type);state.showMessage = true;messageTimer = setTimeout(() => {state.showMessage = false;}, state.duration);
};
const close = () => {if (messageTimer) {clearTimeout(messageTimer);messageTimer = null;}state.showMessage = false;
};
// 导出这state、open、close,保证外部js可以读取到组件内部方法,这很重要!
defineExpose({ state, open, close });

3. 编写实例化组件的js文件

在vue文件的同级目录下创建对应的js文件。

  • 这里需要使用vue的createApp方法创建一个组件,并挂接在一个新建的dom上,然后将dom挂载在body中。
  • 为了避免多次触发信息弹窗,导致产生dom过多造成卡顿。我们采用单例模式去更新唯一的那个组件实例,而不是一直创建新的组件。
  • 通过initInstance 方法初始化组件。
  • 对外导出pMessage方法,用于更新信息弹窗实例的内容及状态。
  • 在pMessage方法中校验传入的信息:
    • 纯字符串就按默认的参数去渲染。
    • 对象则按照传入的信息结合默认参数去渲染。
    • 最后通过open方法去打开信息弹窗,展示dom。

以下是js内容:

import { createApp } from "vue";
import Message from "./index.vue";let instance;const initInstance = () => {const messageInstance = createApp(Message);// 需要一个容器const container = document.createElement("div");// 再进行挂载 - 挂载之后返回实例上下文instance = messageInstance.mount(container);document.body.appendChild(container);
};function pMessage(option) {if (!instance) initInstance();if (!option) {option = {};}option = typeof option === "string" ? { content: option } : option;instance.state.content = option.content || "这是一个 message";instance.state.duration = option.duration || 1500;instance.state.type = option.type || "primary";instance.open();
}export default pMessage;

补充successerrorwarninginfo四个子方法去调用信息弹窗

在js文件中继续补充代码

// 抽象公共逻辑
const createMessageType = (type) => {return function (content, duration) {return pMessage({content,duration,type,});};
};// 自动生成不同类型的消息方法
["success", "error", "warning", "info"].forEach((type) => {pMessage[type] = createMessageType(type);
});

补充信息弹窗组件的关闭方法

在js文件中继续补充代码

pMessage.close = function () {if (instance && instance.close) {instance.close();}
};

4. 看下最终效果

在这里插入图片描述

5. 组件完整代码

index.vue

<template><transition name="scale"><div v-show="state.showMessage" class="pMessage">{{ state.content }}</div></transition>
</template><script setup>
import { reactive, ref } from "vue";const state = reactive({content: "这是一个 message",duration: 1500,type: "primary",showMessage: false,
});
let messageTimer = null;
const open = () => {if (messageTimer) {clearTimeout(messageTimer);messageTimer = null;}handleColor(state.type);state.showMessage = true;messageTimer = setTimeout(() => {state.showMessage = false;}, state.duration);
};
const close = () => {if (messageTimer) {clearTimeout(messageTimer);messageTimer = null;}state.showMessage = false;
};
let hue = ref("");
let saturation = ref("");
let light = ref("");
const handleColor = (type) => {switch (type) {case "primary":hue.value = "204";saturation.value = "86%";light.value = "53%";break;case "success":hue.value = "85";saturation.value = "58%";light.value = "53%";break;case "error":hue.value = "10";saturation.value = "75%";light.value = "62%";break;case "warning":hue.value = "51";saturation.value = "93%";light.value = "54%";break;case "info":hue.value = "0";saturation.value = "0%";light.value = "83%";break;}
};
defineExpose({ state, open, close });
</script><style scoped>
.pMessage {--base_hue: v-bind(hue);--base_saturation: v-bind(saturation);--base_light: v-bind(light);color: #f3f3f3;font-size: 40px;font-weight: bold;width: 100%;height: 150px;display: flex;justify-content: center;align-items: center;position: fixed;top: 35%;z-index: 999;background: hsl(var(--base_hue), var(--base_saturation), var(--base_light));box-shadow: inset 0px -30px hsl(var(--base_hue), var(--base_saturation), calc(var(--base_light) + 20%));
}.scale-enter-active,
.scale-leave-active {transition: 0.2s ease-out;
}.scale-enter,
.scale-leave-to {transform: scale(2);opacity: 0;
}
</style>

index.js

import { createApp } from "vue";
import Message from "./index.vue";let instance;const initInstance = () => {const messageInstance = createApp(Message);// 需要一个容器const container = document.createElement("div");// 再进行挂载 - 挂载之后返回实例上下文instance = messageInstance.mount(container);document.body.appendChild(container);
};function pMessage(option) {if (!instance) initInstance();if (!option) {option = {};}option = typeof option === "string" ? { content: option } : option;instance.state.content = option.content || "这是一个 message";instance.state.duration = option.duration || 1500;instance.state.type = option.type || "primary";instance.open();
}// 抽象公共逻辑
const createMessageType = (type) => {return function (content, duration) {return pMessage({content,duration,type,});};
};// 自动生成不同类型的消息方法
["success", "error", "warning", "info"].forEach((type) => {pMessage[type] = createMessageType(type);
});pMessage.close = function () {if (instance && instance.close) {instance.close();}
};export default pMessage;

6. 组件调用方式

<template><div class="message"><p-button type="success" @click="openMessageSuccess"> 成功提示 </p-button><p-button type="error" @click="openMessageError"> 失败提示 </p-button><p-button @click="openMessage1"> 普通提示不传参 </p-button><p-button @click="openMessage2"> 普通提示传参 </p-button></div>
</template>
<script setup>
import pMessage from "@/components/message/index.js";
const openMessageSuccess = () => {pMessage.success("成功!");
};
const openMessageError = () => {pMessage.error("失败!");
};
const openMessage1 = () => {pMessage("普通提示不传参");
};
const openMessage2 = () => {pMessage({content: "普通提示传参",duration: 300,type: "primary",});
};
</script>

总结

至此一个完整的像素风信息弹窗组件Message就开发完成了。
本篇主要强化理解了几个点:

  • vue的transition组件的使用方法。
  • js的简易防抖实现逻辑。
  • js设计模式中的单例模式应用。
  • js调用组件封装逻辑。

再接再厉~


http://www.ppmy.cn/server/121810.html

相关文章

【目标检测】隐翅虫数据集386张VOC+YOLO

隐翅虫数据集&#xff1a;图片来自网页爬虫&#xff0c;删除重复项后整理标注而成 数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;386 标注…

win11 wsl2安装ubuntu22最快捷方法

操作系统是win11&#xff0c;wsl版本是wsl2&#xff0c;wsl应该不用多介绍了&#xff0c;就是windows上的虚拟机&#xff0c;在wsl上可以很方便的运行Linux系统&#xff0c;性能棒棒的&#xff0c;而且wsl运行的系统和win11主机之间的文件移动是无缝的&#xff0c;就是两个系统…

Java中实现对用户发布的文章进行审核

在Java中实现对用户发布的文章进行审核&#xff0c;避免有违法内容&#xff0c;可以通过以下几个步骤&#xff1a; 集成第三方内容审核服务&#xff1a;可以使用如七牛云、百度AI等第三方服务进行内容审核。这些服务提供了文本、图片和视频的审核API&#xff0c;可以检测内容中…

CSS04-Chrome调试工具

Chrome 浏览器提供了一个非常好用的调试工具&#xff0c;可以用来调试我们的 HTML结构和 CSS 样式。

html TAB切换按钮变色、自动生成table

<!DOCTYPE html> <head> <meta charset"UTF-8"> <title>Dynamic Tabs with Table Data</title> <style> /* 简单的样式 */ .tab-content { display: none; border: 1px solid #ccc; padding: 1px; marg…

rust一些通用编程的概念

rust一些通用编程的概念 官网文档数据类型 - Rust 程序设计语言 中文版 (rustwiki.org) 变量&#xff0c;数据类型&#xff0c;条件判断&#xff0c;循环 变量 rust中变量的可变性是值得注意的 例如: fn main(){let number 1;number 2;println!("the number is {}&quo…

8.进销存系统(基于springboot的进销存系统)

目录 1.系统的受众说明 2.开发技术与环境配置 2.1 SpringBoot框架 2.2 Java语言简介 2.3 MySQL环境配置 2.4 idea介绍 2.5 mysql数据库介绍 2.6 B/S架构 3.系统分析与设计 3.1 可行性分析 3.1.1 技术可行性 3.1.2 操作可行性 3.1.3经济可行性 3.4.1 数据库…

【Mysql】Centos 安装 Mysql8.0

官网下载安装包 官网地址&#xff1a;MySQL :: Download MySQL Community Server 查看服务器的名称和版本号 lsb_release -a 查看服务的架构 uname -m 下载对应的版本&#xff0c;这里操作系统选择 Red Hat 就可以了。&#xff08;CentOS 就是将 RHEL 发行的源代码从新编译…