中的动画剪辑区域元素

发布于 2024-12-24 02:55:47 字数 1686 浏览 2 评论 0原文

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

在过去的几周里,我尝试在画布元素上对某些形状和线条进行动画处理,以创建一些有趣的效果 - 例如手写或类似效果。
为了实现这一点,我使用了一些技术,利用画布元素的 .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,突然动画不再起作用。变量的逐渐增加没有问题,但可见剪切区域不会增长。

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

非常感谢
特里康

I've used stackoverflow.com as a source of inspiration and problem-solving for many months now. I've just never run into a problem without a solution so far, which is the reason why I'd first like to introduce myself hereby, and to share my problem with everyone who's interested.

For the past couple of weeks I've tried animating certain shapes and lines on a canvas element, in order to create some interesting effects - such as handwriting or similar.
In order to achieve this, I use some technique which utilizes the .clip() command of the canvas element, to hide and gradually reveal areas under which a pre-rendered image (forms, lines...) "waits".
The problem I run into here, has to do with the variables which determine the clipping area in the canvas element. It seems to have some strange problem with increasing (but not decreasing) the values in the animation.
And since all this sounds very weird, which I am aware of, here is the relevant part of the code I am talking about.

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

The above code works perfectly fine. It draws a rectangle in a 400*400 canvas, uses it as a clipping region, draws the circle afterwards, and this circle is then clipped accordingly. Through the animation Interval, the length of the clipping-rectangle is then decreased to a test-value of 150. So far, so good. But here comes the part which has kept me riddled for hours on end:

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

If I turn the whole animation around, begin with a width of 150 for the clipping rectangle, and increase it with recW++ up to the test-value of 200, suddenly the animation does no longer work. The gradual increase of the variable works without problems, but the visible clipping area does not grow.

I suspect that I am perhaps just overlooking the obvious here, but I simply cannot seem to find the error and I'd be very thankful if somebody could point me into the right direction ;)

Thanks a lot
Tricon

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

别忘他 2024-12-31 02:55:47

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

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

要解决此问题,您需要在剪辑末尾调用 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);

Your problem is a tricky one to debug unless you have a lot of experience (which I didn't either).

The reason you can animate the shape getting smaller and not it getting larger is because, I suspect, you are ANDing the clips together. Therefore, as they get smaller, everything looks fine since you are expecting the smallest clip area. When the clip is getting larger, however, you are ANDing with the original small clip area, so it appears like there is no animation.

To fix this you need to place a restore() call at the end of your clip. However, for this to work, you also need a save() call at the beginning of your clip. Finally, I added a bordering box to indicate where the clip is exactly, and since this is a fill and a stroke, I placed another beginPath statement to not stroke the circle outside of the clip area (which we just restored from).

Here is the full 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);
逆光下的微笑 2024-12-31 02:55:47

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

这是一些示例代码。

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

}

Here is a nice guide on how to animate a clipping path. Your problem is that once the clip is made, you have to take it away in order to grow it, so you use save states and canvas wipes to make this effect.

Here is some example code.

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

}

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文