奇妙的 clip-path 几何图形

橙三吉。 提交于 2020-12-18 07:41:04

CSS 新属性 clip-path,意味裁剪路径的意思,让我们可以很便捷的生成各种几何图形。

clip-path 通过定义特殊的路径,实现我们想要的图形。而这个路径,正是 SVG 中的 path 。

clip-path  属性api:

/* Keyword values */
clip-path: none;

/* Image values */ 
clip-path: url(resources.svg#c1);

/* Box values
clip-path: fill-box;
clip-path: stroke-box;
clip-path: view-box;
clip-path: margin-box
clip-path: border-box
clip-path: padding-box
clip-path: content-box

/* Geometry values */
/*矩形可以2个值, 也可 4个值    top right bottom left
   矩形可以3个值  第一个值 大小 第二值圆角属性【round】  第三个值圆角大小
*/
clip-path: inset(100px 50px);  
 /*圆形  第一个值大小  第二值 左右水平位置  第三个上下垂直 位置*/
clip-path: circle(50px at 0 100px);   /* */
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);

/* Box and geometry values combined */
clip-path: padding-box circle(50px at 0 100px);

/* Global values */
clip-path: inherit;
clip-path: initial;
clip-path: unset;
View Code

 

例如 clip-path: circle(50px at 50px 50px) 表示在元素的 (50px, 50px)处,裁剪生成一个半径为 50px 的圆,以元素的左上角为坐标起点

而整个 clip-path 属性,最为重要的当属 polygon,可以利用 polygon 生成任意多边形, polygon 几对【x,y】几边形,值可以 是百分比,也可以使用具体的数值

下面分别列举使用 clip-path 生成一个圆形和一个十边形:

/* 圆形 */
.circle {
  width: 100px;
  height: 100px;
  background-color: yellowgreen; 
  clip-path: circle(50px at 50px 50px);
}

/* 十边形 */
.polygon {
  width: 100px;
  height: 100px;
  background-color: yellowgreen; 
  /*polygon(x1,y1,x2,y2...)*/
  clip-path: polygon(50% 0%, 80% 10%, 100% 35%, 100% 70%, 80% 90%, 50% 100%, 20% 90%, 0% 70%, 0% 35%, 20% 10%);
}
View Code

 

clip-path 动画

clip-path 另外一个强大之处在于可以进行 CSS transtion 与 CSS animation,也就是过渡和动画。

<div class="polygon-animate"></div> /* 几何图形变换 polygon 坐标位置可以去 http://bennettfeely.com/clippy/ 获取 如果页面打开过慢 或点击图像选择不了请 使用翻墙软件 */
 .polygon-animate {
  position: absolute;
  width: 200px;
  height: 200px;
  top: 50%;
  left: 50%;
  -webkit-transform: translate(-50%, -50%);
          transform: translate(-50%, -50%);
  background-color: crimson;
  -webkit-transition: .3s;
  transition: .3s;
  -webkit-clip-path: polygon(50% 0%, 0% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%);
          clip-path: polygon(50% 0%, 0% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%);
  -webkit-animation: polygon-ani 5s linear infinite;
          animation: polygon-ani 5s linear infinite;
}

