整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

CSS绘制三角形—border法(详例)

CSS绘制三角形—border法(详例)

. 实现一个简单的三角形

使用CSS盒模型中的border(边框)即可实现如下所示的三角形:

CSS实现简单三角形

实现原理:

首先来看在为元素添加border时,border的样子;假设有如下代码:

<div></div>

div {
    width: 50px;
    height: 50px;
    border: 2px solid orange;
}

效果图:

border的一般使用

这是我们平常使用border最普遍的情况——往往只给border一个较小的宽度(通常为1-2px);然而这样的日常用法就会容易让大家对border的形成方式产生误解,即认为元素的border是由四个矩形边框拼接而成。

然而事实并不是这样。实际上,元素的border是由三角形组合而成,为了说明这个问题,我们可以增大border的宽度,并为各border边设置不同的颜色:

div {
    width: 50px;
    height: 50px;
    border: 40px solid;
    border-color: orange blue red green;
}

效果图:

border的形成方式

既然如此,那么更进一步,把元素的内容尺寸设置为0会发生什么情况呢?

div {
    width: 0;
    height: 0;
    border: 40px solid;
    border-color: orange blue red green;
}

效果图:

元素内容尺寸为0

我们将惊奇地发现,此时元素由上下左右4个三角形“拼接”而成;那么,为了实现最终的效果,即保留最下方的三角形,还应该怎么做?很简单,我们只需要把其它border边的颜色设置为白色透明色

div {
    width: 0;
    height: 0;
    border: 40px solid;
    border-color: transparent transparent red;
}

最终效果

Duang~ 最终的简单三角形就绘制出来了。同理,如果想要得到其它边上的三角形,只需要将剩余的border边颜色设置为白色或透明色即可。

不过,被“隐藏”的上border仍然占据着空间,要想使得绘制出的三角形尺寸最小化,还需要将上border的宽度设置为0(其它情况同理):

div {
    width: 0;
    height: 0;
    border-width: 0 40px 40px;
    border-style: solid;
    border-color: transparent transparent red;
}

2. 实现带边框的三角形

带边框的三角形是指为三角形添加其它颜色的边框,如同为元素添加border一样:

带边框的三角形

由于不能继续通过为已有三角形设置border的方法来为其设置边框(因为三角形本身就是利用border实现的),所以只好另想办法。而能想到的一个最自然的方法就是三角形叠放,即把当前三角形叠放在更大的三角形上方,上图所示的实现方法就是把黄色三角形放在了尺寸更大的蓝色三角形上。

为了实现这样的效果,需要利用绝对定位方法: 首先定义出外面的蓝色三角形:

<div id="blue"><div>

#blue {
    position:relative;
    width: 0;
    height: 0;
    border-width: 0 40px 40px;
    border-style: solid;
    border-color: transparent transparent blue;
}

效果为:

外围蓝色三角形

随后需要定义黄色三角形,由于黄色三角形的定位需要参考蓝色三角形的位置,所以需要用到绝对定位方法。为此还需要将黄色三角形作为蓝色三角形的子元素。一个可行的办法是在蓝色三角形内部定义一个额外的标签以表示黄色三角形,但为了节约标签起见,更好的办法是使用伪元素:

#blue:after {
    content: "";
    width: 0;
    height: 0;
    position: absolute;
    top: 0px;
    left: 0px;
    border-width: 0 40px 40px;
    border-style: solid;
    border-color: transparent transparent yellow;
}

得到的效果为:

定义黄色三角形

需要特别注意此时定义出的黄色三角形与蓝色三角形之间位置的偏移关系,该偏移将受到top、left(本例中)以及黄色三角形本身border宽度的共同影响。

可能会有这样的疑问:为什么黄色三角形会向左偏移一段距离呢,按道理不应该完全重合在蓝色三角形上吗,就像下面这样?

黄色三角形与蓝色三角形完全重合

如果有这样的疑问,说明还没有对绝对定位产生足够的认识。绝对定位的区域是基于绝对定位父元素的padding区域,然后在此基础上运用top、left、right、bottom等一系列属性来约束绝对定位子元素的位置。在本例中,由于蓝色三角形作为绝对定位父元素,其内容的尺寸为0,则内容区域就是该三角形的上顶点:

绝对定位区域

对于黄色三角形,由于设置了left: 0和top: 0,所以黄色三角形的所有内容(包括border、margin)将根据蓝色三角形的上顶点进行定位。可以把此时left: 0和top: 0分别看作是两面“隔墙”——即上隔墙和左隔墙,黄色三角形的所有内容只能在上隔墙的下方和左隔墙的右方区域。

