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

news2024/11/18 5:28:34

目录

  • 引言
  • 自己整一个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.coloradmin.cn/o/2162411.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flask建立的Web网站的can‘t open file C_Program问题的分析

前言 想自己制作一个Web网站对接私有化的大模型。考虑到私有化的大模型都是Python编写为主的。所以&#xff0c;就打算用Python建立一个Web网站。目前&#xff0c;选定的是Flask框架&#xff08;Python3.11&#xff09;。但是&#xff0c;用PyCharm进行调试的时候却出现了问题。…

努比亚z17努比亚NX563j原厂固件卡刷包下载_刷机ROM固件包下载-原厂ROM固件-安卓刷机固件网

努比亚z17努比亚NX563j原厂固件卡刷包下载_刷机ROM固件包下载-原厂ROM固件-安卓刷机固件网 统版本&#xff1a;官方软件作者&#xff1a;热心网友rom大小&#xff1a;911MB发布日期&#xff1a;2018-12-23 努比亚z17努比亚NX563j原厂固件卡刷包下载_刷机ROM固件包下载-原厂RO…

【工作流集成】springboot+vue集成工作流activiti,flowable,智能审批系统,集成方案(源码)

基于Javavue开发的智能审批系统&#xff0c;低代码平台 软件资料清单列表部分文档清单&#xff1a;工作安排任务书&#xff0c;可行性分析报告&#xff0c;立项申请审批表&#xff0c;产品需求规格说明书&#xff0c;需求调研计划&#xff0c;用户需求调查单&#xff0c;用户需…

多智能体笔记本专家系统:集成CrewAI、Ollama和自定义Text-to-SQL工具

在这个项目中&#xff0c;我们的目标是创建一个由多智能体架构和本地大语言模型&#xff08;LLM&#xff09;驱动的个人笔记本电脑专家系统。该系统将使用一个SQL数据库&#xff0c;包含有关笔记本电脑的全面信息&#xff0c;包括价格、重量和规格。用户可以根据自己的特定需求…

数据结构3——双向链表

在上篇文章数据结构2——单链表中&#xff0c;我们了解了什么是链表&#xff0c;以及单链表的实现&#xff0c;接着上篇文章&#xff0c;本篇文章介绍一下比单链表更常用的链表——双向循环链表。 目录 1.双向链表的实现 1.1节点 1.2 初始化节点 1.3 动态开辟新节点 1.4 遍…

美团中间件C++一面-面经总结

1、TCP和UDP 的区别&#xff1f; 速记标识符&#xff1a;连靠刘墉宿营 解释&#xff1a; 面向连接vs无连接 可靠传输vs不保证可靠 字节流vs报文传输 拥塞控制流量控制vs无 速度慢vs速度快 应用场景自己描述 2、服务端处于close wait是什么情况&#xff0c;是由什么造成的&…

【算法】模拟:(leetcode)495.提莫攻击(easy)

目录 题目链接 题目介绍 解法 代码 题目链接 495. 提莫攻击 - 力扣&#xff08;LeetCode&#xff09; 题目介绍 解法 模拟 分情况讨论。 当寒冰再次中毒时&#xff0c;上次「中毒」是否已经结束。 ​ 当上次中毒已经结束了&#xff0c;那么上次「中毒」维持的时间就是 …

C++【类和对象】(构造函数与析构函数)

文章目录 1. 类的默认成员函数2. 构造函数析构函数的特点3. 析构函数析构函数的特点 结语 1. 类的默认成员函数 默认成员对象就是我们没有显示的写&#xff0c;但是编译器会自动生成的成员函数。一个类&#xff0c;我们不写的情况下编译器会默认生成以下6个成员函数&#xff0…

【JVM】一篇文章彻底理解JVM的组成,各组件的底层实现逻辑

文章目录 JVM 的主要组成部分类加载器&#xff08;Class Loader&#xff09;1. 加载&#xff08;Loading&#xff09;2. 链接&#xff08;Linking&#xff09;3. 初始化&#xff08;Initialization&#xff09; Execution Engine&#xff08;执行引擎&#xff09;1. 解释器&…

微服务--Docker