@-webkit-keyframes polygon-ani {
  10% {
    background-color: darkorange;
    -webkit-clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%, 0% 50%, 0% 50%, 0% 50%, 0% 50%, 0% 50%);
            clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%, 0% 50%, 0% 50%, 0% 50%, 0% 50%, 0% 50%);
  }
  14% {
    -webkit-clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%, 0% 50%, 0% 50%, 0% 50%, 0% 50%, 0% 50%);
            clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%, 0% 50%, 0% 50%, 0% 50%, 0% 50%, 0% 50%);
  }
  24% {
    background-color: lemonchiffon;
    -webkit-clip-path: polygon(100% 38%, 82% 100%, 82% 100%, 18% 100%, 0% 38%, 0% 38%, 0% 38%, 0% 38%, 50% 0%);
            clip-path: polygon(100% 38%, 82% 100%, 82% 100%, 18% 100%, 0% 38%, 0% 38%, 0% 38%, 0% 38%, 50% 0%);
  }
  28% {
    -webkit-clip-path: polygon(100% 38%, 82% 100%, 82% 100%, 18% 100%, 0% 38%, 0% 38%, 0% 38%, 0% 38%, 50% 0%);
            clip-path: polygon(100% 38%, 82% 100%, 82% 100%, 18% 100%, 0% 38%, 0% 38%, 0% 38%, 0% 38%, 50% 0%);
  }
  38% {
    background-color: darkturquoise;
    -webkit-clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 100% 75%, 50% 100%, 0% 75%, 0% 75%, 0% 25%, 0% 25%);
            clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 100% 75%, 50% 100%, 0% 75%, 0% 75%, 0% 25%, 0% 25%);
  }
  42% {
    -webkit-clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 100% 75%, 50% 100%, 0% 75%, 0% 75%, 0% 25%, 0% 25%);
            clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 100% 75%, 50% 100%, 0% 75%, 0% 75%, 0% 25%, 0% 25%);
  }
  52% {
    background-color: darkcyan;
    -webkit-clip-path: polygon(50% 0%, 90% 20%, 100% 60%, 75% 100%, 25% 100%, 25% 100%, 0% 60%, 10% 20%, 50% 0%);
            clip-path: polygon(50% 0%, 90% 20%, 100% 60%, 75% 100%, 25% 100%, 25% 100%, 0% 60%, 10% 20%, 50% 0%);
  }
  56% {
    -webkit-clip-path: polygon(50% 0%, 90% 20%, 100% 60%, 75% 100%, 25% 100%, 25% 100%, 0% 60%, 10% 20%, 50% 0%);
            clip-path: polygon(50% 0%, 90% 20%, 100% 60%, 75% 100%, 25% 100%, 25% 100%, 0% 60%, 10% 20%, 50% 0%);
  }
  66% {
    background-color: deepskyblue;
    -webkit-clip-path: polygon(30% 0%, 70% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%);
            clip-path: polygon(30% 0%, 70% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%);
  }
  70% {
    -webkit-clip-path: polygon(30% 0%, 70% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%);
            clip-path: polygon(30% 0%, 70% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%);
  }
  80% {
    background-color: indigo;
    -webkit-clip-path: polygon(83% 12%, 100% 43%, 94% 78%, 68% 100%, 32% 100%, 6% 78%, 0% 43%, 17% 12%, 50% 0%);
            clip-path: polygon(83% 12%, 100% 43%, 94% 78%, 68% 100%, 32% 100%, 6% 78%, 0% 43%, 17% 12%, 50% 0%);
  }
  84% {
    -webkit-clip-path: polygon(83% 12%, 100% 43%, 94% 78%, 68% 100%, 32% 100%, 6% 78%, 0% 43%, 17% 12%, 50% 0%);
            clip-path: polygon(83% 12%, 100% 43%, 94% 78%, 68% 100%, 32% 100%, 6% 78%, 0% 43%, 17% 12%, 50% 0%);
  }
  94% {
    background-color: crimson;
    -webkit-clip-path: polygon(50% 0%, 0% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%);
            clip-path: polygon(50% 0%, 0% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%);
  }
}

