第四集: 从零开始实现一套pc端vue的ui组件库(button组件其二)

第四集: 从零开始实现(button组件2)

本集定位:
以前一直在忙别的事情, 如今终于闲下来, 好好把这个库的文章写一下
本篇目的是承接上文, 把button组件的功能所有实现css

1: 为button添加icon
按钮的icon很重要, 如今通常的按钮都带个图案,由于这样符合人脑的快捷思惟, 方便理解与记忆.vue

<button class="cc-button"
          @touchstart='touchstart($event)'
          :class="[ 
          sizeType,
          type ? 'cc-button--' + type : '',
    {
      'is-left':left,
      'is-right':right,
      'is-centre':centre,
      'is-disabled':disabled,
    }]"
          :type="nativeType"
          @click="click">
    <!-- 图标按钮 -->
    <ccIcon v-if="icon"  // 有没有
            :name='icon' // 有什么样的
            :color="realyIconColor"  // 什么颜色的
            style="margin-right:2px" />  // 与文字有点距离
    <slot />
  </button>

计算realyIconColor
icon有默认的颜色, 可是若是按钮式禁用状态, 那么icon也要相应的置灰node

computed: {
    realyIconColor() {
      if (this.disabled) return "#bbbbbb";
      else return this.iconColor;
    }
  }

图片描述

2: icon的位置
不多遇到须要上下左右布局的icon, 若是须要的话.
方案一: 移动<slot>标签, 或是具名slot标签
方案二: 多写几个icon组件, 经过判断决定显示谁git

3: hover效果
方案一:github

  1. hover的时候出现灰色蒙层效果
  2. 被点下时, 按钮缩小动画
&:not(.is-disabled) {
        &:active {
            box-shadow: none;
            opacity: 0.7;
            transform: translateX(2px) translateY(2px) scale(0.9);
        }
         &:hover {
            background-color:rgba(0,0,0,0.1)
         }
    }

效果图
图片描述
方案二:web

  1. hover出现金属光泽, bulingbuling的,金属光泽.
  2. 被点下时, 按钮缩小动画

综上分析, 金属光泽流过可能成为一个公用属性, 那么我直接写一个公共的样式吧 "cc-bling"
个人思路:算法

  1. 尝试使用渐变的背景颜色, 而后用 ackground-position-x 控制背景的右移
    此方案实践起来不舒服, 并且并无作到'解耦合'的设计原则, 因此不用了
  2. 伪元素, 用一个伪元素加上背景色, 而后把这个元素从左至右划过, 同时用动画把它作得倾斜30度
    这样虽然多了个元素, 可是跟父级解耦了, 值得说的一点是, 倾斜以后高度不够盛满了, 简单粗暴的
    方式就是把高度定位多一些的, 这样旋转也不会致使高度不够的状态了.

let'go浏览器

<button class="cc-button"
  :class="[
    {
      'is-bling':bling, // 加了一个接受 是否金属光泽的属性
      'is-left':left,
      'is-right':right,
      'is-centre':centre,
      'is-disabled':disabled,
    }]"
  </button>
 bling:Boolean, // 条纹

在button.scss;里面添加服务器

@at-root {
        @include commonType(cc-button--);
        .is-bling {
        //此属性名在hover的时候才进行bling操做
            &:hover {
                @extend .cc-bling;
            }
        }
    };

animation.scss
定义一个从左至右的动画dom

@keyframes bling{
    0% {
      left: 0;
    }
    100% {
      left: 300%;
    }
  }

extend.scss
定义具体的样式把

.cc-bling {
    &:after {
        content: '';
        position: absolute;
        background-image: linear-gradient(to right, rgb(232, 229, 229), white);
        left: 0;
        top: -20px; // 避免倾斜的时候头部漏出尖角
        width: 15px;
        height: calc(100% + 30px); // 避免旋转时候出现高度不够的状况
        transform: rotate(-30deg);
        animation-name: bling;
        animation-duration: 1s; // 总用时
        animation-iteration-count: infinite; // 无限循环
        animation-timing-function: linear; // 匀速
    }
}

