当前位置: 首页 > news >正文

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

目录

  • 引言
  • 做之前先仔细看看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 1pxhsl(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 1pxhsl(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 1pxhsl(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 1pxhsl(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 1pxhsl(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 1pxhsl(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颜色模型。为将来项目开发提供了新思路。
  • 更加深入理解了一下组件封装的逻辑。

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

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • SQL编程题复习(24/9/20)
  • 【随手笔记】使用J-LINK读写芯片内存数据
  • Java:List<String> 转换List<BigDecimal> 并求和
  • 【系统架构设计师】专业英语90题(附答案详解)
  • 手写Spring
  • 0基础跟德姆(dom)一起学AI 数据处理和统计分析04-Panda入门
  • ArrayList和Array有什么区别?
  • 【RabbitMQ 项目】项目概述
  • 9.20-使用k8s部署wordpress项目
  • ELF文件结构
  • Git入门学习(1)
  • 基于协同过滤算法+PHP的新闻推荐系统
  • 详解Linux中cat命令
  • linux-安全管理-防火墙与网络安全
  • 硬件工程师笔试面试——开关
  • 【附node操作实例】redis简明入门系列—字符串类型
  • el-input获取焦点 input输入框为空时高亮 el-input值非法时
  • golang 发送GET和POST示例
  • Java反射-动态类加载和重新加载
  • magento 货币换算
  • Odoo domain写法及运用
  • OSS Web直传 (文件图片)
  • VuePress 静态网站生成
  • yii2权限控制rbac之rule详细讲解
  • 诡异!React stopPropagation失灵
  • 利用jquery编写加法运算验证码
  • 使用iElevator.js模拟segmentfault的文章标题导航
  • 一起来学SpringBoot | 第三篇:SpringBoot日志配置
  • 一些基于React、Vue、Node.js、MongoDB技术栈的实践项目
  • 用jquery写贪吃蛇
  • 支付宝花15年解决的这个问题,顶得上做出十个支付宝 ...
  • ​ 轻量应用服务器:亚马逊云科技打造全球领先的云计算解决方案
  • #数据结构 笔记三
  • #在线报价接单​再坚持一下 明天是真的周六.出现货 实单来谈
  • (C++20) consteval立即函数
  • (Java岗)秋招打卡!一本学历拿下美团、阿里、快手、米哈游offer
  • (Java数据结构)ArrayList
  • (PyTorch)TCN和RNN/LSTM/GRU结合实现时间序列预测
  • (SERIES12)DM性能优化
  • (附源码)计算机毕业设计ssm基于B_S的汽车售后服务管理系统
  • (入门自用)--C++--抽象类--多态原理--虚表--1020
  • (一)模式识别——基于SVM的道路分割实验(附资源)
  • (转) Face-Resources
  • (转载)从 Java 代码到 Java 堆
  • .NET 8.0 中有哪些新的变化?
  • .net core MVC 通过 Filters 过滤器拦截请求及响应内容
  • .net core 依赖注入的基本用发
  • .NET MAUI学习笔记——2.构建第一个程序_初级篇
  • .net 怎么循环得到数组里的值_关于js数组
  • .NET面试题解析(11)-SQL语言基础及数据库基本原理
  • .Net实现SCrypt Hash加密
  • .net中的Queue和Stack
  • .net中调用windows performance记录性能信息
  • .NET中使用Protobuffer 实现序列化和反序列化
  • /usr/bin/env: node: No such file or directory