由于黄色三角形的内容区域也位于其顶点处,且对其设置了左右各40px的border,所以黄色三角形的内容区域将向右偏移40px,从而形成了之前的效果。

想想看将黄色三角形的位置设置为left: 0和bottom: 0,会得到怎样的定位效果?(下图所示)

黄色三角形设置为left: 0和bottom: 0

搞懂了绝对定位后,只需要在原代码上稍作修改就可以将黄色三角形的顶点与蓝色三角形顶点相重合,同时还应该适当缩小黄色三角形的尺寸(按相似三角形等比例缩小):

#blue:after {
    content: "";
    position: absolute;
    top: 0px;
    left: -38px;
    border-width: 0 38px 38px;
    border-style: solid;
    border-color: transparent transparent yellow;
}

得到:

黄色三角形与蓝色三角形顶点重合

在上面的代码中,特意删除了之前对width: 0和height: 0的设置,因为子元素具有position:absolute设置,这会使得元素尺寸在不显式设置宽度和高度的情况下,收缩到元素内容的尺寸,由于内容设置的是content: "",所以子元素的尺寸默认也就是0了。故设置width: 0和height: 0就变得多余了。

最后一步就是利用top将黄色三角形向下移动至合适的位置:

#blue:after {
    content: "";
    position: absolute;
    top: 1px;
    left: -38px;
    border-width: 0 38px 38px;
    border-style: solid;
    border-color: transparent transparent yellow;
}

得到最终效果:

最终效果

学会了带边框三角形的绘制,那么实现类似如下三角形箭头自然也是不在话下了:

三角形箭头

实现代码:

#blue:after {
    content: "";
    position: absolute;
    top: 2px;
    left: -38px;
    border-width: 0 38px 38px;
    border-style: solid;
    border-color: transparent transparent #fff;
}

3. 绘制其它角度的三角形

绘制其它角度的三角形,如:

code :
.mate::before{
    content: '';
    width: 0;
    height: 0px;
    border-bottom: 32px solid blue;
    border-right: 20px solid transparent;
    position: absolute;
    top: 0px;
    left: 0px;
}

下图:
.mate::before{
    content: '';
    width: 0;
    height: 0px;
    border-top: 32px solid #d72ce8;
    border-right: 20px solid transparent;
    position: absolute;
    top: -1px;
    left: -3px;
}



就更简单了,其实它们都是基于之前绘制的三角形而来的。如果想绘制右直角三角,则将左border设置为0;如果想绘制左直角三角,将右border设置为0即可(其它情况同理)。



原文链接:https://www.jianshu.com/p/9a463d50e441




目前,教学、教研各种内容线上沉淀、展示丰富多彩,但线上内容“线下化”能力不足或过分依赖人力,比如,线上练习题组卷后以PDF形式分发给学生,家长希望将考试、练习题目打印后,学生带到学校去做(高中生使用手机等电子设备的时间有限),线上各类分析报告以PDF形式分享给学生/家长等。


从业务方面看,不同业务线的多个业务场景都有输出PDF的诉求,如果各业务线自己设计、实现符合自身业务场景的具体方案,除调研、开发工作量较大之外,还会有重复调研,踩坑的情况。


从技术角度看,线上内容转PDF的内容源头来自于H5富文本内容,业界内以此为基础的PDF生成方案多种多样,也各有优劣,比如:


方案对比-表格-1


因此,我们综合了各种PDF生成方案并总结了在探索讲义生成PDF过程中的经验,抽象出了一套通用的,可复用的能力供各业务线快速利用,基本方案和优劣如下:


最终方案-表格-2


目 标




旨在提供一套以H5为载体的PDF通用生成方案,这套方案有如下特点:

  1. 通用性强:能够处理各类H5页面,从分页到生成,做到一套方案,多般兼容。
  2. 扩展性、配置性强:各场景可根据自己的需要自定义页眉、页脚、页码,水印,背景等配置,做到输出形式丰富多彩。
  3. 方便易接入:各业务场景只需关注要展现的内容,无需关注分页,PDF生成等背后的处理 ,为需要产出PDF的业务场景提效赋能;整体来看,调研、设计、开发(+踩坑)一整套 H5 转 PDF的能力至少需要近 30人/日,我们希望这套通用能力的接入成本控制在 7人/日左右;在很多场景接入后,从实际反馈来看,平均只需要 2-3 人/日就接入了。
  4. 质量高:保证输出PDF中内容的展示与H5中无异,各种复杂公式的展示也丝毫无差错。
  5. 性能好:保证 1 分钟内能处理 100+ 的 20页左右的PDF生成任务
  6. 稳定性高:保证有各种兜底策略妥善处理各类异常,同时能够通过限流方案应对突发流量,保证服务稳定。


