4

私は何ヶ月もの間、インスピレーションと問題解決の源として stackoverflow.com を使用してきました。これまでのところ、解決策のない問題に遭遇したことは一度もありません。そのため、ここでまず自己紹介し、関心のあるすべての人に問題を共有したいと思います。

ここ数週間、手書きなどの興味深い効果を作成するために、canvas 要素で特定の図形や線をアニメーション化してみました。
これを実現するために、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 のキャンバスに四角形を描画し、それをクリッピング領域として使用し、その後円を描画し、それに応じてこの円をクリッピングします。アニメーション Interval によって、クリッピング四角形の長さがテスト値の 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 まで増やすと、突然アニメーションが機能しなくなります。変数の漸進的な増加は問題なく機能しますが、目に見えるクリッピング領域は拡大しません。

おそらくここで明らかなことを見落としているだけだと思いますが、エラーを見つけることができないようです。誰かが私を正しい方向に向けることができれば、非常に感謝しています;)

ありがとう
トリコン

4

2 に答える 2

2

あなたが多くの経験を持っていない限り、あなたの問題はデバッグするのが難しい問題です(私もそうしませんでした)。

シェイプが大きくなるのではなく、小さくなるようにアニメートできる理由は、クリップをANDで結合しているためだと思います。したがって、クリップ領域が最小になると予想されるため、サイズが小さくなると、すべてが正常に表示されます。ただし、クリップが大きくなると、元の小さなクリップ領域とANDで結合しているため、アニメーションがないように見えます。

これを修正するに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 に答える
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 に答える