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

news2024/9/20 14:58:44

目录

  • 引言
  • 做之前先仔细看看UI设计稿
    • 解读一下都有哪些元素:
    • 素材补充
  • 代码编写
    • 1. 按钮四周边框
    • 2. 默认状态下按钮颜色立体效果
    • 3. 鼠标移入聚焦
    • 4. 模拟鼠标点击效果
  • 组件封装
    • 1. 按类型设置颜色
    • 2. 设置按钮禁用状态
    • 3. 处理一个bug
    • 4. 看下整体组件效果
    • 5. 组件完整代码
    • 6. 组件调用方式
  • 总结

引言

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

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

本篇复现的组件为button,日常项目中较为常见的组件,主要涉及到的内容有:

  1. 基础样式构建。
  2. 点击动效设计。
  3. 参考市面上常见组件库的设计,根据type切换颜色。
  4. 设置禁用模式。

做之前先仔细看看UI设计稿

UI稿

解读一下都有哪些元素:

  • 按钮四周的边框带缺角,带点子像素的风格。
  • 默认状态下字体区域较亮,右下角阴影处较暗,凸显立体按钮效果。
  • 鼠标移入时亮区压缩,提醒用户聚焦于此处。
  • 鼠标按下时亮区往右下方移动,模拟立体按钮被按下。
  • 整体设计效果太对味儿了!

素材补充

禁用按钮时需要将手势图片替换为禁止图片,使用系统默认的会比较突兀,我这里做了一张图。
禁用
你只需要在iconfont上随便找个禁用图片,然后通过我另一篇文档介绍的方法处理即可。这张图我也补充到文档资源绑定了,在文档开头获取。
链接地址:【VUE3.0】如何得到一张像素风格的图片?

代码编写

按照设计稿解读内容:

1. 按钮四周边框

想象一下如何在一个dom元素四周加上一个带缺角的边框?

  • 最简单的方式是直接按钮设置边框,在按钮内部设置四个子元素,正方形白色底,定位到父元素的四个角盖上。
  • 上一个方法的变体是不设置按钮边框,将内部四个子元素设置成边框的宽高和底色,定位到四个边位置。
  • 有一个比较变态的玩法是在按钮上方覆盖一个黑色底的dom,利用clip-path抠出边框套在按钮上。(难度太大,且不好设置)
  • 本教程采用使用伪类的方式设置边框样式。

首先写好html部分,设置样式类和插槽:

  <button class="pButton">
    <slot>button</slot>
  </button>

设置button基础样式,包含基础的宽高、相对定位、字体样式及鼠标样式。字体和鼠标样式在先导篇配置过,从本文引言处跳转回看。

.pButton {
  height: 45px;
  padding: 0 15px;
  display: flex;
  justify-content: center;
  align-items: center;
  box-sizing: border-box;
  font-family: pixel_en, pixel_ch;
  font-weight: bold;
  letter-spacing: 1.5px;
  user-select: none;
  border: 0px;
  position: relative;
  cursor: var(--cursor_pointer);
}

利用before和after两个伪类,分别设置上下边框和左右边框,交叉叠加在按钮底部,上下和左右都超出按钮两倍的边框厚度,保证可以露出来。

.pButton::after {
  content: "";
  border-left: 4px solid #333;
  border-right: 4px solid #333;
  position: absolute;
  left: -4px;
  top: 0;
  width: calc(100% + 8px);
  height: 100%;
  box-sizing: border-box;
  z-index: -1;
}

.pButton::before {
  content: "";
  border-top: 4px solid #333;
  border-bottom: 4px solid #333;
  position: absolute;
  left: 0;
  top: -4px;
  width: 100%;
  box-sizing: border-box;
  height: calc(100% + 8px);
  z-index: -2;
}

看下这步的效果
边框
至此边框部分就完成了。

2. 默认状态下按钮颜色立体效果

想象一下如何实现这种立体效果?

  • 最简单的方式是按钮设置个暗一些的底色,内部套个子元素设置亮一些,把按钮的文字放在子元素内部。
  • 上一个方法的变体是仍然套一个子元素,子元素宽高小于按钮宽高,使用阴影将剩余空间补满,阴影设置暗色。(子元素宽高不好把握数值)
  • 最终采取按钮本体的向内阴影解决这个问题。