这套方案可分为两个核心部分,页面展示侧 - Medusa,PDF生成侧 - Hydra


页面展示侧 - Medusa




我们页面展示侧的通用能力——Medusa,是基于Paged.js的二次封装,并以NPM包形式提供给业务方使用。Medusa可对任何HTML进行分页、并根据配置添加页眉、页脚等,最终将处理后的HTML渲染到页面中。Medusa封装并简化了对PDF格式的配置,可覆盖绝大多数业务场景,使得各业务场景将更多精力投入其自身业务逻辑的开发。


之所以选择Pagedjs为基础开发我们自己的SDK,是因为它是目前我们能找到的唯一开源的、具有HTML内容分页,样式处理的前端库,同时我们也在讲义中经过了长期的摸索与沉淀。


接下来将详细介绍Paged.js原理、Medusa支持的功能与使用方法。


一 Paged.js是如何工作的




Paged.js包含了 3 个大模块

  • Chunker(负责HTML内容分页)
  • Polisher (负责CSS样式处理)
  • Previewer (负责预览呈现Chunker和Polisher处理后的内容)

这里将主要介绍 Previewer 和 Chunker,因为我们的二次开发和维护不涉及到Polisher。


Previewer

Previewer 的工作非常简单,但我们会主要利用它封装我们的Medusa,初始化一个Previewer对象,Previewer初始化了Chunker和Polisher对象:


Medusa-代码-1


再调用Previewer的preview()方法,preview()方法做了两件事:

  1. 通过Polisher处理样式内容
  2. 通过Chunker处理需要分页的HTML内容,如果没有指定需要分页的HTML,则会处理整个Body的内容

Medusa-代码-2


当chunker.flow结束,即可在浏览器看到整个页面处理完之后的样子。


Chunker


首先,Chunker解析、预处理需要分页的HTML,为其添加一些必要的属性


Medusa-代码-3


然后创建容纳所有页(pages)的容器,并挂载到renderTo容器下(默认Body),以备组织后续的所有页:



Medusa-代码-4


接着,chunker创建了一个page模版,以便增加页面使用:


Medusa-代码-5


其中,TEMPLATE是Pagedjs内部创建页面时所使用的基础模版。


Medusa-代码-6


接下来,chunker进入了渲染+分页过程(这个过程我们不会在二次开发中做修改,但需要了解其基本思路以便在出问题时能有解决思路),这个过程在循环一个迭代器(*layout),迭代器一直在做3件事:


  1. 将内容添加到模版内容区域的容器中 -> 渲染。
  2. 探测overflow,找到overflow的offset并创建BreakToken (探测overflow过程中很多处都用到了迭代器,此处为了说明思路,简化了相关代码)。


原则:

寻找overflow时会将尽可能多的内容节点插入内容区域,这里,“尽可能多”分为几种情况,比如:

  • 没有剩余节点需要再添加了
  • 达到了一页所能承载的最大字符数;刚开始的时候,如果没有指定每页的最大字符数,Pagedjs会给一个默认值为 1500 的每页最大字符用做判断,在之后会记录分隔好的每一页中的字符数,并取最近4页 (少于4页取全部)的平均值作为之后分页的判断条件,这里,Pagedjs相当于对每一页中能够承载的内容做了一个简单的预测,这个算法对于比较规律的内容做分页时还是比较简单高效的。

步骤:

Pagedjs遵循了如下步骤去寻找overflow:

两个前置条件:

  • 内容区域盒子边界已经确定,下面以contentArea.right 和 contentArea.bottom 分别代指其右边界和下边界。
  • 处理过程中每个节点的边界可以计算(对于文字节点,Pagedjs中使用了Range对象为其创建边界),下面以 node.left、 node.right、node.top 和 node.bottom 分别代指节点的左、右、上、下边界。

i. 从需要处理的内容第一个节点开始,判断是否 node.left >=contentArea.right || node.top >=contentArea.bottom


Medusa-代码-7


ii.如果不满足,则判断 node.right <=contentArea.right && node.bottom <=contentArea.bottom


Medusa-代码-8


iii.如果不满足,那说明有子节点overflow了,则继续深入其子节点查找即可。


3.使用模版添加新的页面,并从BreakToken处继续上述动作。


二 Medusa支持的功能及使用方法




基于Paged.js,Medusa支持了如下功能,并为业务方提供了更加简洁、定制化的配置。


  1. 动态页面分页能力
  2. 单页模版配置 -> 生成能力
  3. 前、后置静态页面生成、与分页后的动态页面拼接能力
  4. 页面处理成功后,通知PDF生成服务(Hydra)执行任务


