在<canvas>元素</canvas>中设置剪贴区域的动画

时间:2012-01-02 16:15:22

标签: html5 animation canvas clipping clip

我已经使用stackoverflow.com作为灵感和解决问题的源头已经好几个月了。到目前为止,我从未遇到过没有解决方案的问题,这就是为什么我首先要在此自我介绍,并与所有感兴趣的人分享我的问题。

在过去的几周里,我尝试在画布元素上设置某些形状和线条的动画效果,以创建一些有趣的效果 - 例如手写或类似效果。
为了实现这一点,我使用一些利用canvas元素的.clip()命令的技术来隐藏并逐渐显示预渲染图像(形式,线......)“等待”的区域。 我遇到的问题,与确定canvas元素中剪切区域的变量有关。在增加(但不减少)动画中的值时似乎有一些奇怪的问题 因为所有这些听起来都很奇怪,我所知道的,这是我所谈论的代码的相关部分。

$(document).ready(function() {
    var ctx = $( "#canvas" )[0].getContext("2d");
    ctx.fillStyle = "#a00";
    var recW = 200;

    function animate() {
        ctx.clearRect(0,0,400,400);

        ctx.beginPath();
        ctx.rect(50,50,recW,100);
        ctx.clip();

        ctx.beginPath();
        ctx.arc(250,100,90,0,Math.PI*2,true);
        ctx.fill();

        recW--;

        if (recW == 150) clearInterval(run);
    }
    var run = setInterval(function() { animate(); },60);
});

以上代码完美无缺。它在400 * 400画布中绘制一个矩形,将其用作剪裁区域,然后绘制圆圈,然后相应地剪切该圆圈。通过动画间隔,剪切矩形的长度然后减小到150的测试值。到目前为止,这么好。但是这里出现了让我数小时徘徊数小时的部分:

$(document).ready(function() {
    var ctx = $( "#canvas" )[0].getContext("2d");
    ctx.fillStyle = "#a00";
    var recW = 150;

    function animate() {
        ctx.clearRect(0,0,400,400);

        ctx.beginPath();
        ctx.rect(50,50,recW,100);
        ctx.clip();

        ctx.beginPath();
        ctx.arc(250,100,90,0,Math.PI*2,true);
        ctx.fill();

        recW++;

        if (recW == 200) clearInterval(run);
    }
    var run = setInterval(function() { animate(); },60);
});

如果我转动整个动画,以剪切矩形的宽度150开始,并使用recW ++将其增加到测试值200,突然动画不再起作用。变量的逐渐增加没有问题,但可见剪切区域不会增长
我怀疑我或许只是忽略了这里显而易见的事情,但我似乎无法找到错误,如果有人能指出我正确的方向,我会非常感激;)

非常感谢 拉特里贡

2 个答案:

答案 0 :(得分:1)

你的问题很难调试,除非你有很多经验(我也没有)。

你可以为形状设置动画变小而不是变大的原因是因为,我怀疑,你正在将剪辑加在一起。因此,随着它们变小,一切看起来都很好,因为您期望最小的剪辑区域。但是,当剪辑变大时,您与原始的小剪辑区域进行AND运算,因此看起来没有动画。

要解决此问题,您需要在剪辑的末尾发出restore()来电。但是,要实现此功能,您还需要在剪辑的开头进行save()调用。最后,我添加了一个边框来指示剪辑的确切位置,并且因为这是一个填充和一个笔划,我放置了另一个beginPath语句,以便不剪切剪辑区域外的圆圈(我们刚从)。

以下是完整的jsFiddle code

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

ctx.fillStyle = "#a00";
var recW = 150;

function animate2() {
    ctx.clearRect(50,50,canvas.width,recW - 1);

    ctx.save();

    ctx.beginPath();
    ctx.rect(50, 50, recW, recW);
    ctx.clip();

    ctx.beginPath();
    ctx.arc(250,100,90,0,Math.PI*2,true);
    ctx.fill();

    ctx.restore();
    ctx.beginPath();
    ctx.rect(50 - 1, 50 - 1, recW + 2, recW + 2);
    ctx.lineWidth = 10;
    ctx.stroke();
    console.log(recW);
    recW++;

    if (recW == 300) clearInterval(run);
}
var run = setInterval(function() { animate2(); },5);

答案 1 :(得分:1)

以下是关于如何animate a clipping path.的一个很好的指南你的问题是,一旦制作剪辑,你必须把它拿走才能长出它,所以你使用保存状态和画布擦拭来产生这个效果。

以下是一些示例代码。

<canvas  id="slide29Canvas2" width="970px" height="600px"></canvas>

<script>
    // Grabs the canvas element we made above
var ca1=document.getElementById("slide29Canvas1");

// Defines the 2d thing, standard for making a canvas
var c1=ca1.getContext("2d");

// Creates an image variable to hold and preload our image (can't do animations on an image unless its fully loaded)
var img1 = document.createElement('IMG');

// Loads image link into the img element we created above
img1.src = "http://tajvirani.com/wp-content/uploads/2012/03/slide29-bg_1.png";

// Creates the first save event, this gives us a base to clear our clipping / mask to since you can't just delete elements.
c1.save();

// Our function for when the image loads
img1.onload = function () {

    // First call to our canvas drawing function, the thing that is going to do all the work for us.
        // You can just call the function but I did it through a timer
    setTimeout(function() { drawc1r(0); },5);

        // The function that is doing all the heavy lifting. The reason we are doing a function is because
        // to make an animation we have to draw the circle (or element) frame by frame, to do this manually would be to time
        // intensive so we are just going to create a loop to do it. 'i' stands for the radius of our border
        // so over time our radius is going to get bigger and bigger.
    function drawc1r(i) {

        // Creates a save state. Imagine a save state like an array, when you clear one it pops last element in the array off the stack
        // When you save, it creates an element at the top of the stack. So if we cleared without making new ones, we would end up with nothing on our stage.
    c1.save();

        // This clears everything off the stage, I do this because our image has transparency, and restore() (the thing that pops one off the stack)
        // Doesn't clear off images, and so if we stick an image on the stage over and over, the transparency will stack on top of each other and
        // That isn't quite what we want.
    c1.clearRect(0, 0, ca1.width, ca1.height);

        // Adds one to the radius making the circle a little bigger with every step
    i++;

        // Tells canvas we are going to start creating an item on the stage - it can be a line, a rectangle or any shape you draw, but whatever
        // after this path will be added to the clip when its called. I can have 3 rectangles drawn and that would make a clip.
    c1.beginPath();

        // Can't make a true circle, so we make an arced line that happens to trace a circle - 'i' is used to define our radius.
    c1.arc(853, 320, i, 0, 2 * Math.PI, false);

        // After everything is defined, we make a clip area out of it.
    c1.clip();

        // Now that we have the clip added to it, we are going to add the image to the clip area.
    c1.drawImage(img1, 0, 0);

        // This pops one off the stack which gets rid of the clip so we can enlarge it and make it again on the next pass
    c1.restore();

        // Here is the final size of the circle, I want it to grow the circle until it hits 800 so we set a timeout to run this function again
        // until we get the size we want. The time in milliseconds pretty much defines your framerate for growing the circle. There are other
        // methods for doing this that give you better frame rates, but I didn't have much luck with them so I'm not going to include them.
    if(i < 800) {
        setTimeout(function() { drawc1r(i); },5);
    }

}

相关问题