收起左侧

[前端] 教你利用CSS 绘制各种形状

4
回复
[复制链接]
avatar
  • TA的每日心情
    qdsmile奋斗
    15 小时前
  • 签到天数: 2663 天

    [LV.Master]伴吧终老

    460

    主题

    1056

    帖子

    3万

    积分
    发表于 2017-5-6 10:19:32 | 显示全部楼层 |阅读模式
    利用CSS 绘制各种形状

    自适应的椭圆

    1.自适应的椭圆

    实现方式是通过border-radius这个属性;border-radius它可以单独指定水平和垂直半径。用 / 分隔这两个值。并且该属性的值不仅可以接受长度值,还能接收百分比的值。百分比的值会基于元素的尺寸进行解析,宽是水平半径的解析,高是垂直半径的解析。

    所以可以把两个半径的值都设置成50%;

    1. border-radius: 50% / 50%;
    2. /* 值都是50% 就可以简化成 border-radius: 50%; */
    复制代码

    最终的效果:

    564879.png


    2.半椭圆的实现

    border-radius的值是可以分开设置的,用空格进行分开,甚至可以为四个角提供完全不同的水平和垂直的半径。

    border-radius四个值的渲染顺序是从左上角开始,顺时针渲染;
    当只给定3个值时,第四个值和第二个相等;
    当给定两个值时,第三个值和第一个值相等,第四个值和第二个值相等。

    所以半椭圆的实现方法就是:

    1. border-radius: 50% / 100% 100% 0 0;

    2. /*
    3. *  会解析成下面的样子
    4.     border-top-left-radius: 50% 100%;
    5.     border-top-right-radius: 50% 100%;
    6.     border-bottom-right-radius: 50% 0px;
    7.     border-bottom-left-radius: 50% 0px;
    8. */
    复制代码

    最终效果:

    590a79b1cddg99.png

    同理的四分之一的椭圆的生成方法:

    1. /* 不能设置圆角的就设置成 0 */
    2. border-radius: 100% 0 0 0;
    复制代码

    最终效果:

    590a79b1ec509.png

    平行四边形的实现

    在生成平行四边形时,直接对当前对div使用transform: skew(-45deg);变形,会导致div里面的内容也会发生变形,就像下面这样:

    590a79b21727d.png

    通用的解决办法是通过伪元素的方法,就是把所有的样式用到伪元素上,对伪元素进行变形,由于内容不在伪元素里,所以内容不会受到影响。具体的实现如下:

    1. <div class="parallelogram">
    2.     click
    3. </div>
    复制代码
    1. .parallelogram {
    2.     text-align: center;
    3.     position: relative;
    4. }

    5. .parallelogram::before {
    6.     transform: skew(-45deg);
    7.     position: absolute;
    8.     left: 0;
    9.     right: 0;
    10.     top: 0;
    11.     bottom: 0;
    12.     z-index: -1;
    13.     content: ' ';
    14.     background: #fb3;
    15. }
    复制代码

    生成的效果如下:

    590a79b21727d.png

    这种技巧不仅对于skew()变形来说有用,还适用于其他任何变形样式,当想变形任何一个元素而不想改变它的内容的时候都可以用到它。


    菱形图片的生成

    在css中,有些需求是要把图片裁剪成菱形,使用css可以很好的解决这个问题,为不需要预先把图片裁剪成菱形而影响后期的维护。

    基于变形的方案

    直接使用类似平行四边形的方式旋转,给img设置max-width: 100%,会导致图片被裁剪成八边形。因为100%会被解析成div的边长。所以解决这个问题的办法就是让图片的宽等于div这个容器的对角线的长度,比较好的办法就是把图片用scale放大根号2倍(大概是1.42倍),具体实现的代码如下:

    1. <div class="picture">
    2.     <img src="cat.jpg" />
    3. </div>
    复制代码
    1. .picture {
    2.     width: 400px;
    3.     height: 400px;
    4.     transform: rotate(45deg);
    5.     overflow: hidden;
    6. }

    7. .picture1 > img {
    8.     max-width: 100%;
    9.     height: 400px;
    10.     transform: rotate(-45deg) scale(1.42);
    11. }
    复制代码

    效果如下:

    mm590a79b2529db.png

    scale和width放大的区别:scale的缩放图片是以它的中心点进行缩放的(在不指定transform-origin的前提下),通过width属性来放大图片时,只会以它左上角的原点进行缩放,然后用额外的负边距来调整图片的位置。

    裁切路径方案上面的办法还需要前套一层多余的html标签,使用clip-path这个属性,这个原本是svg的属性,使用polygon()这个多边形的函数,使用的百分比的值会解析为元素自身的尺寸。

    1. img {
    2.     clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
    3. }
    复制代码

    clip-path这个属性还能参与动画,只要动画是在同一个形状函数之间进行的,而且点的数量是相同的就可以,比如我们希望鼠标hover时平滑的扩展为完成的面积,只需这样:

    1. .picture {
    2.     clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
    3.     transition: 1s clip-path;
    4. }
    5. .picture:hover {
    6.     clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
    7. }
    复制代码
    切脚效果的实现

    径向渐变的方式

    实现一个角被切掉的效果,可以使用渐变,渐变接受一个角度作为方向,并且允许文字溢出并超出切角区域(因为它只是背景图案),具体实现如下:

    1. div {
    2.   background: #58a;
    3.   background: linear-gradient(-45deg, transparent 15px, #58a 0);
    4. }
    复制代码

    效果如下:

    590a79b29d681.png

    实现两个切角的办法:

    1. div {
    2.     width: 200px;
    3.     height: 100px;
    4.     background: #58a;
    5.     background: linear-gradient(-45deg, transparent 15px, #58a 0)
    6.                 right, linear-gradient(45deg, transparent 15px, #65a
    7.                  0) left;
    8.     background-size: 50% 100%;
    9.     background-repeat: no-repeat;
    10. }
    复制代码

    效果如下:

    590a79b2bcc6f.png

    1. div {
    2.     width: 200px;
    3.     height: 100px;
    4.     background: #58a;
    5.     background: linear-gradient(135deg, transparent 15px, #58a 0) top
    6. left, linear-gradient(-135deg, transparent 15px, #58a 0) top right, linear-gradient(-45deg, transparent 15px, #58a 0) bottom right, linear-gradient(45deg, transparent 15px, #58a 0) bottom left;
    7.     background-size: 50% 50%;
    8.     background-repeat: no-repeat;
    9. }
    复制代码

    效果如下:

    590a79b2d0be5.png

    上面的代码可维护比较差,在改变切角和颜色时都要修改多处,所以我们可以使用scss的mixin来写:

    1. @mixin beveled-corners($bg, $tl:0, $tr:$tl, $br:$tl, $bl:$tr) {
    2.     background: $bg;
    3.     background:
    4.     linear-gradient(135deg, transparent $tl, $bg 0) top left,
    5.     linear-gradient(225deg, transparent $tr, $bg 0) top right,
    6.     linear-gradient(-45deg, transparent $br, $bg 0) bottom right,
    7.     linear-gradient(45deg, transparent $bl, $bg 0) bottom left;
    8.     background-size: 50% 50%;
    9.     background-repeat: no-repeat;
    10. }
    复制代码

    调用时:

    1. @include beveled-corners(#58a, 15px, 5px);
    复制代码

    最后生成的效果是左上角和右下角是15px的切角,右上角和左下角是5px的切角。上面的minxin设置了初值,当传入的值少于四个值时,就跟border-radius赋值一样。

    一样的道理我们也可以创建弧形切角,至少改成径向渐变

    1. div {
    2.     width: 200px;
    3.     height: 100px;
    4.     background: #58a;
    5.     background: radial-gradient(circle at top left, transparent 15px, #58a 0) top left, radial-gradient(circle at top right, transparent 15px, #58a 0) top right, radial-gradient(circle at bottom right, transparent 15px, #58a 0) bottom right, radial-gradient(circle at bottom left, transparent 15px, #58a 0) bottom left;
    6.     background-size: 50% 50%;
    7.     background-repeat: no-repeat;
    8. }
    复制代码

    效果如下:

    590a79b2f07c6.png

    内联SVG与border-image的方式

    SVG 可以实现与尺寸完全无关的完美缩放,当文本溢出的时候,则会起到普通边框的作用,令文字折行。具体的实现如下:

    1. div {
    2.   width: 200px;
    3.   height: 100px;
    4.   border: 15px solid transparent;
    5.   border-image: 1 url('data:image/svg+xml,\<svg xmlns="http://www.w3.org/2000/svg" width="3" height="3" fill="%2358a">\<polygon points="0,1 1,0 2,0 3,1 3,2 2,3 1,3 0,2"/>\</svg>');
    6. }
    复制代码

    效果如下:

    590a79b31714f.png

    解决中间的背景色的填充可以给div块加一个和border一样的背景色:

    1. div {
    2.     width: 200px;
    3.     height: 100px;
    4.     border: 15px solid transparent;
    5.     border-image: 1 url(data:image/svg+xml,\<svg xmlns="http://www.w3.org/2000/svg" width="3" heigh="3" fill="%2358a">\<polygon points="0,1 1,0 2,0 3,1 3,2 2,3 1,3 0,2"/>\</svg>);
    6.     background: #58a;
    7.     background-clip: padding-box;
    8. }
    复制代码

    裁切路径方案CSS 裁切路径最神奇的地方在于我们可以同时使用百分比数值(它会以元素自身的宽高作为基数度进行换算)和绝对长度值,从而提供巨大的灵活性。

    如果用裁切路径将一个元素切出20px大小的斜面切角,具体的代码如下:

    1. background: #58a;
    2. clip-path: polygon( 20px 0, calc(100% - 20px) 0, 100% 20px, 100% calc(100% - 20px), calc(100% - 20px) 100%, 20px 100%, 0 calc(100% - 20px), 0 20px);
    复制代码

    这个方法最大的好处:可以使用任意类型的背景,甚至可以对替换元素(比如图片)进行裁切
    ,并且支持动画效果。

    缺点:当内边距不够宽时,它会裁切掉文本。


    梯形标签页的实现

    梯形的实现可以通过

    1. div {
    2.     position: relative;
    3.     display: inline-block;
    4.     padding: .5em 1em .35em;
    5.     color: white;
    6. }

    7. div::before {
    8.     content: '';
    9.     position: absolute;
    10.     top: 0;
    11.     right: 0;
    12.     bottom: 0;
    13.     left: 0;
    14.     z-index: -1;
    15.     background: #58a;
    16.     transform: scaleY(1.3) perspective(.5em) rotateX(5deg);
    17.     transform-origin: bottom;
    18. }
    复制代码

    效果如下:

    590a79b32fa76.png

    变形效果会让这个元素以它自身的中心线为轴进行空间上的旋转,这会让它的尺寸很难控制,解决办法可以设置transform-origin:bottom;,当它在3D空间中旋转时,把它的底边固定。固定底边的同时,高度的缩水会变得更加显眼。为了解决这个问题,可以通过变形属性(scaleY())来改变它的尺寸。

    同样的方法可以实现下面这种tab布局:

    590a79b34480c.png

    具体代码如下:

    1. nav > a {
    2.     position: relative;
    3.     display: inline-block;
    4.     padding: .3em 1em 0;
    5. }
    6. nav > a::before {
    7.     content: '';
    8.     position: absolute;
    9.     top: 0; right: 0; bottom: 0; left: 0;
    10.     z-index: -1;
    11.     background: #ccc;
    12.     background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,0));
    13.     border: 1px solid rgba(0,0,0,.4);
    14.     border-bottom: none;
    15.     border-radius: .5em .5em 0 0;
    16.     box-shadow: 0 .15em white inset;
    17.     transform: perspective(.5em) rotateX(5deg);
    18.     transform-origin: bottom;
    19. }
    复制代码

    通过把transform-origin改成bottom left或bottom right,就可以立即得到左侧倾斜或右侧倾斜的标签页。

    这个方法的缺点是:斜边的角度依赖于元素的宽度。不过,对于宽度变化不大的多个元素(比如导航菜单)来说,这个方法还是非常管用的。

    简单的饼图的绘制

    思路是把圆形的左右两部分指定为上述两种颜色,然后用伪元素覆盖上去,通过旋转来决定露出多大的扇区。具体的代码如下:

    1. .pie {
    2.     width: 100px;
    3.     height: 100px;
    4.     border-radius: 50%;
    5.     background: yellowgreen;
    6.     background-image: linear-gradient(to right, transparent 50%, #655 0);

    7. .pie::before {
    8.     content: '';
    9.     display: block;
    10.     margin-left: 50%;
    11.     height: 100%;
    12.     border-radius: 0 100% 100% 0 / 50%;
    13.     background-color: inherit;
    14.     transform-origin: left;
    15. }
    复制代码

    实现的效果如下:

    590a79b363a6f.png

    这种方式还支持动画,就可以用CSS动画来实现一个饼图从0 变化到100% 的动画,从而得到一个炫酷的进度指示器:

    1. @keyframes spin {
    2.     to { transform: rotate(.5turn); }
    3. }
    4. @keyframes bg {
    5.     50% { background: #655; }
    6. }
    7. .pie::before {
    8.     content: '';
    9.     display: block;
    10.     margin-left: 50%;
    11.     height: 100%;
    12.     border-radius: 0 100% 100% 0 / 50%;
    13.     background-color: inherit;
    14.     transform-origin: left;
    15.     animation: spin 3s linear infinite, bg 6s step-end infinite;
    16. }
    复制代码

    生成多个不同比率的静态饼图的办法,可以通过上面的那个动画的实现,需要设置动画处于暂停状态,然后设置负的动画延时直接跳转到动画中的任意时间点, 最终实现的代码如下:

    1. <div class="pie" style="animation-delay: -20s">
    2.     <span>20</span>
    3. </div>
    4. <div class="pie" style="animation-delay: -60s">
    5.     <span>60</span>
    6. </div>
    复制代码
    1. .pie{
    2.     position: relative;
    3.     width: 100px;
    4.     height: 100px;
    5.     line-height: 100px;
    6.     border-radius: 50%;
    7.     background: yellowgreen;
    8.     background-image: linear-gradient(to right, transparent 50%, #655 0);
    9.     color: #000;
    10.     text-align: center;
    11. }

    12. @keyframes spin {
    13.     to { transform: rotate(.5turn); }
    14. }
    15. @keyframes bg {
    16.     50% { background: #655; }
    17. }

    18. .pie::before {
    19.     content: '';
    20.     position: absolute;
    21.     top: 0; left: 50%;
    22.     width: 50%; height: 100%;
    23.     border-radius: 0 100% 100% 0 / 50%;
    24.     background-color: inherit;
    25.     transform-origin: left;
    26.     animation: spin 50s linear infinite, bg 100s step-end infinite;
    27.     animation-play-state: paused;
    28.     animation-delay: inherit;
    29. }
    30. .pie span {
    31.     position: relative;
    32.     z-index: 10;
    33. }
    复制代码

    效果如下:

    590a79b383474.png

    用SVG 解决方案
    1. <svg viewBox="0 0 32 32">
    2.     <circle r="16" cx="16" cy="16" />
    3. </svg>
    复制代码
    1. svg {
    2.     width: 100px;
    3.     height: 100px;
    4.     transform: rotate(-90deg);
    5.     background: yellowgreen;
    6.     border-radius: 50%;
    7. }
    8. circle {
    9.     fill: yellowgreen;
    10.     stroke: #655;
    11.     stroke-width: 32;
    12.     stroke-dasharray: 38 100; /* 可得到比率为38%的扇区 */
    13. }
    复制代码

    让饼图的周长无限接近100,就可以直接把比率的百分比值指定为strokedasharray的长度,不需要做任何计算了。因为周长是2πr,半径就是100/22π ≈ 15.915,最终把这个值取整为16。在SVG的viewBox属性中指定其尺寸,而不再使用width和height属性,这样就可以让它自动适应容器的大小了。

    SVG的优点是„增加第三种颜色是非常容易的,并且可以用内联样式来指定颜色。

    学习心情好,签到少不了。
    avatar
  • TA的每日心情
    qdsmile开心
    2024-12-30 10:00
  • 签到天数: 12 天

    [LV.3]偶尔看看

    0

    主题

    27

    帖子

    74

    积分

    发表于 2017-5-16 09:38:27 | 显示全部楼层
    谢谢谢谢谢谢谢谢谢谢谢谢谢谢
    该会员没有填写今日想说内容.
    avatar
  • TA的每日心情
    qdsmile开心
    2024-12-30 10:00
  • 签到天数: 12 天

    [LV.3]偶尔看看

    0

    主题

    27

    帖子

    74

    积分

    发表于 2017-5-16 09:38:52 | 显示全部楼层

    好文章好文章好文章好文章好文章好文章好文章好文章好文章好文章
    该会员没有填写今日想说内容.
    avatar
  • TA的每日心情
    qdsmile开心
    2024-12-30 10:00
  • 签到天数: 12 天

    [LV.3]偶尔看看

    0

    主题

    27

    帖子

    74

    积分

    发表于 2017-5-16 09:39:12 | 显示全部楼层

    利用CSS 绘制各种形状利用CSS 绘制各种形状利用CSS 绘制各种形状利用CSS 绘制各种形状利用CSS 绘制各种形状
    该会员没有填写今日想说内容.
    avatar
  • TA的每日心情
    qdsmile
    2021-6-24 19:36
  • 签到天数: 9 天

    [LV.3]偶尔看看

    0

    主题

    31

    帖子

    618

    积分
    发表于 2021-5-21 22:13:00 | 显示全部楼层
    及桃嗣热厍酪
    您需要登录后才可以回帖 登录 | 立即注册 QQ登录

    本版积分规则