HTML5画布:从圆形到三角形的形状转换

3
我一直在寻找一种明确的方式,可以将形状从圆形转变为三角形或矩形,反之亦然。我认为可以存储该形状并更改其属性以进行转换。
基本上,我的问题是,如何绘制一个圆形,然后在单击按钮时将其动画变成三角形?使用canvas形状是否可行?
谢谢!

2
你想要做的是形状“tween” - 在此处复制SO --> https://dev59.com/mnI95IYBdhLWcg3wtwVe - beauXjames
HTML5画布没有“形状”,只有像素和路径。你可能能够“补间”一个路径,但你仍然需要为每个动画步骤重新绘制它。 - Alnitak
4个回答

7
这里有一种方法可以用来动画显示一个圆形,以及任何正多边形。
使用二次曲线创建三角形(或任何正多边形)的每条边。
这样,您就可以动画显示二次曲线的控制点来控制多边形的“圆润度”。
如果控制点在多边形线上,则曲线的圆润度为零,结果就是多边形。
如果控制点适当地位于多边形线外,则曲线近似于圆形,结果就是多边形看起来像一个圆形。
要在圆形-多边形之间进行“变形”,只需动画显示曲线的控制点即可。
我使用“近似”来描述圆形,因为二次曲线只能近似表示圆形。如果您想要更好的圆形,则可以重构我的代码,使用立方贝塞尔曲线而不是二次曲线。
以下是示例代码和演示:http://jsfiddle.net/m1erickson/NMJ58/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
    body{ background-color: ivory; }
    canvas{border:1px solid red;}
</style>
<script>
$(function(){

    // change sideCount to the # of poly sides desired
    //
    var sideCount=3;


    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");
    ctx.lineWidth=2;
    ctx.fillStyle=randomColor();

    var PI2=Math.PI*2;
    var cx=150;
    var cy=150;
    var radius=100;

    var xx=function(a){return(cx+radius*Math.cos(a));}
    var yy=function(a){return(cy+radius*Math.sin(a));}
    var lerp=function(a,b,x){ return(a+x*(b-a)); }

    var sides=[];
    for(var i=0;i<sideCount;i++){
        sides.push(makeSide(i,sideCount));
    }

    var percent=0;
    var percentDirection=0.50;

    $("#toShape").click(function(){
        percentDirection=-0.50;
    })

    $("#toCircle").click(function(){
        percentDirection=0.50;
    })

    animate();

    // functions

    function animate(){
        requestAnimationFrame(animate);
        drawSides(percent);
        percent+=percentDirection;
        if(percent>100){percent=100;}
        if(percent<0){percent=0;}
    }

    function drawSides(pct,color){
        ctx.clearRect(0,0,canvas.width,canvas.height);
        if(pct==100){
            ctx.beginPath();
            ctx.arc(cx,cy,radius,0,PI2);
            ctx.closePath();
            ctx.fill();
        }else{
            ctx.beginPath();
            ctx.moveTo(sides[0].x0,sides[0].y0);
            for(var i=0;i<sideCount;i++){
                var side=sides[i];
                var cpx=lerp(side.midX,side.cpX,pct/100);
                var cpy=lerp(side.midY,side.cpY,pct/100);        
                ctx.quadraticCurveTo(cpx,cpy,side.x2,side.y2);
            }
            ctx.fill();
        }
    }

    function makeSide(n,sideCount){
        var sweep=PI2/sideCount;
        var sAngle=sweep*(n-1);
        var eAngle=sweep*n;

        var x0=xx(sAngle);
        var y0=yy(sAngle);
        var x1=xx((eAngle+sAngle)/2);
        var y1=yy((eAngle+sAngle)/2);
        var x2=xx(eAngle);
        var y2=yy(eAngle);

        var dx=x2-x1;
        var dy=y2-y1;
        var a=Math.atan2(dy,dx);
        var midX=lerp(x0,x2,0.50);
        var midY=lerp(y0,y2,0.50);
        var cpX=2*x1-x0/2-x2/2;
        var cpY=2*y1-y0/2-y2/2;

        return({
            x0:x0, y0:y0,
            x2:x2, y2:y2,
            midX:midX, midY:midY,
            cpX:cpX, cpY:cpY,
            color:randomColor()
        });
    }

    function randomColor(){ 
        return('#'+Math.floor(Math.random()*16777215).toString(16));
    }

}); // end $(function(){});
</script>
</head>
<body>
    <button id="toShape">Animate to Shape</button>
    <button id="toCircle">Animate to Circle</button><br>
    <canvas id="canvas" width=300 height=300></canvas>
</body>
</html>

2
太好了!我更愿意看到这个或类似的答案被接受。 - Loktar

5