下方是调用Medusa的代码示例:


Medusa-代码-9


1.1 动态页面分页能力

Medusa核心功能,可将连续的HTML页面转化成一页页PDF样式的HTML。


1.2 单页模版配置 -> 生成能力


通过Grid布局,Paged.js将一个单页模版分为多个区域,整体分为2个大的部分:

  1. base 页面基础配置:每个PDF纸型、水印,内容区域的宽高、margin与padding等等
  2. surround 页面周围区域:如页眉、页脚等配置


业务方通过简单的配置,即可还原UI设计稿中的PDF样式,例子如下图:



1.2.1 base

页面基础配置是对每页的。支持纸型或页面宽高、内容区域margin、padding、背景及水印的设置。



在封装Medusa时,Medusa将读取传入的页面模版配置、静态页内容配置,并将样式上的配置解析并转化为Previewer可理解的样式内容,比如页面宽高的设置:


Medusa-代码-10


将被转化为:


Medusa-代码-11


1.2.2 surround


  1. 可以看到图中的16种不同位置的surround区域。通过设置position,可将业务方自定义的元素渲染到对应的位置上。



2. 目前支持3种类型的surround item:

  • text 文字
  • img 图片
  • pageNum (动态获取)当前页码


example:


Medusa-代码-12


1.3 前/后置静态页面


业务方可通过如下方式配置静态页面的具体内容:


Medusa-代码-13


其中,传入的React JSX Element将会被这样处理:


Medusa-代码-14


处理完成后,将HTML String拼接到页面模版中,再插入分页后内容的前后。


PDF生成侧 - Hydra:




页面展示侧为PDF生成做好了页面的准备,对于PDF生成侧,需要做的工作就更纯粹了,业务方除了请求生成PDF,定期检查PDF生成的进度,无需做任何额外工作。


1.整体流程:

PDF生成是CPU和内存密集型的,由于页面内容的不确定性,也意味着页面渲染时间与生成PDF的时间都是不确定的,因此整体PDF生成的链路被设计成是异步的,如下图:



整体流程上,业务方在请求生成PDF时,会先在后端做一条记录,后端再将任务发送给Node服务,即Hydra;


在生成PDF时, 第 1 步是做页面上的准备,一个生成任务可能有多个URL页面需要生成PDF,所以我们预先启动对应URL数量的PPTR Page,页面都启动完成后,进入下一步;


第 2 步:渲染页面,这个过程中,如果请求是包含多个URL的,这些页面会同步渲染,在所有页面渲染完成后,进入下一步。


第 2.5 步,如果是需要生成连续页码的一整个PDF,还会做额外的一个动作:页码矫正,通过页码矫正,可以将同步渲染的每个页面,按照其之前页面的页码数修正,以保证整体PDF的页码的连贯。


第 3 步,通过PPTR Page的能力将页面转换为PDF buffer,如有必要,再将生成的PDF buffer拼接到一起生成一整个PDF,或者将每个PDF buffer都生成一个PDF,压缩成zip文件。


第 4 步,文件上传OSS,最终返回OSS CDN链接。


2.请求生成PDF:


业务侧请求将对应页面生成PDF的时,只需传入如下字段:


Hydra-代码-1


3.PDF生成过程:


正如在整体流程中所述,PDF生成侧,我们借助 PPTR 的能力打开页面并生成PDF流。


在页面调用 Medusa 分页、组装能力时,所有内容分页组装完成后会向body中插入了一个额外的DOM以标识该页面处理完成:


Hydra-代码-2


这是为了 Hydra 感知页面渲染完成所做的准备,当生成服务的 PPTR 等到该DOM出现时,则表示页面成功渲染并处理完成了:


Hydra-代码-3


此后,在上面已经提到过,对于需要将多个页面生成的PDF拼接成一个PDF的情况,在生成PDF之前需要做一个重要的动作,即页码矫正,原因如下:


  1. 每个页面无法感知其他页面情况的,如:第二个页面不知道第一个页面会生成多少页的PDF。
  2. 它们的页码需要是连续的。


并且我们不希望页面的处理是串行的,因为串行势必导致速度较慢,生成时间长。


这个问题的解决方案如下:

1. 对于每个页面都启用一个page,并同时处理

2. 每个页面处理完成后(pdfLastDOM出现),通过Page.$eval()来统计页数并记录:

Hydra-代码-4


3. 计算出页面中分页之后每一个页面的起始页码,以及所有页面的页码总和