效果图, 是动态的, 从左至右划过.
图片描述
4: 防抖与节流
介绍: 这种节流与防抖都是用户本身作的, 至少按钮这种东西本套组件库就是要组件来作.
使用场景:

  1. 有一次,我写注册登陆页面, 若是用户在注册的时候, 快速的点击了两下, 虽然跳到登陆成功页面, 可是会弹出弹框, "该手机已被注册", 原来是因为第一个请求把手机注册了, 因此接下来的点击事件的请求后台固然返回的是已注册, 因此这里就须要这样的处理, 每次点击有效以后的点击n秒内无效, 防止连点, (防止变速齿轮, 想起了流星蝴蝶剑);
  2. 咱们公司19年搞了个抢购活动, 可能发生这样的场景, 用户守着抢购按钮, 不断地点击, 咱们web端须要在每次用户点击的时候询问后台'活动开始了么?', 没开始就给用户弹tosat(下一章就作这个组件了),开始了才进入活动也或是订单页, 可是, 用户若是是个金手指, 疯狂把玩抢购键, 那就会发出大量的请求了,

我来讲一下:
第一: 为何不使用后台返回的活动开始时间与本地的事件进行比对??

缘由是用户的本地时间并非一个值得信赖的量, 平时能够做为一个参考, 可是像抢购这种分秒必争的事情, 就要让用户与服务器时间同步起来了.

第二: 为何不在第一次请求以后把时间戳记录下来, 本地用计时器模拟计时??

缘由是某些浏览器环境下, 用户切出程序之类的一些操做, 他会杀掉计时器, 致使计时不许, 而这种问题暂时没法解决, 也监控不到, 因此为了分秒必争保险起见.

最后只能是每次都请求一下, 那就须要, 好比说 每600毫秒内, 只让用户的点击有效一次.

咱们就不能单纯的写click事件了,要抽离出来进行蹂躏☺️.
dom

<button class="cc-button"
     @click="click">
  </button>

接值

props: {
    ...,
    shake: Number, // 防抖的秒数
    throttle: Number, // 节流, 请输入秒数
    clickId: [String, Number], // 相同id的组件走一套计时.
}

事件

click() {
    // 根据用户的输入, 来决定怎么计时.
    // 值得一提的是, clickId 相同的话咱们是统一计时的, 
    // 好比说: 三个按钮, 点了其中一个, 其余的几个在规定时间内,都会不可点击
      let clickType, num;
      if (this.throttle) {
        clickType = 1;
        num = this.throttle;
      } else if (this.shake && this.shake > 0) {
        clickType = 2;
        num = this.shake;
      } else if (this.shake && this.shake < 0) {
        clickType = 3;
        num = this.shake * -1;
      }
      prevent(
        this.clickId,
        () => {
          this.$emit("click");
        },
        num,
        clickType
      );
    },

在以前的工做中本身写过一个防抖与节流的函数, 此次就直接拿来用了

let preventList = {}
const prevent = function(id, obj, time, model = 1) {
  switch (model) {
    case 1:
      model1(id, obj, time)
      break;
    case 2:
      model2(id, obj, time)
      break;
    case 3:
      model3(id, obj, time)
      break;
    default:
  }
}

// 模式1 无论点多少下每隔time秒,触发一次
function model1(id, obj, time) {
  if (preventList['can' + id]) return
  obj()
  preventList['can' + id] = true
  preventList['time' + id] = setTimeout(() => {
    preventList['can' + id] = false
  }, time)
}

// 模式2 每次动做都有time的延时再执行,也就是全部点击完事的时候执行一个
function model2(id, obj, time) {
  clearTimeout(preventList['time' + id])
  preventList['time' + id] = setTimeout(() => {
    obj()
  }, time)
}

// 默认的模式, 模式3, 第一下点击触发, 以后时间内不触发
function model3(id, obj, time) {
  if (preventList['can' + id]) {
    clearTimeout(preventList['time' + id])
  } else {
    obj()
    preventList['can' + id] = true
  }
  preventList['time' + id] = setTimeout(() => {
    preventList['can' + id] = false
  }, time)

}

export default prevent

具体的使用

图片描述

//后续涉及到防抖与节流的事件也都是走的这套程序;

下一篇

  1. toast的封装, 经过封装toast让我巩固了好多'芝士'
  2. 组件的另外一种写法

end
这段时间做者辞职, 专心学源码, 练习算法, 重学js, 重学node,重作本身的打包工具, 反正挺忙的,接下来的时间与精力主要放到这套组件上 同时也会出一些算法啊, 心得之类的文章, 欢迎同窗们一块儿交流, 一块儿变得更优秀.

github:连接描述
我的网站: 连接描述

相关文章
相关标签/搜索