To ensure that all drawing occurs within a cylindrical container, you can create the container as a clipping region.
This method guarantees that all content will only be visible inside the cylinder-shaped container.
For a visual demonstration and sample code, check out this Demo.
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
// canvas related variables
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
// general variables
var PI=Math.PI;
var PI2=PI*2;
// cylinder related variables
var cx=cw/2;
var cy=ch/2;
var width=65;
var height=100;
var fillY=cy+height/2+5;
var w2=width/2;
var h2=height/2;
var h4=height/4;
var h8=height/8;
var h16=height/16;
var ytop=-h2+h8;
var cpYtop=-h2-h16;
var ybottom=h2-h8;
var cpYbottom=h2+h16;
var degreeAngle,rAngle,dx,dy,r,a,xx,yy;
// start the cylinder upright (at 0 degree angle)
setContainerAngle(0);
// start the animations
requestAnimationFrame(animateFill);
// animate filling the cylinder
function animateFill(){
if(fillY>cy-height/2+h8){
requestAnimationFrame(animateFill);
}else{
requestAnimationFrame(animateEmpty);
}
draw();
drawPouring(cx,0,fillY);
fillY-=0.50;
}
// animate emptying the cylinder
function animateEmpty(){
if(degreeAngle>-91){
requestAnimationFrame(animateEmpty);
}else{
fillY=cy+height/2+5;
requestAnimationFrame(animateToBeginning);
}
draw();
drawPouring(xx,yy,ch);
setContainerAngle(degreeAngle-0.50);
}
// animate rotating the empty cylinder back to upright
function animateToBeginning(){
if(degreeAngle<=0){
requestAnimationFrame(animateToBeginning);
}else{
setContainerAngle(0);
requestAnimationFrame(animateFill);
}
draw();
setContainerAngle(degreeAngle+1);
}
// draw the scene (background, cylinder, liquid in cylinder)
function draw(){
ctx.fillStyle="gray";
ctx.fillRect(0,0,cw,ch);
ctx.save();
defineFillOutline(cx,cy,width,height,degreeAngle);
if(degreeAngle>=-90){
ctx.clip();
ctx.fillStyle='gold';
ctx.fillRect(0,Math.max(fillY,yy),cw,ch);
}
ctx.restore();
drawContainer(cx,cy,width,height,degreeAngle);
}
// draw the liquid being poured in a vertical stream
function drawPouring(xx,yy,yyy){
ctx.save();
ctx.beginPath();
ctx.moveTo(xx,yy);
ctx.lineTo(xx,yyy);
ctx.lineWidth=5;
ctx.shadowColor="gold";
ctx.shadowBlur=8;
ctx.strokeStyle="gold";
ctx.stroke();
ctx.restore();
}
// define the clipping region (which is the cylinder)
function defineFillOutline(x,y,w,h,degrees){
ctx.save();
ctx.translate(x,y);
ctx.rotate(degreeAngle*PI / 180);
//
ctx.beginPath();
ctx.moveTo(-w2,ytop);
ctx.bezierCurveTo( -w2,cpYtop, w2,cpYtop, w2,ytop);
ctx.lineTo(w2,h2-h8);
ctx.bezierCurveTo( w2,cpYbottom, -w2,cpYbottom, -w2,ybottom);
ctx.closePath();
//
ctx.restore();
}
// draw the cylinder at the specified angle
function drawContainer(cx,cy,width,height,degreeAngle){
//
defineFillOutline(cx,cy,width,height,degreeAngle);
//
ctx.save();
ctx.translate(cx,cy);
ctx.rotate(degreeAngle*PI / 180);
// this is the top-outer lip of the cylinder
ctx.moveTo(-w2,-h2+h8);
ctx.bezierCurveTo( -w2,-h4, w2,-h4, w2,-h2+h8);
ctx.strokeStyle="royalblue";
ctx.lineWidth=2;
ctx.stroke();
//
ctx.restore();
}
// change the angle of the cylinder
function setContainerAngle(degrees){
degreeAngle=degrees;
rAngle=degreeAngle*Math.PI/180;
dx=width/2;
dy=height/2-height/8;
r=Math.sqrt(dx*dx+dy*dy);
a=Math.atan2(dy,dx)+Math.PI+rAngle;
xx=cx+r*Math.cos(a);
yy=cy+r*Math.sin(a);
}
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>