05 - Vue3 UI Framework - Button 组件
官网基本做好了,接下来开始做核心组件
返回阅读列表点击 这里
目录准备
在项目 src
目录下创建 lib
文件夹,用来存放所有的核心组件吧。然后再在 lib
文件夹下创建 Button.vue
文件。
您也可以进行结构化设计,比如,这里就不进行了。
|-lib
|-Button
|- Button.vue
|- Button.ts
|_ Button.scss
需求分析
惯例先行需求分析
- 多种类基础
Button
,包含警告、成功、危险等 - 允许设置
Button
为禁用状态 - 不止有传统
Button
,还可以有文字或链接形式 - 当处于加载中,
Button
应当显示 - 有不同的尺寸可供选择
- 应当允许更换颜色
- 当鼠标放置于
Button
上、鼠标按下未松开、处于加载中等状态时,应当变更背景色 - 允许用户自定义
Button
上显示的文本
那么可以整理出以下参数表格
参数 | 含义 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
level | 默认类型 | string | default / plain / primary / success / info / warning / danger | default |
disabled | 是否禁用 | boolean | false / true | false |
theme | 式样 | string | button / link / text | button |
loding | 是否加载中 | boolean | false / true | false |
size | 尺寸 | string | middle / small / large | middle |
color | 颜色 | string | 任意合法颜色值 | #f3678e |
第 7 条,可以通过设置一个遮罩层来实现,只要遮罩层变色,背景色也等效变色
第 8 条,可以通过插槽实现,注意 vue3
不建议使用具名插槽
骨架
容易得到如下骨架
<template>
<button
class="jeremy-button"
:theme="theme"
:level="level"
:size="size"
:style="{ '--color': color }"
:disabled="disabled"
:loading="loading"
>
<div class="jeremy-button-mask"></div>
<span class="jeremy-button-loadingIndicator" v-if="loading"></span>
<slot></slot>
</button>
</template>
首先,本质应当是一个 button
元素,在此基础上,将参数列表中整理出来的每个参数,都使用 v-bind
绑定到 button
上
注意,此处绑定 color
,必须是如上例一样,绑定到 --color
属性上,才可以在 css
中使用 css3
语法 var()
读取,在 css
小节会再解释,此处略
之后,在 button
内
- 放置一个遮罩层,用于变色
- 放置一个”加载中”的动画,用于在加载中状态下显示
- 放置一个默认插槽,用于传递用户自定义的文本
然后为上述元素配置各自的 class
名称,骨架就完成了。
功能
显然,参数列表中整理出来的内容,一定来自引用该组件的地方的传入,先根据参数列表,写好 ts
声明:
declare const props: {
theme?: "button" | "link" | "text";
level?:
| "default"
| "plain"
| "primary"
| "success"
| "info"
| "warning"
| "danger";
size?: "middle" | "small" | "large";
color: string;
disabled: boolean;
loading: boolean;
};
然后在 export default
中,写入我们的参数
export default {
install: function (Vue) {
Vue.component(this.name, this);
},
name: "JeremyButton",
props: {
theme: {
type: String,
default: "button",
},
level: {
type: String,
default: "default",
},
size: {
type: String,
default: "middle",
},
color: {
type: String,
default: "#8c6fef",
},
disabled: {
type: Boolean,
default: false,
},
loading: {
type: Boolean,
default: false,
},
},
};
对于事件绑定,因为我们设计的组件只有一个唯一的根元素,所以对于外部传递过来的事件,会自动绑定到组件的根元素上面。
样式表
注意 :UI
库的样式表一般不要加 scoped
修饰符,为了尽可能减少对用户样式表的影响,方便用户 DIY
特别注意 : button
元素会有默认黑色外边框,不属于 border
,必须通过 outline: none;
才能消除
然后,我们使用 css3
的 var()
语法,取得我们通过 ts
绑定到 style
上的 --color
属性
为什么是 --color
而不是 color
?因为 var()
语法要求这个参数必须是 --
开头,才可以正常访问到
对于遮罩层,采用淡出到白色即可实现,原理此处不解释了
最后,对于多种不同的 button
,可以使用 scss
提供的 mixin / include
语法来实现,完整代码如下:
$theme-color: var(--color);
$base-mask: fade-out(#fff, 0.7);
$active-mask: fade-out(#fff, 0.5);
$h: 32px;
$radius: 4px;
@keyframes jeremy-spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.jeremy-button {
position: relative;
display: inline-block;
padding: 10px 16px;
color: white;
border-radius: $radius;
border: none;
font-size: 16px;
cursor: pointer;
white-space: nowrap;
transition: background-color 250ms;
outline: none;
:focus {
outline: none;
}
> .jeremy-button-mask {
position: absolute;
display: inline-block;
height: 100%;
width: 100%;
left: 0;
top: 0;
border-radius: $radius;
&:hover {
background: $base-mask;
}
}
&[loading="true"],
&[disabled] {
cursor: not-allowed;
> .jeremy-button-mask {
pointer-events: none;
}
}
> .jeremy-button-loadingIndicator {
width: 14px;
height: 14px;
display: inline-block;
margin-right: 4px;
border-radius: 8px;
border-style: solid;
border-width: 2px;
animation: jeremy-spin 1s infinite linear;
}
}
@mixin layout($color) {
$loading-color: fade-out(black, 0.7);
background: $color;
&:active {
> .jeremy-button-mask {
background: $active-mask;
}
}
> .jeremy-button-loadingIndicator {
border-color: $loading-color $loading-color $loading-color transparent;
}
&[loading="true"],
&[disabled] {
> .jeremy-button-mask {
background: $base-mask;
}
}
}
.jeremy-button[theme="button"] {
$color: $theme-color;
@include layout($color);
}
.jeremy-button:not([theme="button"]) {
padding: 0;
background: white;
color: black;
&:hover {
color: $theme-color;
}
}
.jeremy-button[theme="link"] {
text-decoration: underline;
}
.jeremy-button[level="plain"] {
$base-color: $theme-color;
@include layout(white);
color: black;
> .jeremy-button-mask {
border: 1px solid rgb(187, 187, 187);
}
&:not([loading="true"]):not([disabled]) {
&:hover {
> .jeremy-button-mask {
border: 1px solid $base-color;
}
color: $base-color;
}
}
}
.jeremy-button[level="primary"] {
$color: #29adfa;
@include layout($color);
}
.jeremy-button[level="success"] {
$color: rgb(103, 194, 58);
@include layout($color);
}
.jeremy-button[level="info"] {
$color: #808080;
@include layout($color);
}
.jeremy-button[level="warning"] {
$color: rgb(230, 162, 60);
@include layout($color);
}
.jeremy-button[level="danger"] {
$color: rgb(245, 108, 108);
@include layout($color);
}
.jeremy-button[size="large"] {
padding: 14px 24px;
}
.jeremy-button[size="small"] {
padding: 6px 10px;
}
以上,button
组件就完成了! :happy:
测试一下
项目地址 🎁
GitHub: https://github.com/JeremyWu917/jeremy-ui
官网地址 🌍
JeremyUI: https://ui.jeremywu.top
感谢阅读 ☕