如何设计一个通用化button组件

公司新开项目,所以很多东西可以重新规范化起来(原来的旧项目经历各种水平参差不齐的人修改,以及无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;

}

效果如下:

Snipaste_2023-02-08_15-36-08.png

对于其他颜色来说,字体颜色是白色,那么背景色以及边框颜色是一样的,这时候就可以利用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);
  }


那么效果如下:

Snipaste_2023-02-08_15-50-39.png

除此以外,我们的项目设计稿还有一种叫做镂空的按钮,具体设计样式如下:

Snipaste_2023-02-08_16-05-04.png

实现起来也是比较简单,如下:

  &-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)

Snipaste_2023-02-08_16-13-08.png

小的效果无差,就是稍微缩小一点:

  &-small {
    min-width: 148rpx;
    height: 56rpx;
    line-height: 56rpx;
    padding: 0 16rpx;
    font-size: 28rpx;
  }


效果:

Snipaste_2023-02-08_16-19-22.png

3、形状

这个没啥好提,就是按照设计稿来设计即可:

  &-square {
    border-radius: $bt-border-radius;
  }



  &-circle {
    border-radius: 200rpx;
  }


效果:

Snipaste_2023-02-08_16-25-15.png

至此,已完成了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组件就完成了。这个组件的设计大致可以总结到下面步骤:

  1. 确定组件的需求。我这个组件还是比较简单的,没有过多的样式设计,可以参考ElementUI等框架,他们button组件能力远远不止这些。
  2. 设计组件的实现。咱这个设计,相信大部分人都可以搞的出来,因为难度其实没多少,代码实现也简单,要说有点难度的,我觉得最多是css。

最后我说一句,不要因为参考了别人库的组件设计,就打算跟别人设计的差不多,还得要按照实际需求出发,因为多余的功能你真用不上,而且还有可能费时费力。而且组件都是在实际使用中,慢慢完善的,那些UI库也是如此。

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYvVxFXD' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片