Vue复习姿势系列之UI组件——按钮(Button)
介绍
最常见的操作按钮,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
功能中利用伪元素覆盖在上面使其无法点击等。
作者:狂野蜗牛
链接:https://juejin.cn/post/7018149193885679647