4

几个月来,我一直使用 stackoverflow.com 作为灵感和解决问题的来源。到目前为止,我从来没有遇到过没有解决方案的问题,这就是我首先想在此介绍自己的原因,并与所有感兴趣的人分享我的问题。

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

$(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,突然动画不再起作用。变量的逐渐增加没有问题,但可见剪切区域没有增加。

我怀疑我可能只是在这里忽略了明显的问题,但我似乎根本找不到错误,如果有人能指出我正确的方向,我将非常感激;)

非常感谢
Tricon

4

2 回答 2

2

这是一个关于如何为剪切路径设置动画的很好的指南。您的问题是,一旦制作了剪辑,您必须将其拿走才能使其生长,因此您使用保存状态和画布擦除来产生这种效果。

这是一些示例代码。

<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);
    }

}

于 2012-03-30T18:05:00.470 回答
2

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

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

要解决此问题,您需要restore()在剪辑结束时拨打电话。但是,要使其正常工作,您还需要save()在剪辑的开头进行调用。最后,我添加了一个边框来指示剪辑的确切位置,并且由于这是一个填充和一个笔划,我放置了另一个beginPath语句以不在剪辑区域(我们刚刚恢复)之外描边圆。

这是完整的jsFiddle 代码

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);
于 2012-01-03T08:13:06.853 回答