Vipul Vaghasiya Vipul Vaghasiya - 5 months ago 21
Javascript Question

Animate clipping mask with canvas

enter image description here

Please see attached image. you can see first slide with canvas masking to animate like second slide.

Answer

Well I have no clue what language you wanted, so Javascript it is, but the principles are the same for whatever SDK or language you use.

Easy to do. Use requestAnimationFrame to make it smooth. Create a set of shapes that you can animate each from and just draw them to the canvas context as normal using moveTo, lineTo or which ever path function you want. Instead of calling fill or stroke call ctx.clip() then draw the image and it will only be displayed in the clipping areas.

Below is a rather kludged example but it will give you the basics, you can improve it to meet your needs.

var canvas = document.getElementById("canV");  // get the canvas
var ctx = canvas.getContext("2d");  // get the context
var blobs = [];  // array to hold blobs
var width = canvas.width;
var height = canvas.height;
var blobPCount = 16;  // how bumpy the blobs are
var wobbla = 0;  // adds a bit of a wobble
var growSpeed = 2;  // grow speed 0.1 very slow 10 mega fast
var cludgeFactor = 0.25;  // used to work out when its all done
var cludgeFactorA = 1-cludgeFactor;  // its a bit of a cludge hence the name
// a wiki commons image
var imageURL = "https://upload.wikimedia.org/wikipedia/commons/e/ee/Baby_Cuttlefish2_%285589806913%29.jpg";
var image = new Image(); // create the image
image.src = imageURL; // load it
var done = false;   // flag is true when done


function addBlob(){  // adds a blob
    var b = {};
    b.x = Math.random()*width;   // find a random pos for it
    b.y = Math.random()*height;
    b.points = [];        // create a set of pointy in a circle that will grow outward
    for(var i = 0; i < Math.PI*2;i+= (Math.PI*2)/blobPCount){
        var p = {};
        // mess up the perfection a little
        var dir= (i+((Math.PI*2)/(blobPCount*3))*(Math.random()-0.5))*(1+2/blobPCount);
        p.dx = Math.cos(dir);  // the delta x and y
        p.dy = Math.sin(dir);
        p.x = p.dx * 5;     // the starting size
        p.y = p.dy * 5;
        p.dx *= growSpeed;   // set the speed
        p.dy *= growSpeed;
        b.points.push(p);   // add the point

    }
    blobs.push(b);  // and the blob
}

function growBlobs(){  // grows the blob
    var i;
    for(i = 0; i < blobs.length; i++){  // for each blob
        var b = blobs[i];
        var pc = b.points.length;
        for(var j = 0; j < pc; j++){  // grow the points
            var p = b.points[j];
            p.x += p.dx+p.dx*Math.random()*0.2;  // move the point with a liitle random
            p.y += p.dy+p.dy*Math.random()*0.2;
        }
    }
}

// creates the clipping mask
function createClipMask(){
    var i;
    ctx.beginPath();  // begin a path
    wobbla += 0.2;    // add some wobbla
    var inside = false;  // the flag to test done
 
    for(i = 0; i < blobs.length; i++){  // for each blob
        var b = blobs[i];
        var pc = b.points.length;       // get len
        for(var j = 0; j < pc-1; j++){  // do eacj point
            var p = b.points[j];
            var x = b.x+p.x + Math.sin(wobbla+i+j*0.2)*10; // get a point
            var y = b.y+p.y + Math.cos(wobbla+i+j*0.2)*10;
            if(j === 0){
                ctx.moveTo(x,y); // move to the first point
            }else{
                j ++;  // all other points as a second order bexier
                p = b.points[j];
                var x1 = b.x +p.x*0.75 + Math.sin(wobbla+i+j*0.2)*10; // add the wobble
                var y1 = b.y +p.y*0.75 + Math.cos(wobbla+i+j*0.2)*10;
                ctx.quadraticCurveTo(x,y,x1,y1);  // create tke bezier path
                // test if the points are inside the screen by cludge factor
                if(!inside && x > width*cludgeFactor && x < width*cludgeFactorA &&
                    y > height*cludgeFactor && y < height*cludgeFactorA){
                    inside = true;  

                }
                
            }
        }
        ctx.closePath();  // done with this blob close the path
    }
    // all blobs done so set the clip
    ctx.clip();
    if(!inside){  // if no points inside the cludge area the we are done
        done = true;
    }
   
}

// make a semi random number of blobs
var numberBlobs = Math.ceil(Math.random()^5) +3;
for(var i = 0; i < numberBlobs; i++){
    addBlob();
}
// do the update
function update(){
    if(done){  // if done draw the image
        ctx.drawImage(image,0,0,width,height);
        return;  // return stops the rendering
    }
    ctx.fillStyle = "white";   // draw the white
    ctx.fillRect(0,0,width,height);
    ctx.save();  // save the current ctx state
    if(image.complete){  // has the image loaded
        growBlobs();    // yes so grow blobs
        createClipMask();  // create the clip
        ctx.drawImage(image,0,0,width,height); //draw the clipped image
    }
    ctx.restore();  // dont need the clip anymore so restore the ctx state
    window.requestAnimationFrame (update); //request a new anim frame

}
update();  // start it all going
.canC {
  width:500px;
  height:400px;
}
<canvas class="canC" id="canV" width=500 height=400></canvas>

Also I had to guess what you wanted so hope I guessed right. Any question please do ask.