zoukankan      html  css  js  c++  java
  • 奇妙的 clip-path 几何图形

    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
  • 相关阅读:
    父组件向子组件传递数据(vue.js)
    vue引入JQ的方法
    webstorm添加*.vue文件代码提醒支持webstorm支持es6vue里支持es6写法
    创建脚手架步骤
    JS严格校验身份证号
    微信小程序开发工具 常用快捷键
    GIT 常用命令
    git 操作
    通过selenium(也有Puppeter版在最后)登录网页获取特定信息
    用Django ORM实现树状结构
  • 原文地址:https://www.cnblogs.com/xuey/p/8455459.html
Copyright © 2011-2022 走看看