这里应用到两个点,一个是box-shadow的向内阴影,一个是hsl颜色模型。

  • 关于hsl可以参考:【CSS Tricks】在css中尝试一种新的颜色模型HSL,这里简单解释下为什么要使用hsl。因为按钮的亮部和暗部都属于同一个颜色,不同的亮度,如果采用rgb颜色模型,三个参数都要变,后续有进一步的颜色变化则还需要新查询三个rgb参数,处理起来比较复杂。如果使用hsl颜色模型,则只需要调整最后一个参数亮度加减,即可得到同色系不同亮度的色值。
  • box-shadow需要注意内部阴影的左右偏移值,以及为了避免过度锐利,将模糊和传播距离设置为1px,优化效果。
.pButton {
 /* 其他样式规则 */
  color: #fff;
  /* 背景色 */
  background: hsl(204, 86%, 56%);
  box-shadow: inset -4px -4px 1px 1px hsl(204, 86%, 42%);
}

看下这步的效果
按钮

3. 鼠标移入聚焦

这步主要是利用css的hover选择器,鼠标移入时,将阴影偏移加大2px,同时将高亮区域亮度稍微调暗一点,表示聚焦。

.pButton:hover {
  background: hsl(204, 86%, 51%);
  box-shadow: inset -6px -6px 1px 1px hsl(204, 86%, 42%);
}

4. 模拟鼠标点击效果

这步主要是利用css的active选择器,在鼠标点击按钮时触发,将box-shadow偏移值取反,移动至右下角,模拟按下操作。

.pButton:active{
  background: hsl(204, 86%, 51%);
  box-shadow: inset 6px 6px 1px 1px hsl(204, 86%, 42%);
}

看下3、4步后的效果
按钮测试
至此按钮的基本雏形就完成了。

组件封装

1. 按类型设置颜色

  • 根据市面上常见的UI组件库,按钮的type一般分为primary、success、warning、info、error和default这几种,分别对应不同的颜色。我们需要在组件内部接收一个参数type,根据类型对按钮颜色做改变。
  • 先提取不同类型下颜色的hsl值,利用到vue3的css变量,去动态设置颜色。

js部分

import { ref} from "vue";
const props = defineProps({
  type: {
    type: String,
    default: "",
  },
});
// 颜色处理
let hue = ref("0");
let saturation = ref("0%");
let light = ref("100%");
switch (props.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;
  default:
    hue.value = "0";
    saturation.value = "0%";
    light.value = "100%";
    break;
}

css部分:亮度变化通过calc进行计算后赋值。

.pButton {
/* 设置hsl参数变量 */
  --btn_hue: v-bind(hue);
  --btn_saturation: v-bind(saturation);
  --btn_light: v-bind(light);
  /* 替换变量 */
  background: hsl(
    var(--btn_hue),
    var(--btn_saturation),
    calc(var(--btn_light) + 3%)
  );
  box-shadow: inset -4px -4px 1px 1px
    hsl(var(--btn_hue), var(--btn_saturation), calc(var(--btn_light) - 11%));
}

2. 设置按钮禁用状态

通过接收disabled的Boolean变量控制按钮禁用状态。

  • 因为我们对button的样式做了重写,所以在button设置了disabled属性后默认的禁用样式失效,需要在button上设置动态class编写禁用后的样式。
  • 对button设置disabled属性后,可以阻止按钮点击事件,达到禁用效果。

html部分

  <button class="pButton" :class="{ disabled }" :disabled="disabled">
    <slot>button</slot>
  </button>

js部分

const props = defineProps({
  disabled: {
    type: Boolean,
    default: false,
  },
});

css部分

  • 设置手部禁用样式。
  • 复写hover后的样式,和button默认状态保持一致,鼠标移入后不发生变化,达到禁用效果。
.disabled {
  opacity: 0.5;
  cursor: var(--cursor_disabled);
}
.disabled:hover {
  background: hsl(
    var(--btn_hue),
    var(--btn_saturation),
    calc(var(--btn_light) + 3%)
  );
  box-shadow: inset -4px -4px 1px 1px
    hsl(var(--btn_hue), var(--btn_saturation), calc(var(--btn_light) - 11%));
}

3. 处理一个bug