@keyframes polygon-ani {
  10% {
    background-color: darkorange;
    -webkit-clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%, 0% 50%, 0% 50%, 0% 50%, 0% 50%, 0% 50%);
            clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%, 0% 50%, 0% 50%, 0% 50%, 0% 50%, 0% 50%);
  }
  14% {
    -webkit-clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%, 0% 50%, 0% 50%, 0% 50%, 0% 50%, 0% 50%);
            clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%, 0% 50%, 0% 50%, 0% 50%, 0% 50%, 0% 50%);
  }
  24% {
    background-color: lemonchiffon;
    -webkit-clip-path: polygon(100% 38%, 82% 100%, 82% 100%, 18% 100%, 0% 38%, 0% 38%, 0% 38%, 0% 38%, 50% 0%);
            clip-path: polygon(100% 38%, 82% 100%, 82% 100%, 18% 100%, 0% 38%, 0% 38%, 0% 38%, 0% 38%, 50% 0%);
  }
  28% {
    -webkit-clip-path: polygon(100% 38%, 82% 100%, 82% 100%, 18% 100%, 0% 38%, 0% 38%, 0% 38%, 0% 38%, 50% 0%);
            clip-path: polygon(100% 38%, 82% 100%, 82% 100%, 18% 100%, 0% 38%, 0% 38%, 0% 38%, 0% 38%, 50% 0%);
  }
  38% {
    background-color: darkturquoise;
    -webkit-clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 100% 75%, 50% 100%, 0% 75%, 0% 75%, 0% 25%, 0% 25%);
            clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 100% 75%, 50% 100%, 0% 75%, 0% 75%, 0% 25%, 0% 25%);
  }
  42% {
    -webkit-clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 100% 75%, 50% 100%, 0% 75%, 0% 75%, 0% 25%, 0% 25%);
            clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 100% 75%, 50% 100%, 0% 75%, 0% 75%, 0% 25%, 0% 25%);
  }
  52% {
    background-color: darkcyan;
    -webkit-clip-path: polygon(50% 0%, 90% 20%, 100% 60%, 75% 100%, 25% 100%, 25% 100%, 0% 60%, 10% 20%, 50% 0%);
            clip-path: polygon(50% 0%, 90% 20%, 100% 60%, 75% 100%, 25% 100%, 25% 100%, 0% 60%, 10% 20%, 50% 0%);
  }
  56% {
    -webkit-clip-path: polygon(50% 0%, 90% 20%, 100% 60%, 75% 100%, 25% 100%, 25% 100%, 0% 60%, 10% 20%, 50% 0%);
            clip-path: polygon(50% 0%, 90% 20%, 100% 60%, 75% 100%, 25% 100%, 25% 100%, 0% 60%, 10% 20%, 50% 0%);
  }
  66% {
    background-color: deepskyblue;
    -webkit-clip-path: polygon(30% 0%, 70% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%);
            clip-path: polygon(30% 0%, 70% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%);
  }
  70% {
    -webkit-clip-path: polygon(30% 0%, 70% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%);
            clip-path: polygon(30% 0%, 70% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%);
  }
  80% {
    background-color: indigo;
    -webkit-clip-path: polygon(83% 12%, 100% 43%, 94% 78%, 68% 100%, 32% 100%, 6% 78%, 0% 43%, 17% 12%, 50% 0%);
            clip-path: polygon(83% 12%, 100% 43%, 94% 78%, 68% 100%, 32% 100%, 6% 78%, 0% 43%, 17% 12%, 50% 0%);
  }
  84% {
    -webkit-clip-path: polygon(83% 12%, 100% 43%, 94% 78%, 68% 100%, 32% 100%, 6% 78%, 0% 43%, 17% 12%, 50% 0%);
            clip-path: polygon(83% 12%, 100% 43%, 94% 78%, 68% 100%, 32% 100%, 6% 78%, 0% 43%, 17% 12%, 50% 0%);
  }
  94% {
    background-color: crimson;
    -webkit-clip-path: polygon(50% 0%, 0% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%);
            clip-path: polygon(50% 0%, 0% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%);
  }
}
css

 

除此之外,我们还可以尝试,将一个完整的图形,分割成多个小图形

<hgroup class="triangle2rect">
    <div class="a"></div>
    <div class="b"></div>
    <div class="c"></div>
    <div class="d"></div>
