likes
comments
collection

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

作者站长头像
站长
· 阅读数 15

写在前面

在元宇宙、数字孪生等一系列数字化转型噱头的疯狂炒作下,前端界面可以说是越来越飘逸,越来越有科技感,使用普通的开源、通用组件库已经满足不了丧心病狂 的 UI 设计师了,这个时候,SVG 就该登场了,在之前一度觉得 SVG 没啥用,很多教程写了很多画圆,画长方形啥的,看来看去并不知道对实际开发有啥帮助,直到自己真正用起来,才体会到 SVG 真香,虽然 SVG 并不难,但是如果能够将 SVG 灵活运用起来,将给我们带来极其舒适的感受 !

SVG 实战例子,基于 Vue2

UI 需求

UI 设计师:“emmm,我要一个长方形,然后长方形四个角切出来,但是切出来之后三角形保留着。。。。。”

气短工程师:‘中指-中指-中指’,“好的呢,没问题,这就给您实现,可能没那么快哦,亲麻烦您给点时间哇!”

UI设计师:“行,那就下午午休后给我看看吧!”

气短工程师:“。。。。。”

实现思路

1、DIV + CSS-Border

这种实现方式应该是很多人最容易想到的实现思路

元素结构
<template>
  <div class="box">
    <div class="rect">
      <div class="corner corner1"></div>
      <div class="corner corner2"></div>
      <div class="corner corner3"></div>
      <div class="corner corner4"></div>
    </div>
  </div>
</template>
构造三角形

首先通过 CSS 中的 border 样式构造四个三角形

.corner {
    position: absolute;
    width: 0px;
    height: 0px;
    border: 30px solid #000;
}
.corner1 {
    top: -31px;
    left: -31px;
    border-top-color: transparent;
    border-bottom-color: transparent;
    border-left-color: transparent;
    border-right-color: white;
}
.corner2 {
    top: -31px;
    right: -31px;
    border-top-color: transparent;
    border-bottom-color: transparent;
    border-left-color: white;
    border-right-color: transparent;
}
.corner3 {
    bottom: -31px;
    left: -31px;
    border-top-color: white;
    border-bottom-color: transparent;
    border-left-color: transparent;
    border-right-color: transparent;
}
.corner4 {
    bottom: -31px;
    right: -31px;
    border-top-color: transparent;
    border-bottom-color: transparent;
    border-left-color: white;
    border-right-color: transparent;
}
调整三角形位置,覆盖长方形四个角

通过 transform:rotateZ() 控制三角形的位置对 DIV 进行覆盖完成长方形切角

.corner {
  position: absolute;
  width: 0px;
  height: 0px;
  border: 30px solid #000;
}
.corner1 {
  top: -31px;
  left: -31px;
  transform: rotateZ(45deg);
  border-top-color: transparent;
  border-bottom-color: transparent;
  border-left-color: transparent;
  border-right-color: white;
}
.corner2 {
  top: -31px;
  right: -31px;
  transform: rotateZ(-45deg);
  border-top-color: transparent;
  border-bottom-color: transparent;
  border-left-color: white;
  border-right-color: transparent;
}
.corner3 {
  bottom: -31px;
  left: -31px;
  transform: rotateZ(45deg);
  border-top-color: white;
  border-bottom-color: transparent;
  border-left-color: transparent;
  border-right-color: transparent;
}
.corner4 {
  bottom: -31px;
  right: -31px;
  transform: rotateZ(45deg);
  border-top-color: transparent;
  border-bottom-color: transparent;
  border-left-color: white;
  border-right-color: transparent;
}
实现方式的不足

这种写法有个很大的问题是只能写出填充的切角长方形,如果想写出没有填充色,只有边框的长方形切角,就会显得非常吃力,而且对于长方形的位置的调节,在自适应的页面内,非常容易因为位置偏移而“暴露”切角底部的长方形的角

// 完整代码
<template>
  <div class="box">
    <div class="rect">
      <div class="corner corner1"></div>
      <div class="corner corner2"></div>
      <div class="corner corner3"></div>
      <div class="corner corner4"></div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Yimwu-test',
}
</script>

<style lang='scss' scope>
  .box {
    width: 100vw;
    height: 100vh;
    * {
      padding: 0;
      margin: 0;
    }
    .rect {
      margin-left: 500px;
      width: 500px;
      height: 200px;
      background: cadetblue;
      position: relative;
      .corner {
        position: absolute;
        width: 0px;
        height: 0px;
        border: 30px solid #000;
      }
      .corner1 {
        top: -31px;
        left: -31px;
        transform: rotateZ(45deg);
        border-top-color: transparent;
        border-bottom-color: transparent;
        border-left-color: transparent;
        border-right-color: white;
      }
      .corner2 {
        top: -31px;
        right: -31px;
        transform: rotateZ(-45deg);
        border-top-color: transparent;
        border-bottom-color: transparent;
        border-left-color: white;
        border-right-color: transparent;
      }
      .corner3 {
        bottom: -31px;
        left: -31px;
        transform: rotateZ(45deg);
        border-top-color: white;
        border-bottom-color: transparent;
        border-left-color: transparent;
        border-right-color: transparent;
      }
      .corner4 {
        bottom: -31px;
        right: -31px;
        transform: rotateZ(45deg);
        border-top-color: transparent;
        border-bottom-color: transparent;
        border-left-color: white;
        border-right-color: transparent;
      }
    }
  }
</style>

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

2、SVG 实现切角组件

通过 SVG 实现切角框的方式就显得优雅很多,通过使用SVG中的 <polygon> 多边形,向 points 传入四个切角对应的坐标便可以优雅地控制切角大小

定义结构
<template>
  <div class="Chamfering-container" ref="ctn">
    <svg class="svg">
      <polygon :points="polygonPoint" :style="{
        fill: background,
        strokeWidth: `${borderWidth}px`,
        stroke: borderColor,
      }" />
    </svg>
  </div>
</template>
定义组件参数
props: {
  // 角顺序:左上-右上-右下-左下
  corner: {
    type: Array,
    default: () => {
      return [30,0,30,0]
    }
  },
  // 边框宽度
  borderWidth: {
    type: Number,
    default: 1
  },
  // 边框颜色
  borderColor: {
    type: String,
    default: '#0CE5F6'
  }
  // 填充颜色
  background: {
    type: String,
    default: 'rgba(12,229,246,.5)'
  },
},
Polygon 坐标点计算
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
    // 计算每个角对应的两个点的坐标
    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}`
    return [
      LT,
      RT,
      RB,
      LB
    ].join(',')
  }
},
切角框组件完整代码
<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 [30,0,30,0]
      }
    },
    borderWidth: {
      type: Number,
      default: 1
    },
    background: {
      type: String,
      default: 'rgba(12,229,246,.5)'
    },
    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>
效果图

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

拓展

可能上面展示的例子看起来过于简单,中规中矩,没啥新颖的地方,那么下面提供几个我已实现的变种类型供大家尝试实现一下,后期有空再更新新类型代码,欢迎在评论区说出你的想法呀 !

带切角的切角框

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

底部凸起切角框

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

底部、顶部凸起切角框

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

总结

SVG 虽然并不难,很多教程也都做了比较详细的解读,也算是初级的前端技术,但是依然非常值得花点时间学习。灵活运用 SVG 进行开发,不仅能够方便我们的开发,提升效率,也能够实现更加酷炫,更加科技感的组件库,从而让我们轻松应对 UI 设计师天马行空的小脑袋 !!

往期好文推荐

面试官:说说从输入 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 哪个模式会刷新页面?

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

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

写在最后

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