当颜色偏白了之后,字体颜色就不能为白色,不然看不清楚。这时候hsl模型的好处就体现出来了,hue、saturation、light三个参数,我们简单的判断light参数是否大于80%(自己摸索的值,可以根据实际情况调整),如果大于80%则颜色偏白,设置深色字体颜色,反之设置白色字体颜色。

js部分

const fontColor = ref(
  parseInt(light.value) >= 80 ? "hsl(210, 11%, 15%)" : "hsl(0, 0%, 100%)"
);

css部分

.pButton {
// 其他样式
 color: v-bind(fontColor);
 }

4. 看下整体组件效果

整体效果

5. 组件完整代码

<template>
  <button class="pButton" :class="{ disabled }" :disabled="disabled">
    <slot>button</slot>
  </button>
</template>

<script setup>
import { ref } from "vue";
const props = defineProps({
  type: {
    type: String,
    default: "",
  },
  disabled: {
    type: Boolean,
    default: false,
  },
});
// 颜色处理
let hue = ref("0");
let saturation = ref("0%");
let light = ref("100%");
switch (props.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;
  default:
    hue.value = "0";
    saturation.value = "0%";
    light.value = "100%";
    break;
}
const fontColor = ref(
  parseInt(light.value) >= 80 ? "hsl(210, 11%, 15%)" : "hsl(0, 0%, 100%)"
);
</script>
<style scoped>
.pButton {
  --btn_hue: v-bind(hue);
  --btn_saturation: v-bind(saturation);
  --btn_light: v-bind(light);
  height: 45px;
  padding: 0 15px;
  display: flex;
  justify-content: center;
  align-items: center;
  box-sizing: border-box;
  font-family: pixel_en, pixel_ch;
  font-weight: bold;
  letter-spacing: 1.5px;
  color: v-bind(fontColor);
  user-select: none;
  background: hsl(
    var(--btn_hue),
    var(--btn_saturation),
    calc(var(--btn_light) + 3%)
  );
  border: 0px;
  position: relative;
  box-shadow: inset -4px -4px 1px 1px
    hsl(var(--btn_hue), var(--btn_saturation), calc(var(--btn_light) - 11%));
  cursor: var(--cursor_pointer);
}

.pButton:hover {
  background: hsl(
    var(--btn_hue),
    var(--btn_saturation),
    calc(var(--btn_light) - 2%)
  );
  box-shadow: inset -6px -6px 1px 1px
    hsl(var(--btn_hue), var(--btn_saturation), calc(var(--btn_light) - 11%));
}
.pButton:active {
  background: hsl(
    var(--btn_hue),
    var(--btn_saturation),
    calc(var(--btn_light) - 2%)
  );
  box-shadow: inset 6px 6px 1px 1px
    hsl(var(--btn_hue), var(--btn_saturation), calc(var(--btn_light) - 11%));
}
.pButton::after {
  content: "";
  border-left: 4px solid #333;
  border-right: 4px solid #333;
  position: absolute;
  left: -4px;
  top: 0;
  width: calc(100% + 8px);
  height: 100%;
  box-sizing: border-box;
  z-index: -1;
}

.pButton::before {
  content: "";
  border-top: 4px solid #333;
  border-bottom: 4px solid #333;
  position: absolute;
  left: 0;
  top: -4px;
  width: 100%;
  box-sizing: border-box;
  height: calc(100% + 8px);
  z-index: -2;
}
.disabled {
  opacity: 0.5;
  cursor: var(--cursor_disabled);
}
.disabled:hover {
  background: hsl(
    var(--btn_hue),
    var(--btn_saturation),
    calc(var(--btn_light) + 3%)
  );
  box-shadow: inset -4px -4px 1px 1px
    hsl(var(--btn_hue), var(--btn_saturation), calc(var(--btn_light) - 11%));
}
</style>

6. 组件调用方式

    <p-button type="primary">primary</p-button>
    <p-button type="success">success</p-button>
    <p-button type="error">error</p-button>
    <p-button type="warning">warning</p-button>
    <p-button type="info">info</p-button>
    <p-button>button</p-button>
    <p-button type="primary" disabled>primary</p-button>

总结

至此一个完整的button像素风按钮就开发完成了。开发过程中我也收获了许多:

  • 锻炼了一下对UI稿的拆解分析能力。
  • 结合自身技能储备,对每个步骤都能想到一些解决办法并选择合适的方案,巩固了一下技能。
  • 探索了一些新的东西,比如hsl颜色模型。为将来项目开发提供了新思路。
  • 更加深入理解了一下组件封装的逻辑。