</hgroup>
.triangle2rect {
    position: absolute;
    width: 100px;
    height: 100px;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    animation: aniContainer 2s infinite alternate;
}
.triangle2rect div {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}
.a {
    background: deeppink;
    clip-path: polygon(0% 0%, 0% 100%, 50% 50%);
    animation: a 2s infinite alternate;
}
.b {
    background: deeppink;
    clip-path: polygon(0% 0%, 100% 0%, 50% 50%);
    animation: b 2s infinite alternate;
}
.c {
    background: deeppink;
    clip-path: polygon(100% 0%, 100% 100%, 50% 50%);
    animation: c 2s infinite alternate;
}
.d {
    background: deeppink;
    clip-path: polygon(100% 100%, 0% 100%, 50% 50%);
    animation: d 2s infinite alternate;
}
@keyframes a {
    0%, 10% {
        background: deeppink;
        clip-path: polygon(0% 0%, 0% 100%, 50% 50%);
    }
    90%, 100% {
        background: #000;
        clip-path: polygon(0% 100%, 25% 100%, 12.5% 0%);
    }
}
@keyframes b {
    0%, 10% {
        background: deeppink;
        clip-path: polygon(0% 0%, 100% 0%, 50% 50%);
    }
    90%, 100% {
        background: #000;
        clip-path: polygon(25% 0%, 50% 0%, 37.5% 100%);
    }
}
@keyframes c {
    0%, 10% {
        background: deeppink;
        clip-path: polygon(100% 0%, 100% 100%, 50% 50%);
    }
    90%, 100% {
        background: #000;
        clip-path: polygon(62.5% 0%, 75% 100%, 50% 100%);
    }
}
@keyframes d {
    0%, 10% {
        background: deeppink;
        clip-path: polygon(100% 100%, 0% 100%, 50% 50%);
    }
    90%, 100% {
        background: #000;
        clip-path: polygon(100% 0%, 87.5% 100%, 75% 0%);
    }
}
@keyframes aniContainer {
    0%, 10% {
        width: 100px;
        height: 100px;
    }
    90%, 100% {
        width: 250px;
        height: 60px;
    }
}
css

 

clip-path 动画的局限

clip-path 动画虽然美好,但是存在一定的局限性,那就是进行过渡的两个状态,坐标顶点的数量必须一致。

也就是如果我希望从三角形过渡到矩形。假设三角形和矩形的 clip-path 分别为:

  • 三角形:clip-path: polygon(50% 0, 0 100%, 100% 0)
  • 矩形: clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%)

进行过渡动画时候,直接从 polygon(50% 0, 0 100%, 100% 0) --> polygon(0 0, 100% 0, 100% 100%, 0 100%) 是不行的,因为是从 3 个坐标点变换到 4 个坐标点。

因此这里需要这用一个讨巧的办法,在三角形的表示方法中,使用四个坐标点表示,其中两个坐标点进行重合即可。也就是:

  • 三角形:clip-path: polygon(50% 0, 0 100%, 100% 0) -> clip-path: polygon(50% 0, 50% 0, 0 100%, 100% 0)

 

N边形过渡动画

如果脑洞够大,随机生成 N(N>=1000)边形  //只是随机生成了 2000 个坐标点,然后使用 clip-path 将这些坐标点连接起来,并不是符合要求的多边形

https://codepen.io/Chokcoco/pen/XgJRzO?editors=1010

<div></div>
div {
    width: 300px;
    height: 300px;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    transition: all .5s;
    transition-timing-function: cubic-bezier(.92,-0.5,1,.12);
    border-radius: 50%;
}
Css
 1 setInterval(function() {
 2     const length = 2000;
 3 
 4     let el = document.querySelectorAll("div")[0];
 5     let coordinate = "";
 6 
 7     for (let i = 0; i < length; i++) {
 8         coordinate +=
 9             parseInt(Math.random() * 10000) / 100 +
10             "% " +
11             parseInt(Math.random() * 10000) / 100 +
12             "%, ";
13     }
14 
15     coordinate = "polygon(" + coordinate.slice(0, -2) + ")";
16 
17     el.style.clipPath = coordinate;
18     el.style.backgroundColor =
19         "#" + (~~(Math.random() * (1 << 24))).toString(16);
20 }, 500);
JavaScript

 

 

