美文网首页
darkti UI—popover气泡卡片

darkti UI—popover气泡卡片

作者: darkTi | 来源:发表于2020-06-23 19:27 被阅读0次

    一、写前需要思考的

    1、 它有两个需求,一是点它出现一个div,二是div里可以出现一些内容;
    2、 思路:首先想象一下用户该如何用我们的组件(也就是在template里面他该怎么写)
    3、 popover最难的一点,你的元素div要如何出现在卡片上方;
    4、 它难就难在要考虑很多浏览器方面的样式行为,也就是css方面;

    二、先实现最简单的功能(点击按钮外部关闭内容元素)

    1、 思路:当你点击按钮,使内容出现,与此同时,你需要给document绑定一个点击事件,当你第二次点击按钮或者popover外面的区域时,就要执行此事件,并同时把此事件从document上解绑定;你不解绑的话,事件越绑越多;

    • 默认的插槽是不需要包起来的;
    • 一定要异步给document添加函数(用setTimeout),否则同步的话,直接运行下来又把visible变成false了,内容永远都不会出来;
    • ()=>{}相当于function(){}.bind(this),而且要清楚function x(){}function x(){}.bind(this)是两个不同的函数!!!!!!!
    • 阻止冒泡,主要是阻止冒到document上面触发x函数;但是阻止冒泡是个很危险的选择,一般我们应该让用户自己抉择是否冒泡,不应该在轮子组件中阻止冒泡;

    2、在popover组件上加阻止冒泡会有bug,像下面的代码,<d-popover>外上的点击事件是无法触发到的;

    <div @click="yyy">
          <d-popover>
               <template v-slot:content>
                   <div>这是一段内容</div>
                   <div>nctwayv内容</div>
               </template>  
               <button>点我</button>
          </d-popover>
     </div>
    

    3、不能把内容元素跟按钮写在一起,一旦按钮div上有个overflow:hidden,那么这个元素div就会被遮盖住;所以我们给它一个ref,通过ref获取到元素,再把元素append到document里;

    • 这里又要注意一点,一旦你把内容元素append到document里,它就脱离了d-popover的作用域,所以css中,不能把content-wrapper写在popover里了,应该挪出来;而且要获取按钮的top、left来给内容元素赋值top、left;
    • 当你移动vue组件中一个元素的位置时,是不会影响这个元素的功能的,只是改变了位置;

    4、不是单单直接赋值top、left就可以了,有一种情况,当document的长度和宽度均超过视图时,就不能单单只是赋值top、left了,所以垂直方向上需要再加上window.scrollY(水平方向同理);


    垂直方向.png

    5、到此还有bug,就是当你点击按钮关闭内容元素时,它不会去执行document上的那个x函数去关闭,而是直接执行的this.visible = !this.visible这句话去关闭的元素,然后document上的x函数一直没有执行也没有被删除;

    • 所以我们要获取用户点击的哪里,点击按钮或按钮外部去关闭执行的函数不一样;
    • 怎么获取你点击的是哪个元素?把点击事件本身打印出来看,e.target
    • 因为我们把popover上的阻止冒泡去掉了,所以现在点击内容元素的话它还是会消失,所以我们需要给document的绑定事件判断一下条件,当点击的是内容元素时,我们什么都不要做
    • 把关闭入口收拢,做到高内聚,低耦合!!!!
    • 不要使用this.visible = !this.visible,通过this.visible = true / false控制各个条件下的操作;
    • 需要给document的绑定函数一个执行范围,当它发现点击的范围在内容区域时,就不做任何操作
    documentEvent(e){
       if(this.$refs.contentWrapper && this.$refs.contentWrapper.contains(e.target) ){return}
       this.close()
    },
    close(){
       //只要关闭内容元素,就要把document上的事件移除
       this.visible = false
       document.removeEventListener('click', this.documentEvent)
    },
    
    tips.png
    • 为什么可以做到点击第二个按钮时,第一个按钮的内容元素会关掉?因为点击第二个按钮的时候回同时触发document上的事件,就会把第一个按钮的内容元素关闭了!!!!

    三、支持四个位置

    1、先优化内容元素的样式;

    • 给contentWrapper设置max-width: 20em; word-break: break-all;(英文网站最好不要加word-break: break-all;

    2、但是仔细看,三角箭头哪里是没有阴影的,所以就不能用box-shadow了,用filter: drop-shadow(0 0 3px rgba(51,51,51,0.4)); background-color: white;;

    image.png
    3、表驱动编程!!!
    把多个if ....else if....写成excel表一一对应的形式;
    表驱动编程.png

    四、动态绑定一个事件(click/hover)

    1、添加props: trigger,hover时对应mouseenter、mouseleave事件;
    2、你没有办法直接在div上给它v-bind一个事件变量,因为v-bind只接受固定的事件名,所以你只能在mounted钩子函数中通addEventListener来触发不同事件;
    3、你在mounted钩子函数中添加事件,vue是不知道的,所以你还需要手动在beforeDestory钩子里面移除事件;(!!注意:不能在destoryed钩子里,因为destoryed时DOM已完全消除完毕,是获取不到元素的)
    4、到此还有一个小bug,hover时,放到那个三角箭头那里时,会有问题;

    bug.png
    image.png

    五、其他知识点

    1、 v-if和v-show区别: v-if会改变它是否存在于DOM树中,而v-show只改变它的display样式(v-show是一直存在于DOM树中的);
    2、有时让插槽内容能够访问子组件中才有的数据是很有用的,具体的可见文档:作用域插槽https://cn.vuejs.org/v2/guide/components-slots.html

    //d-user组件内
    <span>
      <slot v-bind:user="user">
        {{ user.lastName }}
      </slot>
    </span>
    
    export default {
        data(){
         return {
                user: 'doudou'
              }
         }
    }
    
    //使用组件
    <d-user>
      <template  v-slot:default="slotProps">   //当然slotProps这个名字你可以自己随便取
          {{slotProps.user}}     
      </template>
    </d-user>
    

    相关文章

      网友评论

          本文标题:darkti UI—popover气泡卡片

          本文链接:https://www.haomeiwen.com/subject/ibrlnhtx.html