有了第一个组件的样式模板的经验,后续的组件开发也有了参考依据,我想后边做起来会比较快一些。但愿

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2149199.html

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

相关文章

数字自然资源领域的实现路径

在数字化浪潮的推动下&#xff0c;自然资源的管理与利用正经历着前所未有的变革。本文将从测绘地理信息与遥感专业的角度&#xff0c;深度分析数字自然资源领域的实现路径。 1. 基础数据的数字化 数字自然资源的构建&#xff0c;首先需要实现基础数据的数字化。这包括地形地貌…

db2恢复数据库

db2licm -l检查下license IBM Support: Fix Central - Please wait, Select fixes db2 force application all db2ckbkp -H JYC.0.DB2.NODE0000.CATN0000.20240603223001.001 db2 "restore db jyc logtarget x:\db2\log" db2 "rollforward db jyc to end of log…

音频北斗定位系统有什么用?

在当今科技飞速发展的时代&#xff0c;定位技术已经成为我们日常生活和各行各业不可或缺的一部分。其中&#xff0c;音频北斗定位系统作为一种新兴的定位技术&#xff0c;正逐渐展现出其独特的优势和应用价值。那么&#xff0c;到底音频北斗定位系统有什么用呢?我们一起来了解…

住宅代理IP如何提高 IP声誉?

你有没有遇到过类似的问题&#xff1f;发送的邮件被标记为垃圾邮件并被屏蔽、访问某些网站被拒绝、广告效果不理想&#xff0c;甚至网上交易无缘无故被拒绝&#xff1f;这到底是什么原因造成的&#xff1f;其实&#xff0c;这些问题可能都和 IP 信誉息息相关。 如果你的 IP 地址…

一文读懂HPA弹性扩展以及实践攻略

一文读懂HPA弹性扩展以及实践攻略 目录 1 概念&#xff1a; 1.1 什么是弹性扩展1.2 HPA 的工作原理1.3 通过监控指标来调整副本数 1.3.1 计算公式说明1.3.2 平均值计算1.3.3 未就绪 Pod 和丢失的指标处理1.3.4 多指标支持1.3.5 缩减副本的平滑策略 1.4 HPA的优缺点 2 实践攻略…

微服务保护学习笔记(五)Sentinel授权规则、获取origin、自定义异常结果、规则持久化

