持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第28天,点击查看活动详情
写最前面
相信大家在平时开发过程中,多多少少都有过被产品经理“刁难”的经历,随着产品越来越成熟,或者说随着面对的客户越来越高端、甲方要求越来越苛刻,开源的通用 UI 组件库的缺点已经明显显露出来。
开源组件库在开发以及对外开放的时候,所恪守的原则是通用、易用,而正是由于这些原则,使得通用组件库在公司更加高层的产品中,无法满足公司差异化、个性化等的追求,因此,在使用开源的通用组件库的时候,我们也时常会“偶遇”产品经理的奇思妙想,这个时候,用来应对这种“攻击”最好的“功法”是 SVG,接下来就让我们一起来看看如何使用 SVG 来“拆招”!!
需求分析
为了使得公司的产品在 UI 方面能够和其他产品区分开,那么就需要产品经理开动头脑,发挥想象力,最后驱动 UI 设计师,给前端们挖下一个一个的“坑”,那么接下来就来看看第一个坑。
在日常的前端界面编写中,最最常见的莫过于容器了,容器可以用于圈定范围,使得页面结构更加清晰,每个功能组件更加完整,因此,在个性化 UI 中,首当其冲的也就是相关的容器。最简单的容器可以是一个单纯的 DIV ,如图1,如果想要好看一点,那 UI 设计师可以在朴实无华的 DIV 上加上一些样式,如图2,而如果这样子的容器还是无法满足 UI 设计师呢,这个时候 UI 设计师开始对容器的角下主意,如图3,当 UI 设计师设计出切角框的时候,前端页面仔就得开始忙活起来了,下面我们开始来一步步实现这个需求!
需求实现
切角框可以使用 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
所切割,也就是我们所需要的效果,具体实现如下
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;
}
}
最终效果展示
往期好文推荐
Websocket 底层是 TCP 还是 UDP?白话版解析 TCP 和 UDP 传输过程
面试官:说说从输入 URL 到页面显示到底经历了什么,体现一下你的知识广度
面试官:HTTPS 采用的是对称加密还是非对称加密?具体说说其加密过程
面试官:你就只会 npm run build 吗?(Webpack 配置 Vue+Ts)
面试官:连VuePress都没搭过还说开发过组件库?(VuePress 搭建)
面试官: 连 Vue 视图更新都不会写?(Vue视图更新原理【一】)
面试官: 能不能手写 Vue 响应式?(Vue2 响应式原理【完整版】)
面试官:能不能手写 Vue3 响应式(Vue3 原理解析之响应系统的实现)
面试官:说一下这个Loading动画实现思路 (CSS3 实现 Loading 动画)
面试官:你确定你说的防抖不是节流吗?( 面试题梳理系列 (二))
面试官:除了 HTTP,你还用过什么通信协议?(Websocket 在数字孪生中的应用)
面试官:你真的理解 Event Loop 吗?( JS 事件循环 )
面试官:v-for 中 key 为什么不能用 index,从原理层面聊聊?
面试官:vue-router 的 hash 与 history 哪个模式会刷新页面?
面试官:说说你平时用过的自适应方案(数字孪生可视化自适应方案)
写在最后
博主接下来将持续更新好文,欢迎关注博主哟!!
如果文章对您有帮助麻烦亲点赞、收藏 + 关注和博主一起成长哟!!❤️❤️❤️