zoukankan      html  css  js  c++  java
  • 碎裂效果尝试(clip-path篇)

    上一篇用canvas去实现碎裂的效果,优点在于性能流畅,缺点在于无法显示3D效果(没有去尝试WebGL碎裂的研究……)、碎裂的实现也挺麻烦的,毕竟canvas中的clip只是获取绘图区域的一部分子区域,而并不是将某个元素取部分碎片,今天说的clip-path就不一样啦,我本身很喜欢用这个去实现碎裂

    不了解clip-path可点击链接参考了解:http://www.w3cplus.com/css3/css-svg-clipping.html

    先说说它的优点

    1.可以实现碎片3D运动,各种乱飘,6的不行

    2.原理比较简单,方便理解

    3.大部分DOM元素都可以实现碎裂!大部分DOM元素都可以实现碎裂!大部分DOM元素都可以实现碎裂!

    重要的事情说三遍,大部分DOM元素是什么意思?无论你的div里面有啥、亦或是你的表单、你的img标签、你的table,都可以实现碎裂,完全脱离了canvas绘图环境的束缚!

    是不是很让人期待,但下面要先泼一盆冷水,在真实环境下,clip-path使用起来很不方便

    1.IE不支持,FF不支持

    2.非常的卡,因为DOM渲染的缘故,一旦碎片太多,简直卡成狗

    既然有好有坏,我们来看看效果吧~

    这篇文章真的是我用心之作,从想法到实现,没有参考过网上一个任何资源,网上clip-path基本上都是用来做外观变动动画的,可以实现很酷的效果。我在某天夜晚脑洞大开将其用作碎裂的实现,希望自己的研究能得到大家的认可

    最开始出现的是一个表单,先贴出HTML和CSS代码

    <!doctype html>
    <html>
    <head>
    <meta charset="utf-8">
    <title>无标题文档</title>
    <style>
        body,input,form{ margin:0; padding:0;}
        body{ overflow:hidden; background-color:#0FF;}
        
        #content{ width:600px; height:400px; margin:10px auto;
            -webkit-transform-style:preserve-3d;
            -webkit-perspective:800px;
            -webkit-perspective-origin:center center;
            transform-style:preserve-3d;
            perspective:800px;
            perspective-origin:center center;
        }
        
        #wrap{ width:560px; height:360px; box-shadow:2px 2px 4px 1px black; padding:20px; background-color:white; 
            transition:all 4s ease; 
            transform:rotateX(0deg) rotateY(0deg); 
             -webkit-transform-origin:center;
        }
        
        
        #wrap .fline{ height:70px; padding:10px; line-height:70px; text-align:center;}
        .fline span{ font-size:24px; font-family:Verdana, Geneva, sans-serif;}
        .fline input{ height:24px; width:200px;}
        .lastline input{ width:120px;}
    </style>
    </head>
    
    <body>
        <div id="content">
            <div id="wrap">
                <form action=".">
                    <div class="fline">
                        <span>Name:</span><input type="text" >
                    </div>
                    <div class="fline">
                        <span>Password:</span><input type="password" >
                    </div>
                    <div class="fline">
                        <span>Repeat Pass:</span><input type="password" >
                    </div>
                    <div class="fline lastline">
                        <input type="button" value="提交" >
                        <input type="button" value="关闭" id="shutdown" >
                    </div>
                </form>
            </div>
        </div>
    </body>
    </html>

    注意,代码中出现transition一律跳过,刚刚那个gif是用定时器实现动画的,还有一种是利用transition实现动画,两种方式各有利弊,之后会贴栗子

    先把js部分贴出来吧

    <script>
        var oWrap = document.getElementById('wrap'),
            oForm = oWrap.getElementsByTagName('form')[0],
            oShutdown = document.getElementById('shutdown'),
            oContent = document.getElementById('content');
            
        oShutdown.onclick = function(){
            var w = oWrap.offsetWidth,
                h = oWrap.offsetHeight;
                
            var arr = cut(3, [[0,0],[w+7,0],[w+7,h+7],[0,h+7]], false);  //+7因为有7像素的阴影
            
            duang(oWrap, arr);
        }
        
        function duang(obj, arr){
            var left = obj.offsetLeft,
                top = obj.offsetTop;
                
            var    aF = [];
            
            for(var i = 0;i < arr.length;i++){
                var tmpObj = obj.cloneNode(true);
                tmpObj.style.position = 'absolute';
                tmpObj.style.top = obj.offsetTop - 10 + 'px';
                tmpObj.className = 'test';
                
                
                str = 'polygon(';
                
                for(var j = 0;j < arr[i].length;j++){
                    str += arr[i][j][0] + 'px ';
                    str += arr[i][j][1] + 'px,'
                }
                
                str = str.slice(0, -1) + ')';
    
                tmpObj.style.WebkitClipPath = str;
                tmpObj.style.clipPath = str;
                
                
                aF.push((function(obj, x, y){
                    
                    return function(t){
                        obj.style.transform = 'rotateX(' + t*x + 'deg) rotateY(' + t*y + 'deg)';
                    }
                    
                })(tmpObj, Math.floor(Math.random()*10 + 2), Math.floor(Math.random()*10 + 2)));
                
                oContent.appendChild(tmpObj);
            }
            
            var n = 0;
            
            setInterval(function(){
                
                for(var k = 0;k < aF.length;k++){
                    aF[k](n);
                }
                n++;
                
            }, 30);
            
            obj.style.display = 'none';
        }
        
        function cut(iLayer, aPoint, isThin){
            var aResult = [];
            
            if(arguments[2] == undefined){
                isThin = false;
            }
            
            var x = [],
                y = [];
                
            for(var i = 0;i < aPoint.length;i++){
                x.push(aPoint[i][0]);
                y.push(aPoint[i][1]);
            }
            
            var dealX = x.sort();
            var dealY = y.sort();
            var randX,randY;
            
            var maxX = Math.max.apply(dealX, dealX),
                maxY = Math.max.apply(dealY, dealY),
                minX = Math.min.apply(dealX, dealX),
                minY = Math.min.apply(dealY, dealY);
                
            if(iLayer == 1){
                if(!isThin){
                    randX = Math.floor((Math.random() * (0.6) + 0.2) * (maxX - minX)) + minX;
                    randY = Math.floor((Math.random() * (0.6) + 0.2) * (maxY - minY)) + minY;
                }else{
                    randX = Math.floor(Math.random() * (maxX - minX)) + minX;
                    randY = Math.floor(Math.random() * (maxY - minY)) + minY;
                }
                
                for(i = 0;i < aPoint.length;i++){
                    var tmp = aPoint[i+1] || aPoint[0];
                    aResult.push([aPoint[i], tmp, [randX, randY]]);
                }
            }else{
                randX = (maxX - minX) / 2 + minX;
                randY = (maxY - minY) / 2 + minY;
    
                aResult = aResult.concat(cut(iLayer - 1, [[minX,minY],[randX,minY],[randX,randY],[minX,randY]], isThin));
                aResult = aResult.concat(cut(iLayer - 1, [[maxX,minY],[maxX,randY],[randX,randY],[randX,minY]], isThin));
                aResult = aResult.concat(cut(iLayer - 1, [[maxX,maxY],[randX,maxY],[randX,randY],[maxX,randY]], isThin));
                aResult = aResult.concat(cut(iLayer - 1, [[minX,maxY],[minX,randY],[randX,randY],[randX,maxY]], isThin));
            }
            
            return aResult;
        }
    </script>

    因为比较简单易懂,所以没怎么整理就放上来了

    整个js的核心在两个方法,一个是duang方法,还有一个是cut方法,这里也主要就是说这两个方法

    cut方法的作用是将一个长方形切割成一定数量的三角形

    参数有三,分别是

    1.三角形的数量多少(我称为层数,稍后解释)

    2.长方形顶点坐标的数组

    3.是否薄切形成三角形(默认为false,推荐为false)

    详细解释一下,第一个参数为层数,为什么用层数这个名词,因为我随机形成三角形的算法和层似乎有关系,就用了这个命名

    当iLayer为1的时候,会在长方形中随机取一点,然后和四个顶点相连(这四个顶点也就是第二个参数),效果如下

    当iLayer为2时,首先取长方形的中心点,将长方形平分成四块,效果如下

    然后每一块在其中随机取点与四个顶点相连接,相当于4次iLayer=1,用递归实现,效果如图

    iLayer等于3,4,5的时候同理,一般去iLayer为5的时候长方形就被分割的非常的碎了

    参数2说过了就是长方形顶点数组,参数3是否薄切在于分割三角形的时候会不会出现非常细小的三角形,比如下图中出现的两个

    不推荐出现这样的三角形,因为后期效果中还是均匀一点的三角形比较好看。这样cut方法就讲好了,然后说说duang方法。方法如其名,就是加特效的

    第二个参数是一个数组,给的就是cut切割出来的n个三角形的数组集合,然后克隆n个元素(长方形),加上clip-path使其变成一块一块的,通过定时器使其运动起来~虽然没注释,但代码简单,应该都能读懂的

    之前有说到transition和定时器效果的不同,下面贴一个transition的栗子,其中iLayer取了5,碎的很彻底,大家看一下效果

    点击关闭的时候可以感到明显的卡顿感,不过碎的还是很彻底的哇~

    好了,演示演示的差不多了,核心原理也讲解了,下面开始说说这里面的一些坑

    ——————————————————————————————————————————————————————————————

    1.当css数值变化太多的时候,浏览器无法立刻计算出变化的数值差,transition就会失效。所以我点击关闭,弹出show time并不是真的想show time,而是必须给浏览器一个缓冲时间。所以第一个gif采用定时器实现时就没有出现show time

    2.transition是数值上的变化,但是不能从无到有。比方说left属性0px到600px可以有动画,但是本身没有left,直接设置一个left为600px,是没有变化的

    3.既然transition坑比较多为啥不全部用定时器呢?因为iLayer过大时无法使用定时器,原因我给定时器设置的间隔是30ms,当iLayer比较大比如5的时候,30ms的时间根本就没有处理完那么多三角形的变化,会出现很严重的bug,只能用transition……

    ——————————————————————————————————————————————————————————————

    展望:

    这种方式性能是很大的问题,关键在于能不能尽可能的优化,iLayer设置的合理值?用类型化数组使其加快性能?希望这个方法后面会有大展拳脚的时候!!

    想象中一个效果是可以将页面中的东西切片分割再重组,比方说当用户点击一个链接,整个页面碎掉,再还原的时候就变成一个新的页面,似不似很炫酷……估计会卡,后面有时间会写一个看看,因为这段时间要准备去实习了,很忙,有时间再说……

    结语:终于把自己一直想发的话题发了, 在五月的最后一天。从点子的产生到长方形随机切割三角形的方法的实现,再到运动时遇到的一个又一个坑,我知道这个碎裂的实现有太多的不足,但我还是很开心,感觉就像自己发明了一个玩具一般

    生命不休,奋斗不止~

  • 相关阅读:
    Linux文件编辑器 vi
    Sudo 和 Root 帐号
    Linux基本命令篇的习题解答
    Linux应用篇
    VMware中虚拟机网卡的四种模式
    Ubuntu游戏集合
    Linux与Window文件共享
    Linux知识点滴II
    [转载][翻译]jQuery Mobile教程创建一个订餐web应用(下)
    在WPF中创建带有刻度线的滑动条
  • 原文地址:https://www.cnblogs.com/constructor/p/4541179.html
Copyright © 2011-2022 走看看