4. 再修改页码容器样式的 counterReset 值即可,其后续页码可自递增。


Hydra-代码-5


5. 之后,再通过 Medusa 在页面window对象中Polyfill的相关配置,比如需要生成的PDF的单页宽、高以生成PDF流。


Hydra-代码-6


6. 最后如有必要,通过pdf-lib拼接这些 pdfBuffer 即可。


Hydra-代码-7


7. PDF生成完成后,上传OSS并返回URL链接


4.性能、稳定性保证:


在整体方案落地前,我们对服务进行了多次性能测试:


以下载题目为例,在4个容器,每个容器 3C 12G 的配置下的并行处理能力如下:


对于 20 道题目,每个PDF生成任务在 15 页左右,平均 1 分钟内能完成 280 个任务的处理。

对于 40 道题目,每个PDF生成任务在 30 页左右,平均 1 分钟内能完成 105 个任务的处理。

对于 60 到题目,每个PDF生成任务在 40 页左右,平均 1 分钟内能完成 54 个任务的处理。


同时,根据 Hydra 服务的整体的处理能力,后端通过任务队列的形式帮助我们保证服务不被瞬间的突刺流量击垮。


已接入/正在接入的相关业务线及场景:




目前,公司有 5 大业务线,8 个场景已经完全接入我们的能力用于 H5 转 PDF,如下是错题本、内容资料库接入后生成的PDF样例:


错题本:




内容资料库试卷:




未来展望




目前整体的PDF生成方案已经能够满足大多数场景和内容,但依然有可改进空间。


HTML的流式布局要求我们必须手动的对内容分页,才能添加页眉,页脚等(即Mdusa做的工作),正因为如此,在处理复杂的内容时,可能会出现一些问题:比如,遇到复杂表格时,由于表格可能会有多种多样的行、列合并,同时表格单元格内的内容也可以多种多样,在分页过程中,Medusa内部的PagedJS并不能完美的处理对于长、且复杂的表格的分割,因此可能遇到分割后表格单元格缺失、错乱或宽高错误的问题,这些问题在讲义中体现较明显。


我们仍在持续关注与研究复杂DOM内容的分割问题,会尝试加以优化和改进PagedJS的能力,同时,我们也以另外一种思路设计了自己的DOM分页器方案,但经过评估,由于实现比较复杂,成本较高,暂时没有投入开发资源。


不过,我们相信,未来我们一定能以更完美的方式分割DOM以生成更高质量的PDF。


作者:高源、陈欣博

来源:微信公众号:高途技术

出处:https://mp.weixin.qq.com/s/c_N7jdNklrNFKR_Cub2Tgg

ss一般用于布局排版,不过在某些场合css也可以方便地生成几何图形,比如下面这样的图形:

几何图形的生成方案

当我们想要显示上面这些几何图形,怎么做呢?有很多种方案,一般是插入一个图片,或者用js在canvas上画一个,也可以用上次讲的图标字体来做,哪种方案好呢?其实各种做法都有优缺点:

  • 图片:简单,没啥技术含量,但是要另外调用尺寸较大的图片文件,也不方便变换大小和颜色,典型的体力活儿
  • canvas:优点是灵活性大,但手工绘制有点麻烦,跟图文排版混入也有些不方便
  • 图标字体:形状有限,要另配字库文件,单独制作也很麻烦
  • 纯CSS的解决方案:简单,快速高效,灵活,无需配合其他技术,也便于跟图文的混排,但主要靠动脑筋来解决问题,有时候弄复杂了就比较烧脑

几何图形的纯CSS解决方案

所以今天开始,我们就说一些用纯css的解决方案,从核心技术上,大致可分3种:

1、 以边框特性为核心,配合旋转和拼接

2、 以clip-path为核心的剪切手法

3、 以内联SVG为核心的解决方案

以边框特性为核心生成几何图形的原理

今天讲的是第一种方案,其原理非常有意思,我们先做一个三角形了解下:

html代码,1个div块就够了

<div class="SanJiaoU"></div>

css代码如下:

.SanJiaoU {

border-left: 70px solid transparent;

border-right: 70px solid transparent;

border-bottom: 120px solid #e00;

}

好了,一个漂亮的三角形出现了

是不是很神奇呢?现在来说一下它的原理,你就恍然大悟了!

原理很巧妙,仔细看这个手绘图,它利用的是矩形的边框在4个角上天然形成的斜边交界当矩形设置成宽高都是0的时候,主体没了,但还剩下边框的4个角连在一起可见,此时只要把不需要的边框部分设置成透明,剩下的就是我们要的三角了。