是的,这正是我想要的效果,非常感谢!我以前经常偶然发现raphael,但不知怎么回事,最近没有用正确的关键词搜索到它! - Mircea Sandu
抱歉,但这并不是我一开始想要的。Raphael确实很好,但它使用SVG,而我无法使用它来创建透明掩模。 - Mircea Sandu
@mircea,您能详细说明一下动画过程吗?虽然它可以通过按钮点击进行更改,但所需的动画类型是主要关注点,我想! - MarmiK

4
很难理解你的确切目标以及你需要在形状之间进行什么样的动画,因为你没有指定动画步骤/状态。但是你可以看一下CSS3过渡效果。我在以下演示中制作了一些示例,说明了如何模拟“形状变形”(这是正确的术语吗?),如果这是你想做的事情:

DEMO(点击圆圈以使它们动画)

对于从圆形到矩形的过渡效果,实现起来非常容易,只需动画化边框半径即可:

CSS:

#rec {
    float:left;
    width:200px;
    height:200px;
    background:gold;
    border-radius:50%;
    -webkit-transition: border-radius 1s, width 1s;
    cursor:pointer;
}
#rec.animate {
    border-radius:0%;
    width:300px;
}

对于圆形到三角形的转换,形状过渡有点难以实现,形态变形不完美,需要改进,但你可以得到想法。
圆形和三角形是通过伪元素设置的,并且使用两种不同的过渡显示其中之一:
CSS:
/*CIRCLE TO TRIANGLE 1  */
#tr1{
    float:right;
    position:relative;    
    cursor:pointer;    
    width:200px;
    height:200px;
}
#tr1:before, #tr1:after{
    content:'';
    position:absolute;
}
#tr1:before{
    top:0; left:0;
    background:gold;
    border-radius:50%;
    width:100%;
    height:100%;
    -webkit-transition: width 1s, height 1s, left 1s, top 1s;
}
#tr1:after{
    left:50%;
    top:50%;
    border-left:0px solid transparent;
    border-right:0px solid transparent;
    border-bottom:0px solid gold;
    -webkit-transition: border-left 1s, border-right 1s, border-bottom 1s,left 1s, top 1s;
}
#tr1.animate:before{
    width:0;
    height:0;
    top:50%; left:50%;
}
#tr1.animate:after{
    top:0; left:0;
    border-left:100px solid transparent;
    border-right:100px solid transparent;
    border-bottom:200px solid gold;
}

/*CIRCLE TO TRIANGLE 2  */

#tr2{
    float:right;
    position:relative;    
    cursor:pointer;    
    width:200px;
    height:200px;
}
#tr2:before, #tr2:after{
    content:'';
    position:absolute;
}
#tr2:before{
    top:0; left:0;
    background:gold;
    border-radius:50%;
    width:100%;
    height:100%;
    -webkit-transition: width 1s, height 1s, left 1s, top 1s;
}
#tr2:after{
    left:20px;
    top:0;
    border-left:80px solid transparent;
    border-right:80px solid transparent;
    border-bottom:160px solid gold;
    -webkit-transition: border-left 1s, border-right 1s, border-bottom 1s,left 1s, top 1s;
    z-index:-1;
}
#tr2.animate:before{
    width:0;
    height:0;
    top:50%; left:50%;
}
#tr2.animate:after{
    top:0; left:0;
    border-left:100px solid transparent;
    border-right:100px solid transparent;
    border-bottom:200px solid gold;
}

0

我将授予@rje,因为他让我朝着正确的方向前进,并且在所有的努力之后,我意识到我的问题没有具体说明我的问题。

首先,我将解释我实际上正在寻找什么:基本上,在画布上创建一个“形状”时,很容易将其用作蒙版。通过蒙版,我指的是,假设您在画布中央绘制一个圆形,它保持透明,但画布填充有某种颜色。 直接使用raphael js来完成这项任务有点奇怪,直到你掌握了它,因为它实际上是svg,并且有不同的规则。您必须绘制一个外部路径,该路径将与纸张(raphael“画布”)匹配,并且一个内部路径将保持透明。对我来说,挑战是使其也具有响应性,但我最终成功地使用类似于此的东西实现了它

var tw; // canvas width
var th; // canvas height
var tr; // radius of circle, in my case calculated dynamically smth like (th - 20) / 2
var outerpath = "M0,0L" + tw + ",0L" + tw + "," + th + "L0," + th + "L0,0z";
var innerpath = "M" + (tw/2) + "," + (th/2 - tr) + "A" + tr + "," + tr + "  0 1,0 " + (tw/2 + 0.1) + "," + (th/2 - tr) + "z";
var path = outerpath + innerpath;
var mask = paper.path(path).attr({'stroke': 0, 'fill' :  '#ccc', 'fill-opacity' : 0.9});

使用 Raphael 的好处在于,您可以对它们进行动画处理,并且还可以使用例如 innerpath 来创建另一个形状并使用另一种颜色填充它。太神奇了。
谢谢大家。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接