这么简单的需求别再说你不会做了好嘛!(ClipPath)

178 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第28天,点击查看活动详情

写最前面

相信大家在平时开发过程中,多多少少都有过被产品经理“刁难”的经历,随着产品越来越成熟,或者说随着面对的客户越来越高端、甲方要求越来越苛刻,开源的通用 UI 组件库的缺点已经明显显露出来。

开源组件库在开发以及对外开放的时候,所恪守的原则是通用、易用,而正是由于这些原则,使得通用组件库在公司更加高层的产品中,无法满足公司差异化、个性化等的追求,因此,在使用开源的通用组件库的时候,我们也时常会“偶遇”产品经理的奇思妙想,这个时候,用来应对这种“攻击”最好的“功法”是 SVG,接下来就让我们一起来看看如何使用 SVG 来“拆招”!!

需求分析

为了使得公司的产品在 UI 方面能够和其他产品区分开,那么就需要产品经理开动头脑,发挥想象力,最后驱动 UI 设计师,给前端们挖下一个一个的“坑”,那么接下来就来看看第一个坑。

在日常的前端界面编写中,最最常见的莫过于容器了,容器可以用于圈定范围,使得页面结构更加清晰,每个功能组件更加完整,因此,在个性化 UI 中,首当其冲的也就是相关的容器。最简单的容器可以是一个单纯的 DIV ,如图1,如果想要好看一点,那 UI 设计师可以在朴实无华的 DIV 上加上一些样式,如图2,而如果这样子的容器还是无法满足 UI 设计师呢,这个时候 UI 设计师开始对容器的角下主意,如图3,当 UI 设计师设计出切角框的时候,前端页面仔就得开始忙活起来了,下面我们开始来一步步实现这个需求!

image.png

image.png

image.png

需求实现

切角框可以使用 CSS 实现吗?

当我们看到切角框的时候,第一个想到的一定是 CSS ,我们仔细思考一下,如果采用 CSS 我们该如何实现呢?主要的思路应该是使用 CSS 构建四个三角形,然后通过平移和旋转后,与一个长方形相结合,达到切角的效果,而我们都知道 CSS 实现三角形依赖与 border 的特殊用法,因此在进行旋转与偏移的过程中,并没办法很好地控制偏移和旋转的量,因此实现的效果也不尽人意。

采用 SVG 实现切角框

当我们尝试了 CSS 的实现方式后,可以发现,如果要真正实现完美的切角框, 采用 CSS 是不现实的,不仅难以封装成通用组件,也无法完整实现 UI 设计师的需求。因此我们应该探索探索采用更加优雅的 SVG,来作为实现切角框的方式,使用 SVG 该如何实现呢,接下来我们来一步一步分析。

1、参数定义

首先当我们想要开发一个组件的时候,我们首先需要提前定义好组件相关的参数,对于这个切角框,我们需要定义的参数一共有4个,第一个是 background,用于控制切角框的里的背景颜色,第二个是 corner,用于控制切角大小,第三个是 borderWidth 用于控制边框大小,第四个是 borderColor,用于控制边框的颜色。

2、具体实现

其实实现方式并不复杂,第一步,通过 <svg> 标签定义一个 svg ,第二步,在 svg 中添加一个 polygon 标签,并将 borderWidth、borderColor、background 作为样式绑定上去,第三步,也就是最最关键的一步了,动态计算 组成 polygon的所有点。

计算 polygon 的点其实很简单,主要通过外容器的 width、height 以及每个 corner 的大小进行计算得出,最后按照顺序将四个角对应的一共8个点连接成点的字符串,传入到 polygon 中,也就能够实现切角框了,具体代码如下

<template>
  <div class="Chamfering-container" ref="ctn">
    <svg class="svg">
      <polygon :points="polygonPoint" :style="{
        fill: background,
        strokeWidth: `${borderWidth}px`,
        stroke: borderColor,
      }" />
    </svg>
  </div>
</template>

<script>
export default {
  props: {
    corner: {
      type: Array,
      default: () => {
        return [0,0,0,0]
      }
    },
    borderWidth: {
      type: Number,
      default: 1
    },
    background: {
      type: String,
      default: 'none'
    },
    borderColor: {
      type: String,
      default: '#0CE5F6'
    }
  },
  data(){
    return {
      width: 0,
      height: 0
    }
  },
  computed: {
    polygonPoint(){
      let { width,height } = this
      // SVG 坐标原因,需要加偏移才能完整显示边框
      let left = this.borderWidth / 2
      let top = this.borderWidth / 2
      let right = width - this.borderWidth / 2
      let bottom = height - this.borderWidth / 2
      console.log(width,height,left,top,right,bottom)
      let LT = this.corner[0] ? `${left} ${top + this.corner[0]}, ${left + this.corner[0]} ${top}` : `${left},${top}`
      let RT = this.corner[1] ? `${right - this.corner[1]} ${top}, ${right} ${top + this.corner[1]} ` : `${right},${top}`
      let RB = this.corner[2] ? `${right} ${bottom - this.corner[2]}, ${right - this.corner[2]} ${bottom}` : `${right} ${bottom}`
      let LB = this.corner[3] ? `${left + this.corner[3]} ${bottom}, ${left} ${bottom - this.corner[3]}` : `${left} ${bottom}`
      let res = [
        LT,
        RT,
        RB,
        LB
      ].join(',')
      console.log(res)
      return [
        LT,
        RT,
        RB,
        LB
      ].join(',')
    }
  },
  mounted(){
    this.$nextTick(()=>{
      this.width = this.$refs['ctn'].offsetWidth
      this.height = this.$refs['ctn'].offsetHeight
    })
  }
}
</script>