在前面的例子里,上下左右4条边框我们只设置了3个,其中上边框无,左右边框宽70颜色为透明,下边框宽120颜色为红色,于是就显示出红色下边框与左右透明边框交界形成的三角了,它的底边宽是70x2=140,高是120。

几何图形应用示例

原理明白了,那么刚才是正三角,倒三角怎么做呢?类推一下,显然应该是由上边框与左右透明边框来形成,也就是把 border-bottom 改成 border-top,其余不变

.SanJiaoD {

border-left: 70px solid transparent;

border-right: 70px solid transparent;

border-top: 120px solid #e00;

}

成功:

好,现在我们就开始神奇的CSS形状养成之旅吧!

我把形状一个个贴出来,你先动动脑筋看自己能不能琢磨出来,然后参考下我的css代码和提示,这个过程还是蛮有意思的。

.SanJiaoL {

border-top: 70px solid transparent;

border-bottom: 70px solid transparent;

border-right: 120px solid #e00;

}

向左的三角是以右边框为主体形成的

.SanJiaoR {

border-top: 70px solid transparent;

border-bottom: 70px solid transparent;

border-left: 120px solid #e00;

}

向右的三角是以左边框为主体形成的

嗯,看着没难度,现在变一下,直角三角形怎么弄?其实更简单,直角只需2个边框就可以构成,看看这两个图形:

.SanJiaoZUL {

border-left: 120px solid transparent;

border-bottom: 120px solid #e00;

}

.SanJiaoZUR {

border-right: 120px solid transparent;

border-bottom: 120px solid #e00;

}

倒过来呢?

.SanJiaoZDL {

border-left: 120px solid transparent;

border-top: 120px solid #e00;

}

.SanJiaoZDR {

border-right: 120px solid transparent;

border-top: 120px solid #e00;

}

稍微变通一下,试试立体的,这个是指南针形状:

/*上面2个尖*/

.SanJiaoU2 {

border-left: 20px solid transparent;

border-bottom: 90px solid #d00;

position: relative;

}

.SanJiaoU2::after {

content: "";

border-right: 20px solid transparent;

border-bottom: 90px solid #a00;

position: absolute;

}

/*下面2个尖*/

.SanJiaoD2 {

border-left: 20px solid transparent;

border-top: 90px solid #d00;

position: relative;

}

.SanJiaoD2::after {

content: "";

border-right: 20px solid transparent;

border-top: 90px solid #a00;

position: absolute;top:-90px;

}

原理很简单,2个直角三角形左右拼接,颜色不同,然后上下再拼接。

注意啊,这里用了after这个伪类,目的就是拼接下一个形状。由于有拼接,所以就需要通过

position和left,top这几个属性来控制2个元素的位置,详细看代码,在后面的示范中,我们会大量用到这个技巧。

.JianTou1 {

width: 80px; height: 80px;

transform: rotate(-45deg);

box-shadow: 25px 25px 0px 0 red;

}

这是一个箭头,我们先用box-shadow这个属性做一个直角,然后用 transform: rotate 旋转45度,得到这个箭头。这个技巧后面也会大量使用,请留意!

.JianTou2 {

width: 80px; height: 80px;

transform: rotate(-45deg);

box-shadow: 25px 25px 0px 0 red;

position: relative;

}

.JianTou2::after{

content: "";

width: 80px; height: 80px;

box-shadow: 25px 25px 0px 0 red;

position: absolute;left:40px;top:40px;

}

会做一个箭头,当然就会做2个箭头,用after拼接一下就行了

