资讯专栏INFORMATION COLUMN

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

赵春朋 / 3250人阅读

摘要:第四集从零开始实现组件本集定位之前一直在忙别的事情现在终于闲下来好好把这个库的文章写一下本篇目的是承接上文把组件的功能全部实现为添加按钮的很重要现在一般的按钮都带个图案因为这样符合人脑的快捷思维方便理解与记忆图标按钮

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

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

1: 为button添加icon
按钮的icon很重要, 现在一般的按钮都带个图案,因为这样符合人脑的快捷思维, 方便理解与记忆.

 

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

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

2: icon的位置
很少遇到需要上下左右布局的icon, 如果需要的话.
方案一: 移动标签, 或是具名slot标签
方案二: 多写几个icon组件, 通过判断决定显示谁

3: hover效果
方案一:

hover的时候出现灰色蒙层效果

被点下时, 按钮缩小动画

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

效果图

方案二:

hover出现金属光泽, bulingbuling的,金属光泽.

被点下时, 按钮缩小动画

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

尝试使用渐变的背景颜色, 然后用 ackground-position-x 控制背景的右移
此方案实践起来不舒服, 而且并没有做到"解耦合"的设计原则, 所以不用了

伪元素, 用一个伪元素加上背景色, 然后把这个元素从左至右划过, 同时用动画把它做得倾斜30度
这样虽然多了个元素, 但是跟父级解耦了, 值得说的一点是, 倾斜之后高度不够盛满了, 简单粗暴的
方式就是把高度定位多一些的, 这样旋转也不会导致高度不够的状态了.

let"go

在button.scss;里面添加

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

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

  @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: 防抖与节流
介绍: 这种节流与防抖都是用户自己做的, 至少按钮这种东西本套组件库就是要组件来做.
使用场景:

有一次,我写注册登录页面, 如果用户在注册的时候, 快速的点击了两下, 虽然跳到登录成功页面, 但是会弹出弹框, "该手机已被注册", 原来是由于第一个请求把手机注册了, 所以接下来的点击事件的请求后台当然返回的是已注册, 所以这里就需要这样的处理, 每次点击有效之后的点击n秒内无效, 防止连点, (防止变速齿轮, 想起了流星蝴蝶剑);

我们公司19年搞了个抢购活动, 可能发生这样的场景, 用户守着抢购按钮, 不断地点击, 我们web端需要在每次用户点击的时候询问后台"活动开始了么?", 没开始就给用户弹tosat(下一章就做这个组件了),开始了才进入活动也或是订单页, 但是, 用户如果是个金手指, 疯狂把玩抢购键, 那就会发出大量的请求了,

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

   原因是用户的本地时间并不是一个值得信赖的量, 平时可以作为一个参考, 但是像抢购这种分秒必争的事情, 就要让用户与服务器时间同步起来了.

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

   原因是某些浏览器环境下, 用户切出程序之类的一些操作, 他会杀掉计时器, 导致计时不准, 而这种问题暂时无法解决, 也监控不到, 所以为了分秒必争保险起见.

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

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

 

接值

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

具体的使用

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

下一篇

toast的封装, 通过封装toast让我巩固了好多"芝士"

组件的另一种写法

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

github:链接描述
个人网站: 链接描述

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105845.html

相关文章

  • 第二集: 从零开始实现一套pcvueui组件(icon组件)

    摘要:第二集从零开始实现组件本集定位这套组件我本来是先从做的但是我发现每个组件都要用到这个组件如果没有他很多组件没法扩展而且本身不依赖其他组件所以还是先把它作为本篇文章的重点吧组件读过源码的同学都知道他们选择的是字体图标的方式来做组件的而我的这 第二集: 从零开始实现(icon组件) 本集定位: 这套ui组件我本来是先从button做的, 但是我发现每个组件都要用到icon这个组件, 如...

    dack 评论0 收藏0
  • 第二集: 从零开始实现一套pcvueui组件(icon组件)

    摘要:第二集从零开始实现组件本集定位这套组件我本来是先从做的但是我发现每个组件都要用到这个组件如果没有他很多组件没法扩展而且本身不依赖其他组件所以还是先把它作为本篇文章的重点吧组件读过源码的同学都知道他们选择的是字体图标的方式来做组件的而我的这 第二集: 从零开始实现(icon组件) 本集定位: 这套ui组件我本来是先从button做的, 但是我发现每个组件都要用到icon这个组件, 如...

    wua_wua2012 评论0 收藏0
  • 第三集: 从零开始实现一套pcvueui组件(button组件其一)

    摘要:第三集从零开始实现组件本集定位为什么要叫那因为我感觉这个组件细节比较多应该会讲很多内容所以先把基础功能在这一集实现下集去做拓展组件这是一个基本上每个工程都会用到的组件传统的千篇一律的样式仿佛按钮不做的一样就没法用似的我偏要加一些别人没加过的 第三集: 从零开始实现(button组件1) 本集定位: 为什么要叫1那, 因为我感觉这个组件细节比较多, 应该会讲很多内容, 所以先把基础功...

    chnmagnus 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<