改良后的 (VUE官网)  https://codepen.io/Chokcoco/pen/NgqGOo?editors=1111

 1 <div id="app">
 2     <!-- 使用 clip-path -->
 3     <div id="svgpolygon" v-bind:style="styleObject"></div>
 4     <label>Sides: {{ sides }}</label>
 5     <input type="range" min="3" max="500" v-model.number="sides">
 6     <label>Minimum Radius: {{ minRadius }}%</label>
 7     <input type="range" min="0" max="90" v-model.number="minRadius">
 8     <label>Update Interval: {{ updateInterval }} milliseconds</label>
 9     <input type="range" min="10" max="2000" v-model.number="updateInterval">
10 </div>
Html
 1 #svgpolygon {
 2     display: block;
 3     width: 180px;
 4     height: 180px;
 5     border-radius: 50%;
 6     border: 1px solid #333;
 7 }
 8 
 9 input[type="range"] {
10   display: block;
11   width: 100%;
12   margin-bottom: 15px;
13 }
Css
 1 new Vue({
 2     el: "#app",
 3     data: function() {
 4         var defaultSides = 500;
 5         var stats = Array.apply(null, { length: defaultSides }).map(function() {
 6             return 100;
 7         });
 8         return {
 9             stats: stats,
10             points: generatePoints(stats),
11             sides: defaultSides,
12             minRadius: 50,
13             interval: null,
14             updateInterval: 500
15         };
16     },
17     computed: {
18         styleObject: function() {
19             return {
20                 background: '#41B883',
21                 'clip-path': 'polygon(' + this.points +')'
22             }
23         }
24     },
25     watch: {
26         sides: function(newSides, oldSides) {
27             var sidesDifference = newSides - oldSides;
28             if (sidesDifference > 0) {
29                 for (var i = 1; i <= sidesDifference; i++) {
30                     this.stats.push(this.newRandomValue());
31                 }
32             } else {
33                 var absoluteSidesDifference = Math.abs(sidesDifference);
34                 for (var i = 1; i <= absoluteSidesDifference; i++) {
35                     this.stats.shift();
36                 }
37             }
38         },
39         stats: function(newStats) {
40             TweenLite.to(this.$data, this.updateInterval / 1000, {
41                 points: generatePoints(newStats)
42             });
43         },
44         updateInterval: function() {
45             this.resetInterval();
46         }
47     },
48     mounted: function() {
49         this.resetInterval();
50     },
51     methods: {
52         randomizeStats: function() {
53             var vm = this;
54             this.stats = this.stats.map(function() {
55                 return vm.newRandomValue();
56             });
57         },
58         newRandomValue: function() {
59             return Math.ceil(
60                 this.minRadius + Math.random() * (100 - this.minRadius)
61             );
62         },
63         resetInterval: function() {
64             var vm = this;
65             clearInterval(this.interval);
66             this.randomizeStats();
67             this.interval = setInterval(function() {
68                 vm.randomizeStats();
69             }, this.updateInterval);
70         }
71     }
72 });
73 
74 function valueToPoint(value, index, total) {
75     var x = 0;
76     var y = -value * 0.9;
77     var angle = Math.PI * 2 / total * index;
78     var cos = Math.cos(angle);
79     var sin = Math.sin(angle);
80     var tx = x * cos - y * sin + 100;
81     var ty = x * sin + y * cos + 100;
82     return { x: tx, y: ty };
83 }
84 
85 function generatePoints(stats) {
86     var total = stats.length;
87     var points = stats
88         .map(function(stat, index) {
89             var point = valueToPoint(stat, index, total);
90             return point.x + "px " + point.y +"px,";
91         })
92         .join(" ");
93     
94     points = points.slice(0, -1);
95      
96     return points;
97 }
JavaScript

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!