文章目录 前言4 授权规则4.1 基本原理4.2 获取origin4.3 配置授权规则 5 自定义异常结果6 规则持久化 前言 微服务保护学习笔记(一)雪崩问题及解决方案、Sentinel介绍与安装 微服务保护学习笔记(二)簇点链路、流控操作、流控模式(关联、链路) 微服务保护学习笔记(三)流控效果(…

C语言 14 结构体 联合体 枚举

之前认识过很多种数据类型&#xff0c;包括整数、小数、字符、数组等&#xff0c;通过使用对应的数据类型&#xff0c;就可以很轻松地将数据进行保存了&#xff0c;但是有些时候&#xff0c;这种简单类型很难去表示一些复杂结构。 结构体 比如现在要保存 100 个学生的信息&am…

本地部署一个轻量化智能聊天服务Vocechat并实现异地远程交互

文章目录 前言1. 拉取Vocechat2. 运行Vocechat3. 本地局域网访问4. 群晖安装Cpolar5. 配置公网地址6. 公网访问小结 7. 固定公网地址 前言 本文主要介绍如何在本地群晖NAS搭建一个自己的聊天服务Vocechat&#xff0c;并结合内网穿透工具实现使用任意浏览器远程访问进行智能聊天…

我的AI工具箱Tauri版-VideoReapeat视频解说复述克隆

本教程基于自研的AI工具箱Tauri版进行VideoReapeat视频解说复述克隆。 VideoReapeat视频解说复述克隆 是自研的AI工具箱Tauri版中的一款专用模块&#xff0c;旨在通过AI技术对视频解说内容进行复述和克隆。该工具可自动洗稿并重新生成视频解说&#xff0c;通过简单配置即可对大…

协同编程的艺术:SIDE 让团队协作更上一层楼

一、协同编程的现状 在当前软件开发中&#xff0c;团队协作面临着诸多挑战。沟通不畅常常导致项目进度延迟&#xff0c;版本控制复杂使得代码合并困难重重。传统 IDE 在协同工作方面存在明显的局限性&#xff0c;缺乏实时协作功能&#xff0c;团队成员之间的沟通工具也不够完善…

如何上传tauri项目到csdn gitcode

如何上传tauri项目到csdn gitcode 首先保证项目目录有.gitignore&#xff0c;避免不必要的文件上传分享。 gitignore文件 # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* pnpm-debug.log* lerna-debug.log*node_modules dist dist-ssr *.local# Editor …

浅谈如何入门游戏漏洞挖掘,零基础入门到精通,收藏这一篇就够了

引言 每个白帽子心中&#xff0c;都有一团火 那就是对网络安全的信念 哪怕&#xff0c;是一条少有人走的路 补天希望与更多同路人携手&#xff0c;再向前一步 2021年补天平台启动“技术模块” 牛年新春特别分享单元 大年初一至初七 每天一个精选技术方向 每天一条原创…

QMT获取可转债行情数据方法介绍!支持QMT量化软件的券商平台?

获取可转债行情 为了获取转债的日线/1m/1d的k数据&#xff0c;以通过数据订阅形式获取最新行情subscribe_quote。如果您需要获取历史数据&#xff0c;可以使用download_history_data函数下载相关数据&#xff0c;然后使用get_market_data_ex函数提取所需的信息。这样&#xff…

Smartbi AIChat应用案例-某保险集团内部经营分析

案例简介 某保险企业面对大数据时代下的业务发展挑战&#xff0c;面临数据查询、分析与探索需求的激增。然而&#xff0c;当前的数据基础设施与应用方式已难以满足业务快速发展的需求。企业面临分析链路长、报表时效性低、制作效率低且灵活度不足、无效报表堆积等困局。为打破这…

探索《越南语翻译通》App:高效语言学习的利器

在当今这个全球化的世界里&#xff0c;语言学习变得越来越重要。随着科技的发展&#xff0c;我们有了更多便捷的工具来帮助我们学习新的语言。今天&#xff0c;我们来探索一款名为《越南语翻译通》的App&#xff0c;它正逐渐成为语言学习者的新宠。 《越南语翻译通》App的特点…

Linux安装(带VM激活码)

1.安装Vmare WorkStation虚拟机 VMware Workstation Pro是VMware&#xff08;威睿公司&#xff09;发布的一代虚拟机软件&#xff0c;中文名称为"VMware 工作站".它的主要功能是可以给用户在单一的桌面上同时运行不同的操作系统&#xff0c;它也是可进行开发、测试、…

笑不活了!薅走羊毛党20亿跑路?这生意我横竖没看懂……

昨天&#xff0c;各种八卦群传出一张非常离谱的截图&#xff0c;被无数吃瓜群众称为这是今年最好笑的事&#xff0c;纷纷表示能不能把我拉群里&#xff0c;让我再笑会……。 截图大概是这样的&#xff0c;博主开头先是发了几个哈哈哈&#xff0c;隔着屏幕都能感受到这事到底有…

3个月9次迭代,快手可灵AI面向全球发布1.5模型

9月19日&#xff0c;可灵AI迎来重磅升级&#xff0c;视频生成新增可灵1.5模型&#xff0c;在画质质量、动态质量、美学表现、运动合理性以及语义理解等方面均有显著提升。与此同时&#xff0c;可灵AI还引入了全新的“运动笔刷”功能&#xff0c;进一步提升视频生成的精准控制能…

电能计量,三相电基础

1. 三相交流电基础 三相交流电由三个频率相同、振幅相等、相位依次互相差120的交流电势组成。这样组织的三相电有一个特点&#xff0c;就是任何一个时刻&#xff0c;三相电的电压之和都等于零。这个特性使得本来需要六根导线来传输三组电压的减少到了三根导线&#xff0c;这样…

访问者模式:将算法与对象结构分离的设计模式

在软件开发中&#xff0c;我们常常需要对对象结构中的各个元素进行不同的操作。比如在编译器中&#xff0c;我们可能需要对抽象语法树&#xff08;AST&#xff09;的各个节点进行语法检查、代码生成、优化等操作。如果将这些操作直接嵌入到节点类中&#xff0c;会导致类的职责过…