介绍
最常见的操作按钮,button是浏览器自带的操作按钮,但为了统一样式,我们会抹平浏览器之间的样式差距,顺便增加点新的功能。
想要实现的功能
| 功能 | 介绍 |
|---|---|
| type | primary(主色) / success(成功) / warning(警告) / danger(危险) / info(信息) / text(文本) |
| disabled | 禁止使用 |
| size | 4种尺寸,large(超大),medium(常规),small(小),mini(迷你) |
| icon | 可设置图标 |
| round | 是否圆角按钮 |
| circle | 是否圆形按钮 |
| loading | 加载中状态 |
功能实现
1. 基础功能
src/packages 目录下新建button文件夹,文件夹内创建button.vue和index.js
src/styles 目录下心新建button.scss,并在src/styles/index.scss中引入
// button.vue
<template>
<button class="my-button my-button-primary">
<slot>按钮</slot>
</button>
</template>
<script>
export default {
name: 'Button',
data () {
return {
}
},
}
</script>
// button/index.js
import Button from './button.vue'
export default Button
// styles/button.scss
@charset "UTF-8";
@import "common/var";
@import "mixins/mixins";
@include b(button) {
height: 36px;
font-size: $--font-size-medium;
color: #fff;
border-radius: 4px;
outline: none;
border: 1px solid transparent;
padding: 0 10px;
cursor: pointer;
&-primary {
background-color: $--color-primary;
// focus,hover的时,背景色变浅
&:focus,
&:hover {
background-color: rgba($--color-primary, 0.7);
}
&:active {
// active时,背景色变浅,且与focus,hover做区分
background-color: rgba($--color-primary, 0.9);
}
}
}
// styles/index.scss
@import "button";
2. 类型type
Button的类型区分主要是通过背景色区分的。因此只要根据type设置class即可。
总共设计了6种类型,primary(默认),info,success,warning,error,text。
// button.vue
<template>
<button class="my-button" :class="{[`my-button-${type}`]: true}">
<slot>按钮</slot>
</button>
</template>
<script>
// 工具函数,用于判断传入的值是否符合条件
import { oneOf } from '../../utils/assist'
export default {
name: 'Button',
data () {
return {
}
},
props: {
type: {
validator(value) {
return oneOf(value, [
'primary',
'info',
'success',
'warning',
'text',
'error',
])
},
type: String,
default: 'primary',
},
},
}
</script>
然后是不同type对应的样式
// button.scss --- 省略部分代码 ---
&-info {
background-color: $--color-info;
border-color: #ddd;
color: $--font-color-main;
&:focus,
&:hover {
color: rgba($--font-color-main, 0.7);
}
&:active {
color: rgba($--font-color-main, 0.9);
}
}
&-success {
background-color: $--color-success;
border-color: $--color-success;
&:focus,
&:hover {
background-color: rgba($--color-success, 0.7);
}
&:active {
background-color: rgba($--color-success, 0.9);
}
}
&-warning {
background-color: $--color-warning;
border-color: $--color-warning;
&:focus,
&:hover {
background-color: rgba($--color-warning, 0.7);
}
&:active {
background-color: rgba($--color-warning, 0.9);
}
}
&-text {
background-color: $--color-text;
border-color: $--color-text;
color: $--color-primary;
&:focus,
&:hover {
color: rgba($--color-primary, 0.7);
}
&:active {
color: rgba($--color-primary, 0.9);
}
}
&-error {
background-color: $--color-error;
border-color: $--color-error;
&:focus,
&:hover {
background-color: rgba($--color-error, 0.7);
}
&:active {
background-color: rgba($--color-error, 0.9);
}
}
3. disabled 禁用
该功能实现逻辑比较简单,使用button标签的原生disabled属性即可实现。
主要是写disabled状态对应的样式
<template>
<button
class="my-button"
:disabled="disabled"
:class="{
[`my-button-${type}`]: true,
[`my-button-${type}-disabled`]: disabled,
}"
@click="handleClick"
>
<slot>按钮</slot>
</button>
</template>
<script>
// 工具函数,用于判断传入的值是否符合条件
import { oneOf } from '../../utils/assist'
export default {
name: 'Button',
data() {
return {}
},
props: {
type: {
validator(value) {
return oneOf(value, [
'primary',
'info',
'success',
'warning',
'text',
'error',
])
},
type: String,
default: 'primary',
},
disabled: {
type: Boolean,
default: false,
},
},
methods: {
handleClick(event) {
console.log('按钮被点击', event)
this.$emit('click', event)
},
},
}
</script>
// button.scss 省略部分代码
&-primary {
background-color: $--color-primary;
border-color: $--color-primary;
// focus,hover的时,背景色变浅
&:focus,
&:hover {
background-color: rgba($--color-primary, 0.7);
border-color: rgba($--color-primary, 0.7);
}
&:active {
// active时,背景色变浅,且与focus,hover做区分
background-color: rgba($--color-primary, 0.9);
border-color: rgba($--color-primary, 0.9);
}
// disabled时,变为禁用状态
&-disabled {
cursor: not-allowed;
background-color: rgba($--color-primary, 0.5);
border-color: rgba($--color-primary, 0.5);
&:focus,
&:hover,
&:active {
background-color: rgba($--color-primary, 0.5);
border-color: rgba($--color-primary, 0.5);
}
}
}
根据type设置disabled样式,禁用状态下鼠标样式变为not-allowed,背景色增加50%的不透明度,上面样式代码是primary类型的,其他5个类型写法一样即可。
4. 尺寸size
设置了四个尺寸: large, medium(默认), small, mini。
尺寸的切换同样由绑定class来完成。
// button.vue 省略部分代码
<template>
<button
class="my-button"
:disabled="disabled"
:class="{
[`my-button-${type}`]: true,
[`my-button-${type}-disabled`]: disabled,
[`my-button-size-${size}`]: true
}"
@click="handleClick"
>
<slot>按钮</slot>
</button>
</template>
<script>
export default {
.......
props: {
size: {
validator(value) {
return oneOf(value, [
'large',
'medium',
'small',
'mini'
])
},
type: String,
default: 'medium',
},
}
}
</script>
// button.scss 省略部分代码
// 以下是size相关样式
......
&-size {
&-large {
height: 40px;
line-height: 40px;
}
&-medium {
height: 36px;
line-height: 36px;
}
&-small {
height: 32px;
line-height: 32px;
font-size: $--font-size-small;
}
&-mini {
height: 28px;
line-height: 28px;
font-size: $--font-size-small;
}
}
5. 可设置图标 icon
图标的使用方法参考iconfont的Font class引用方式,后续只要维护图标项目库即可做到图标更新。
从iconfont上选择图标添加至项目,然后下载到本地。
下载的文件解压到src/styles/font目录下,然后在styles/index.scss中引用。
// styles/index.scss
@import "./font/iconfont.css";
这样就能在组件上使用了,使用方式是<i class="iconfont icon-xxx" />
// button.vue 省略部分代码
<template>
<button
class="my-button"
:disabled="disabled"
:class="{
[`my-button-${type}`]: true,
[`my-button-${type}-disabled`]: disabled,
[`my-button-size-${size}`]: true
}"
@click="handleClick"
>
<i :class="['iconfont', `${icon}`]"></i>
<slot></slot>
</button>
</template>
<script>
export default {
......
// 图标
icon: {
type: String
}
......
}
</script>
6. 圆角按钮
功能比较简单,设置border-radius即可。
// button.vue 省略部分代码
<template>
<button
class="my-button"
:disabled="disabled"
:class="{
[`my-button-${type}`]: true,
[`my-button-${type}-disabled`]: disabled,
[`my-button-size-${size}`]: true,
[`my-button-size-${size}-round`]: round
}"
@click="handleClick"
>
<i :class="['iconfont', `${icon}`]"></i>
<slot></slot>
</button>
</template>
<script>
export default {
......
// 图标
round: {
type: Boolean,
default: false
}
......
}
</script>
// button.scss 省略部分代码
&-size {
&-large {
height: 40px;
line-height: 40px;
&-round {
border-radius: 20px;
}
}
&-medium {
height: 36px;
line-height: 36px;
&-round {
border-radius: 18px;
}
}
&-small {
height: 32px;
line-height: 32px;
font-size: $--font-size-small;
&-round {
border-radius: 16px;
}
}
&-mini {
height: 28px;
line-height: 28px;
font-size: $--font-size-small;
&-round {
border-radius: 14px;
}
}
}
7. 圆形按钮
实现思路与圆角按钮一样,设置border-radius为50%即可。
这里需调整min-width和padding,使其能达到width=height的条件。
// button.vue 省略部分代码
<template>
<button
class="my-button"
:disabled="disabled"
:class="{
[`my-button-${type}`]: true,
[`my-button-${type}-disabled`]: disabled,
[`my-button-size-${size}`]: true,
[`my-button-size-${size}-round`]: round,
[`my-button-size-${size}-circle`]: circle
}"
@click="handleClick"
>
<i :class="['iconfont', `${icon}`]"></i>
<slot></slot>
</button>
</template>
<script>
export default {
......
// 图标
circle: {
type: Boolean,
default: false
}
......
}
</script>
// button.scss 省略部分代码
......
&-size {
&-large {
height: 40px;
line-height: 40px;
&-round {
border-radius: 20px;
}
&-circle {
min-width: 40px;
border-radius: 50%;
}
}
&-medium {
height: 36px;
line-height: 36px;
&-round {
border-radius: 18px;
}
&-circle {
min-width: 36px;
border-radius: 50%;
}
}
&-small {
height: 32px;
line-height: 32px;
font-size: $--font-size-small;
&-round {
border-radius: 16px;
}
&-circle {
padding: 0 5px;
min-width: 32px;
border-radius: 50%;
}
}
&-mini {
height: 28px;
line-height: 28px;
font-size: $--font-size-small;
&-round {
border-radius: 14px;
}
&-circle {
padding: 0 5px;
min-width: 28px;
border-radius: 50%;
}
}
}
8. 加载中 loading
button内部新增一个loading图标,由用户传入的loading参数控制显示隐藏。注意与提供给用户的icon做下切换,使loading图标和icon参数的图标互斥。loading状态下,按钮被禁用,因此disabled属性由loading和disabled共同决定。- 用伪元素构造一个
layer覆盖在button上面,同时button的pointer-events设为none,这样能实现按钮无法点击的效果。
// button.vue
<template>
<button
class="my-button"
:disabled="disabled || loading"
:class="{
[`my-button-${type}`]: true,
[`my-button-${type}-disabled`]: disabled,
[`my-button-size-${size}`]: true,
[`my-button-size-${size}-round`]: round,
[`my-button-size-${size}-circle`]: circle,
[`my-button-loading-layer`]: loading
}"
@click="handleClick"
>
<i
class="iconfont icon-loading"
:class="{ [`my-button-loading`]: true }"
v-if="loading"
/>
<i :class="['iconfont', `${icon}`]" v-if="!loading"></i>
<slot></slot>
</button>
</template>
<script>
// 工具函数,用于判断传入的值是否符合条件
import { oneOf } from '../../utils/assist'
export default {
name: 'Button',
data() {
return {}
},
props: {
type: {
validator(value) {
return oneOf(value, [
'primary',
'info',
'success',
'warning',
'text',
'error',
])
},
type: String,
default: 'primary',
},
disabled: {
type: Boolean,
default: false,
},
size: {
validator(value) {
return oneOf(value, [
'large',
'medium',
'small',
'mini'
])
},
type: String,
default: 'medium',
},
// 图标
icon: {
type: String
},
// 圆角按钮
round: {
type: Boolean,
default: false
},
// 圆形按钮
circle: {
type: Boolean,
default: false
},
loading: {
type: Boolean,
default: false
}
},
methods: {
handleClick(event) {
console.log('按钮被点击', event)
this.$emit('click', event)
},
},
}
</script>
// button.scss 省略部分代码
&-loading-layer {
pointer-events: none;
&::before {
pointer-events: none;
content: '';
position: absolute;
left: -1px;
top: -1px;
right: -1px;
bottom: -1px;
border-radius: inherit;
background-color: rgba(255, 255, 255, 0.4);
}
}
结语
以上是vue知识复习之UI组件的第一篇,给大家介绍了最常见的组件Button的实现过程,运用到的Vue相关知识有父子组件通信,class与style绑定,slot插槽等。都是基础知识,难度不大,更多的是对css的处理,比如覆盖样式时的css权重设计,loading功能中利用伪元素覆盖在上面使其无法点击等。
脚手架请参考第一篇: juejin.cn/post/701798…
组件示例地址: oversnail.github.io/over-snail-…
git地址: github.com/overSnail/o…
涉及知识点及参考文章
1. (vue):class与style绑定
2. (vue):prop父子组件传参
3. (vue):slot插槽
4. (css):你对CSS权重真的足够了解吗
5. (css):巧用伪元素before和after制作绚丽效果