.JianTou3 {

width: 160px; height: 80px;

background:

linear-gradient(-135deg, transparent 27px, #e00 0) top right,

linear-gradient( -45deg, transparent 27px, #e00 0) bottom right;

background-size: 100% 50%;

background-repeat: no-repeat;

border-radius:10px;

color:#fff;font-size: 32px;line-height: 80px;padding-left: 30px;

}

导航箭头,这个代码是用 linear-gradient 在矩形的右侧上下各切了一个角做成的。

.TiXing{

width: 90; height: 0;

border-left: 60px solid transparent;

border-right: 60px solid transparent;

border-bottom: 60px solid #e00;

}

梯形跟三角形的区别,仅仅是width非0而!

.PingXing{

width: 160px; height: 60px; margin-left: 30px;

transform: skew(-45deg);

background:#e00;

}

平行四边形,是用 transform: skew 把正常的矩形推歪一个角度即可,简单吧!

好,前面还都是比较简单的形状,现在来点复杂的

.LiuJiao{

border-left: 70px solid transparent;

border-right: 70px solid transparent;

border-top: 115px solid #e00;

position: relative;

}

.LiuJiao::after{

content: "";

border-left: 70px solid transparent;

border-right: 70px solid transparent;

border-bottom: 115px solid #e00;

position: absolute;left:-70px;top: -155px;

}

六角形,是2个三角形,一上一下,一正一反,通过after拼接

.WuBian{

border-left: 73px solid transparent;

border-right:73px solid transparent;

border-bottom: 53px solid #e00;

position: relative;

}

.WuBian::after{

content:""; width: 90; height: 0;

border-left: 28px solid transparent;

border-right:28px solid transparent;

border-top: 86px solid #e00;

position: absolute;top:53px;left:-73px;

}

五边形,是三角形加梯形

.BaBian{

width: 140px; height: 60px;

background-color: #e00;

position: relative;

}

.BaBian::before,.BaBian::after{

content:""; width: 60; height: 0;

border-left: 40px solid transparent;

border-right:40px solid transparent;

position: absolute;

}

.BaBian::before{ top: 60px;border-top: 40px solid #e00; }

.BaBian::after { top:-40px;border-bottom: 40px solid #e00; }

八边形,我是用矩形加一前一后两个梯形,用的是before和after来拼接前后梯形。

.Xin{

width: 80px;height: 80px;

background-color: #e00;

transform: rotate(-45deg);

position: relative;left:30px;

}

.Xin::before,.Xin::after{

content: "";width: 80px;height: 80px;

border-radius:80px;

background-color: #e00;

position: absolute;

}

.Xin::before{ left: 0px;top:-40px;}

.Xin::after { left:40px;top: 0px;}

心形,是以一个方块为核心(转45度),用before和after外加2个圆组成的,有意思吧?

.PingXingXie{

width: 160px; height: 60px;

transform: rotate(-45deg) skew(-45deg);

background:#e00;

}

斜的平行四边形,显然是rotate和 skew 两个变形处理合起来造成的。

.ShuiDi{

width: 60px; height: 60px;

border: 20px solid #e00;

border-radius: 50px 0 50px 50px;

transform: rotate(-45deg);

}

空心的水滴,看着很复杂,其实很简单,正方形的4个角,把其中3个设置成圆角,然后整体旋转45度让尖朝上就可以了,核心就是一句 border-radius: 50px 0 50px 50px; 好奇妙啊!

.ShuiDi1{

border: 50px solid #e00;

border-radius: 50px 0 50px 50px;

transform: rotate(-45deg);

}

实心的水滴,跟前面的手法略有不同,我是把正方形的边长弄成0,然后直接用边框形成的!

.ShuiDi2{

width: 60px; height: 60px;

border: 20px solid #e00;

border-radius: 50px 0 50px 50px;

transform: rotate(45deg);

}

.ShuiDi2::after{

width: 60px; height: 60px;

content: "";

border: 20px solid #e00;

border-radius: 50px 0 50px 50px;

transform: rotate(-180deg);

position: absolute; top: -100px;left: 60px;

}

这是水滴的另一个变通使用,2个水滴拼接而成

.LingXing{

width: 100px; height: 100px;

background-color: #e00;

transform: rotate(45deg);

}

菱形,就是正方形旋转一下

.ZuanShi{

border-left: 70px solid transparent;

border-right: 70px solid transparent;

border-bottom: 40px solid #e00;

position: relative;

}

.ZuanShi::after{

content: "";

border-left: 70px solid transparent;

border-right: 70px solid transparent;

border-top: 80px solid #e00;

position: absolute; left:-70px;top: 40px;

}

钻石,是2个三角拼接,代码有点粗暴,可能有更好的做法

.ZuanShi1{

width:70px;

border-left: 25px solid transparent;

border-right: 25px solid transparent;

border-bottom: 40px solid #e00;

position: relative;

}

.ZuanShi1::after{

content: "";

border-left: 60px solid transparent;

border-right: 60px solid transparent;

border-top: 80px solid #e00;

position: absolute; left:-25px;top: 40px;

}

平头的钻石,是梯形和三角拼接

.JianTou{

width:10px;

border-left: 5px solid transparent;

border-right: 5px solid transparent;

border-bottom: 100px solid #e00;

position: relative;left:50px;

}

.JianTou::after{

content: "";

border-left: 30px solid transparent;

border-right: 30px solid transparent;

border-top: 20px solid #e00;

position: absolute; left:-25px;top: 100px;

}

箭头,仍然是梯形和三角的拼接,异曲同工!

.Zui{

border: 50px solid red;

border-radius: 50px;

position: relative;

transform: rotate(-45deg);

}

.zui::after{

content: "";

width:50px;height: 50px;

background-color: #fff;

position: absolute;

}

大嘴,圆形拼接正方形(旋转)

.DunPai{

width: 120px; height: 160px;

background-color: red;

border-radius: 80% 80% 80% 80% / 20% 20% 80% 80%;

}

盾牌,通过调整border-radius圆角的弧度而形成

.MsgBox{

width: 250px;min-height:40px;

border-radius: 15px;

background-color: #e00;

position: relative;left:20px;

padding:20px;color:#fff;font-size:24px;

}

.MsgBox::after{

content: "";

width: 20px;height: 20px;

background-color: #e00;

position: absolute; top:30px;left:-10px;

transform: rotate(-45deg);

}

聊天的气泡,圆角矩形与正方形(旋转45度)拼接

.Hua12{

width: 100px;height:100px;

background-color: #e00;

position: relative;left: 30px;

}

.Hua12::before,.Hua12::after{

content: "";

width: 100px;height: 100px;

background-color: #e00;

position: absolute;left:0;top:0;

}

.Hua12::before{transform: rotate(30deg);}

.Hua12::after {transform: rotate(60deg);}

大红花!是3个正方形旋转角度后拼接而成

.TaiJi{

width: 160px;height: 80px;

border-radius:50%;

border: #e00 solid;

border-width: 1px 1px 80px 1px;

position: relative;left: 30px;

}

.TaiJi::before,.TaiJi::after{

content: "";

width:20px;height: 20px;

border-radius:50%;

position: absolute;top:40px;

}

.TaiJi::before{

border:30px solid #e00;

background-color: #fff;

left:0px;

}

.TaiJi::after{

border:30px solid #fff;

background-color: #e00;

left:80px;

}

太极图,这个我可是思考了半天才想出来的,他其实是3个圆圈组成,最技巧的地方在于如何只用一个圆形做出一半阴一半阳的效果来,先卖个关子,其实代码里已经体现出来了,哈哈!

.SouSuo{

width: 80px;height: 80px;

border: 15px solid #e00;

border-radius:50%;

position: relative;

}

.SouSuo::after{

content: "";

width: 50px;height: 15px;

background-color: #e00;

transform: rotate(45deg);

position: absolute;top:80px;left:60px;

}

搜索符号,一个圆圈加一个矩形(旋转45度)做手柄

.YueYa {

width: 80px; height: 80px;

border-radius: 50%;

box-shadow: 25px 25px 0px 0 red;

}

月牙,一个圆圈,外面加一个阴影,自然就形成了月牙状!

.ShanXing{

border-left: 80px solid transparent;

border-right: 80px solid transparent;

border-top: 100px solid red;

border-radius: 50%;

}

扇形,其实是一个边框构成的倒三角,用border-radius弄出来圆弧后,就成了扇形

.ShiZi{

width: 80px; height: 80px;

box-shadow: 25px 25px 0px 0 red;

position: relative;

}

.ShiZi::after{

content: "";

width: 80px; height: 80px;

box-shadow: -25px -25px 0 0 red;

position: absolute;left:105px;top:105px;

}

红十字,先用box-shadow弄出L形,再用两个L形状旋转拼接而成

.Suo{

width:160px;height: 100px;

background-color: #e00;

border-radius: 20px;

position: relative;

}

.Suo::before{

content: "";

width:60px;height: 60px;

border:20px solid #e00;

border-radius: 100px 100px 0 0;

position: absolute;left:30px;top:-80px;

}

.Suo::after{

content: "";

width: 0px;height: 30px;

border:10px solid #fff;

border-radius: 20px;

position: absolute;left:70px;top:30px;

}

锁,是3个矩形组合而成,1个矩形做锁身,一个大圆角矩形做锁环,一个小圆角矩形做钥匙孔

.KuoHao{

width: 10px;height: 100px;

border: 10px solid #e00;border-right: 0;

border-radius: 20px 0 0 20px;

position: relative;left:50px;

}

.KuoHao::after{

content: "";

width: 20px; height: 20px;

transform: rotate(45deg);

box-shadow: -10px 10px 0px 0 #e00;

background-color: #fff;

position: absolute;left:-5px;top:40px;

}

大括号,是半拉圆角矩形(细长),叠加一个L形尖角构成。

好了,以边框特性为核心生成几何图形的示例就到这里,主要还是以示范原理为主。实际使用中可以有更多、更巧妙的创意、更优化的代码。

关于css生成几何图形,还有以clip-path为核心的剪切手法,以及以内联SVG为核心的解决方案,我们后续另文再详细讲解,内容相信会更加精彩!