Docker是一个开源的应用容器引擎&#xff0c;它基于Go语言并遵从Apache2.0协议开源。Docker提供了一种轻量级、可移植和自包含的容器化环境&#xff0c;使开发人员能够在不同的计算机上以一致的方式构建、打包和分发应用程序。 一、Docker的基本概念 容器&#xff08;Contain…

828华为云征文|使用Flexus X实例创建FDS+Nginx服务实现图片上传功能

一、Flexus X实例 什么是Flexus X实例呢&#xff0c;这是华为云最新推出的云服务器产品&#xff0c;如下图&#xff1a; 华为云推出的Flexus云服务器X系列&#xff0c;是在华为顶尖技术团队&#xff0c;特别是荣获国家科技进步奖的领军人物顾炯炯博士及其团队的主导下精心研发…

AWS注册时常见错误处理

引言 创建AWS账号是使用AWS云服务的第一步&#xff0c;但在注册过程中可能会遇到一些常见的问题。本文中九河云将帮助您排查和解决在创建AWS账户时可能遇到的一些常见问题&#xff0c;包括未接到验证电话、最大失败尝试次数错误以及账户激活延迟等。 常见问题及解决方法 1. …

记一次Mac 匪夷所思终端常用网络命令恢复记录

一天莫名奇妙发现ping dig 等基础命令都无法正常使用。还好能浏览器能正常访问&#xff0c;&#xff0c;&#xff0c;&#xff0c; 赶紧拿baidu试试^-^ ; <<>> DiG 9.10.6 <<>> baidu.com ;; global options: cmd ;; connection timed out; no serve…

leetcode24. 两两交换链表中的节点,递归

leetcode24. 两两交换链表中的节点 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 示例 1&#xff1a; 输入&#xff1a;he…

[译] K8s和云原生

本篇内容是根据2019年8月份Kubernetes and Cloud Native音频录制内容的整理与翻译, Johnny 和 Mat 与 Kris Nova 和 Joe Beda 一起探讨了 Kubernetes 和云原生。他们讨论了 Kubernetes 推动的“云原生”应用的兴起、使用 Kubernetes 的合适场合、运行如此大型的开源项目所面临…

【Java】虚拟机(JVM)内存模型全解析

目录 一、运行时数据区域划分 版本的差异&#xff1a; 二、程序计数器 程序计数器主要作用 三、Java虚拟机 1. 虚拟机运行原理 2. 活动栈被弹出的方式 3. 虚拟机栈可能产生的错误 4. 虚拟机栈的大小 四、本地方法栈 五、堆 1. 堆区的组成&#xff1a;新生代老生代 …

叶国富学得会胖东来吗?

“大家都看不懂就对了&#xff0c;如果都看得懂我就没有机会了。”昨晚&#xff0c;实体零售迎来一则重磅消息&#xff0c;名创优品获得了全国第二大连锁超市永辉超市的大股东身份。在资本市场负反馈的压力下&#xff0c;名创优品创始人叶国富有了上述回应。 消息公布后&#x…

云服务器是干什么的?

随着云计算的发展&#xff0c;云服务器的功能逐步完善。但是还有不少用户不清楚云服务器是干什么的&#xff1f;云服务器提供了一种灵活、可扩展的计算解决方案&#xff0c;适用于各种在线业务和项目。提供虚拟化的计算资源是云服务器最基本也是最重要的功能。 云服务器是干什…

vue项目npm run serve 报错,Error: read ECONNRESET at TCP.onStreamRead

背景&#xff1a;vue2的项目&#xff0c;之前npm run serve一直可以正常使用&#xff0c;突然每次启动都会报错了&#xff0c;报错信息如下&#xff1a; node:events:492 throw er; // Unhandled error event ^ Error: read ECONNRESET at TCP.onStreamRead (n…

Android开发okhttp下载图片带进度

Android开发okhttp下载图片带进度 下载网络图片的方法有很多&#xff0c;这次介绍写用okhttp来下载网络图片&#xff0c;主要我看中的是用okhttp下载有进度返回&#xff0c;提示下用户 一、思路&#xff1a; 用OkHttpClient().newCall(request) 二、效果图&#xff1a; 三、…