公司新开项目,所以很多东西可以重新规范化起来(原来的旧项目经历各种水平参差不齐的人修改,以及无eslint等,已经不堪入目),我主动将button组件揽下,之前一直没有机会去写这样的组件,正好借此机会学习学习。(由于是uniapp的项目,所以在使用方面也是遵循uniapp)
一、如何设计组件样式
大型的UI框架都有一个基本的设计原则跟规范,那么我们在设计组件的时候,也需要这样的东西。最直接的,就是通过跟UI沟通,确定好button组件的样式,如颜色、大小、形状等。那么我们就可以得出下面的基本样式(注:往后的样式中带&
都是写在zc-button里面):
$bt-color-default: #fff;
$bt-color-primary: #3c9cff;
$bt-color-success: #0BAA82;
$bt-color-error: #f6646c;
$bt-color-warning: #F79C03;
$bt-border-width: 2rpx;
$bt-border-radius: 16rpx;
.zc-button {
position: relative;
align-items: center;
display: inline-block;
box-sizing: border-box;
cursor: pointer;
}
由于uniapp的button会有一些原来的样式,所以为了妨碍我们的设计,我们需要重置这些样式并修改部分样式:
&-reset {
margin: 0;
padding: 0;
text-decoration: none;
background-color: inherit;
font-size: inherit;
color: inherit;
&::after {
border: none;
}
}
&-normal {
padding: 0 24rpx;
font-size: 28rpx;
}
在tempalte方面,我们的现阶段的内容如下:
<template>
<button class="zc-button zc-button-reset zc-button-normal">
<slot></slot>
</button>
</template>
那么在基础样式方面,我们就完成了全部的设计,接下来就是对其他内容的补充。
1、颜色
在上面的基础样式补充之后,只会出现一个只有文字的button,那么我们需要先丰富颜色这块的内容。我们定义了五种颜色选项,分别是:
default、primary、success、error、warning。对于default很简单,只需要下面写即可:
&-default {
color: #666;
background-color: $bt-color-default;
border-color: #979797;
border-width: $bt-border-width;
border-style: solid;
}
效果如下:
对于其他颜色来说,字体颜色是白色,那么背景色以及边框颜色是一样的,这时候就可以利用scss的@minin。
@mixin bt-default-style($color) {
color: $bt-color-default;
background-color: $color;
border-color: $color;
border-width: $bt-border-width;
border-style: solid;
}
那么其他颜色就可以写成下面的方式:
&-primary {
@include bt-default-style($bt-color-primary);
}
&-success {
@include bt-default-style($bt-color-success);
}
&-error {
@include bt-default-style($bt-color-error);
}
&-warning {
@include bt-default-style($bt-color-warning);
}
那么效果如下:
除此以外,我们的项目设计稿还有一种叫做镂空
的按钮,具体设计样式如下:
实现起来也是比较简单,如下:
&-plain {
background-color: $bt-color-default;
&.zc-button-primary {
color: $bt-color-primary;
}
&.zc-button-success {
color: $bt-color-success;
}
&.zc-button-error {
color: $bt-color-error;
}
&.zc-button-warning {
color: $bt-color-warning;
}
}
2、按钮大小
项目的设计,按钮主要是有三种,大,普通,小
。在设计大按钮的时候,我们喜欢按钮独占一行,并且占满屏幕,如下:
&-large {
display: block;
width: 100%;
height: 100rpx;
line-height: 100rpx;
padding: 0 30rpx;
font-size: 32rpx;
}
效果如下(为了演示,父元素设置了padding)
:
小的效果无差,就是稍微缩小一点:
&-small {
min-width: 148rpx;
height: 56rpx;
line-height: 56rpx;
padding: 0 16rpx;
font-size: 28rpx;
}
效果:
3、形状
这个没啥好提,就是按照设计稿来设计即可:
&-square {
border-radius: $bt-border-radius;
}
&-circle {
border-radius: 200rpx;
}
效果:
至此,已完成了button组件的样式设计,下面来看看如何整合这些样式。
二、如何整合组件样式
其实组合这些样式比较简单,首先我们先给现有的属性列一下props:
props: {
type: {
type: String,
default: 'default'
},
size: {
type: String,
default: 'normal'
},
plain: {
type: Boolean,
default: false
},
shape: {
type: String,
default: 'square'
},
}
其实就分成两类,一种是多属性
如:type、size、shape。一种是布尔值的
如:plain。两种类型可以分开实现,比如多属性
的,我们可以遍历的方式组合,如:
['type','size','shape'].map(item=> `zc-button-${this[item]}`)
// ['zc-button-default','zc-button-normal','zc-button-square']
而布尔值
类型的则需要判断过后才能决定是否添加class。那么我们就可以得到下面的Function:
generateClassName (fixed, change) {
const prefix = 'zc-button-'
const classNameArray = []
if (fixed) {
classNameArray.concat(fixed.map(item => `${prefix}${item}`))
}
if (change) {
change.forEach(item => {
this[item] && classNameArray.push(`${prefix}${item}`)
})
}
return classNameArray.joio(' ')
}
接着我们再写一个computed
,如下:
bemClasses () {
this.generateClassName(['type','shape', 'size'], ['disabled', 'plain'])
}
为什么要写computed
呢?因为绑定的时候不需要执行,而写函数的话,需要多写两个括号执行(有理有据,令人信服)。
三、还有其他松散的东西
-
比如button都会有
disabled、loading、
这些还是比较简单的,因为原来的uniapp里面的button组件就有的属性,我们只需要传值就行了。 -
有的时候,我们也需要提供text的属性,也就是如下:
<button
class="zc-button zc-button-reset"
:disabled="disabled"
:loading="loading"
:class="bemClasses"
@tap.stop="onClick"
>
<slot>
<text
class="bt-text"
:style="{ fontSize: textSize + 'rpx', color: textColor }"
>
{{ text }}</text
>
</slot>
</button>
这在很多UI库都会有相关的属性提供,是为了保证组件直接使用,不必都要写插槽写进文字。插槽的作用就不用多说了,直接是提供对内容的改造,比如可以在里面添加icon等。
- 有时候还需要提供button样式修改能力,这样就可以让使用者写出
五彩斑斓黑的按钮
,我们需要做点改动:
<template>
<button
class="zc-button zc-button-reset"
:disabled="disabled"
:loading="loading"
+ :style="color"
:class="bemClasses"
@tap.stop="onClick"
>
<slot>
<text
class="bt-text"
:style="{ fontSize: textSize + 'rpx', color: textColor }"
>
{{ text }}</text
>
</slot>
</button>
</template>
<script>
/**
* ZcButton button组件
* @description 基于现有设计稿的button组件(按钮文字可以写插槽)
* @property {String} type button按钮的类型:default,primary,success,error,warning
* @property {String} size button按钮大小:large,normal,small
* @property {Boolean} disabled button按钮是否禁用
* @property {Boolean} loading button按钮加载中状态
* @property {String} color button按钮自定义颜色
* @property {String} plain button按钮是否镂空
* @property {String} shape button按钮形状:square,circle
* @property {String} text button按钮文字
* @property {String} textSize button按钮文字大小
* @property {String} textcolor button按钮文字颜色
*/
export default {
name: 'ZcButton',
props: {
type: {
type: String,
default: 'default'
},
size: {
type: String,
default: 'normal'
},
disabled: {
type: Boolean,
default: false
},
loading: {
type: Boolean,
default: false
},
color: {
type: String,
default: ''
},
plain: {
type: Boolean,
default: false
},
shape: {
type: String,
default: 'square'
},
text: {
type: String,
default: '按钮'
},
textSize: {
type: String,
default: '28'
},
textColor: {
type: String,
default: ''
}
},
emits: ['click'],
data () {
return {}
},
computed: {
// 生成classes
+ bemClasses () {
+ if (!this.color) {
+ return this.generateClassName(['type', 'shape', 'size'], ['disabled', 'plain'])
+ } else {
+ return this.generateClassName(['shape', 'size'], ['disabled', 'plain'])
+ }
+ }
},
methods: {
onClick () {
this.$emit('click')
},
generateClassName (fixed, change) {
const prefix = 'zc-button-'
let classNameArray = []
if (fixed.length > 0) {
classNameArray = classNameArray.concat(fixed.map(item => `${prefix}${this[item]}`))
}
if (change.length > 0) {
change.forEach(item => {
this[item] && classNameArray.push(`${prefix}${item}`)
})
}
return classNameArray.join(' ')
}
}
}
</script>
<style lang="scss">
@import "./button.scss";
</style>
无他为手熟尔~
总结
至此,一个通用化button组件就完成了。这个组件的设计大致可以总结到下面步骤:
- 确定组件的需求。我这个组件还是比较简单的,没有过多的样式设计,可以参考ElementUI等框架,他们button组件能力远远不止这些。
- 设计组件的实现。咱这个设计,相信大部分人都可以搞的出来,因为难度其实没多少,代码实现也简单,要说有点难度的,我觉得最多是css。
最后我说一句,不要因为参考了别人库的组件设计,就打算跟别人设计的差不多,还得要按照实际需求出发,因为多余的功能你真用不上,而且还有可能费时费力。而且组件都是在实际使用中,慢慢完善的,那些UI库也是如此。