<style lang='scss' scope>
  .Chamfering-container {
    width: 100%;
    height: 100%;
    .svg {
      width: 100%;
      height: 100%;
    }
  }
</style>

切角框中放置图片

当我们实现了切角框后,会发现,如果我们想要在切角框中放置一张图片,图片并非如我们想象的那样,被切角框框在里面,而是直接超出了切角框,如下图,那么如何实现这个需求呢?这里我们需要利用一个 style 属性 clip-path,当我们给某个元素设置了 clip-path 后,元素将被该 path所切割,也就是我们所需要的效果,具体实现如下

image.png

svg 定义 clipPath

在 SVG 中创建一个 <defs>用于存放定义类标签,然后在该标签中定义一个 clipPath,该标签中包含的 polygon 与我们在上文画的 polygon 保持一致,这样,我们就能够实现将图片安装切角框切割的需求了。代码演示如下

<template>
  <div class="Chamfering-container" ref="ctn">
    <svg class="svg">
      <defs>
        <clipPath id="clipPath">
          <polygon :points="polygonPoint" />
        </clipPath>
      </defs>
      <polygon :points="polygonPoint" :style="{
        fill: background,
        strokeWidth: `${borderWidth}px`,
        stroke: borderColor,
      }" />
    </svg>
    <div class="slot">
      <slot></slot>
    </div>
  </div>
</template>

给 slot 设置 clip-path 属性

当定义好了 clipPath 后,最后一步就是给 slot 加上 clip-path 了,代码如下

<style>
  .slot {
    width: 100%;
    height: 100%;
    clip-path: url(#clipPath);
    position: absolute;
    top: 0;
    left:0;
  }
}

最终效果展示

image.png

往期好文推荐

Websocket 底层是 TCP 还是 UDP?白话版解析 TCP 和 UDP 传输过程

面试官:了解低代码平台吗?(数字孪生低代码平台探索)(五)

面试官:了解低代码平台吗?(数字孪生低代码平台探索)(四)

面试官:了解低代码平台吗?(数字孪生低代码平台探索)(三)

面试官:了解低代码平台吗?(数字孪生低代码平台探索)(二)

面试官:了解低代码平台吗?(数字孪生低代码平台探索)(一)

面试官:说说从输入 URL 到页面显示到底经历了什么,体现一下你的知识广度

面试官:作为前端,服务器相关了解多少?

面试官:HTTPS 采用的是对称加密还是非对称加密?具体说说其加密过程

面试官:说说 Cookie 和 Token 的区别?

面试官:网络安全了解多少,简单说说?(一)

面试官:网络安全了解多少,简单说说?(二)

面试官:网络安全了解多少,简单说说?(三)

面试官:网络安全了解多少,简单说说?(四)

面试官:网络安全了解多少,简单说说?(五)

面试官:网络安全了解多少,简单说说?(六)

面试官:网络安全了解多少,简单说说?(七)

面试官:网络安全了解多少,简单说说?(八)

浅尝 | 从 0 到 1 Vue 组件库封装

面试官:这么简单的正则表达式都不会?

Webpack 打包类库踩坑

面试官:你就只会 npm run build 吗?(Webpack 配置 Vue+Ts)

面试官:连VuePress都没搭过还说开发过组件库?(VuePress 搭建)

面试官: 连 Vue 视图更新都不会写?(Vue视图更新原理【一】)

面试官: 能不能手写 Vue 响应式?(Vue2 响应式原理【完整版】)

面试官:能不能手写 Vue3 响应式(Vue3 原理解析之响应系统的实现)

JS 优雅之道(JS 代码优化小 Tip)

面试官:你真的会用 SVG 吗? (SVG 应用实战)

面试官:说一下这个Loading动画实现思路 (CSS3 实现 Loading 动画)

JS 扫盲题 ( 面试题梳理系列 (一))

面试官:你确定你说的防抖不是节流吗?( 面试题梳理系列 (二))

面试官:除了 HTTP,你还用过什么通信协议?(Websocket 在数字孪生中的应用)

面试官:你真的理解 Event Loop 吗?( JS 事件循环 )

面试官:v-for 中 key 为什么不能用 index,从原理层面聊聊?

面试官:vue-router 的 hash 与 history 哪个模式会刷新页面?

面试官:说说你平时用过的自适应方案(数字孪生可视化自适应方案)

面试官:说一下如何优化过渡动画(数字孪生可视化过渡动画)

写在最后

博主接下来将持续更新好文,欢迎关注博主哟!!
如果文章对您有帮助麻烦亲点赞、收藏 + 关注和博主一起成长哟!!❤️❤️❤️