Artanis
Artanis

Reputation: 1005

Canvas clipping mask positioning issue

Long story short - take a look at simple fiddle below. Long story - I'm using HTML5 Canvas and SVG path clipping mask to mask out custom shape from image. Everything works as expected, apart from when I try to position container elswhere.

You can see issue in question in this Fiddle right here - https://jsfiddle.net/eyv35f6k/1/

body {
  padding: 0; 
  margin: 0
  }
  
  .container {
    position:absolute;
    top: 0px;
    /* *********** top: 50px; *********** */
  }

.css-clipped {
  display: block; 
  -webkit-clip-path:url(#svgPath);
  clip-path:url(#svgPath);
  }
<!- Element that cointains image that must be clipped by using custom shape  ->
  <div class="container">
    <img class="css-clipped" src="https://sarasoueidan.com/demos/css-svg-clipping/html-img-clipped-with-css/flowers.jpg">
  </div>


<!- SVG that contains clop path ->
  <svg height="0" width="0">
    <defs>
      <clipPath id="svgPath">
        <path fill="#FFFFFF" stroke="#000000" stroke-width="1.0" stroke-miterlimit="10" d="m49,0c2,0 4,0 6,0c16,1 30,3 36,13c6,9 7,23 7,36c0,13 -1,27 -7,36c-6,10 -20,12 -36,13c-2,0 -4,0 -6,0l0,0c-2,0 -4,0 -6,0c-16,-1 -30,-3 -36,-13c-6,-9 -7,-23 -7,-36c0,-13 1,-27 7,-36c6,-10 20,-12 36,-13c2,0 4,0 6,0l0,0z"/>
      </clipPath>
    </defs>
  </svg>

If you will try to move around .container element, you will see that container and image inside it moves, while canvas mask remains in the same top left corner of the page body, instead of moving together with element it's been applied to. I made example picture in photoshop to illustrate what I'm trying to achieve:

enter image description here

Could you please advise me, how to make it so that canvas mask moves together with element it's applied to? Thanks in advance.

Upvotes: 0

Views: 1280

Answers (1)

markE
markE

Reputation: 105015

You can use clipping in at least 2 ways:

Viewport Mode:

As you drag, clipping reveals a "window" onto different smaller parts of the larger image underneath.

Drag-a--static-Clip Mode:

A small static image is clipped from the larger image. As you drag, the static image is moved around the canvas.

Here's example code and a Demo showing both clipping modes.

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
    var BB=canvas.getBoundingClientRect();
    offsetX=BB.left;
    offsetY=BB.top;        
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }

var isDown=false;
var startX,startY;

var ox=0;
var oy=0;
var width=75;
var height=50;
var mode='Viewport'
var clippedImage;

var img=new Image();
img.onload=start;
img.src="https://sarasoueidan.com/demos/css-svg-clipping/html-img-clipped-with-css/flowers.jpg";
function start(){
    clipImage=createClipImg(ox,oy,width,height);
    $("#canvas").mousedown(function(e){handleMouseDown(e);});
    $("#canvas").mousemove(function(e){handleMouseMove(e);});
    $("#canvas").mouseup(function(e){handleMouseUpOut(e);});
    $("#canvas").mouseout(function(e){handleMouseUpOut(e);});
    //
    $('input[type=radio][name=group1').change(function(){
        mode=this.value;
        if(mode=='Drag'){
            clippedImage=createClipImg(ox,oy,width,height);
        }
        draw();
    });
    draw();
}

function draw(){
    ctx.clearRect(0,0,cw,ch);
    if(mode=='Viewport'){
        clippedViewport(ox,oy,width,height);
    }else{
        ctx.drawImage(clippedImage,ox,oy);
    }
}

function clippedViewport(x,y,w,h){
    ctx.save();
    ctx.beginPath();
    ctx.rect(x,y,w,h);
    ctx.closePath();
    ctx.clip();
    ctx.drawImage(img,0,0);
    ctx.restore();
}

function createClipImg(x,y,w,h){
    var c=document.createElement('canvas');
    var cctx=c.getContext('2d');
    c.width=w;
    c.height=h;
    cctx.drawImage(img,-x,-y);
    return(c);
}



function handleMouseDown(e){
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();
  //
  startX=parseInt(e.clientX-offsetX);
  startY=parseInt(e.clientY-offsetY);
  // Put your mousedown stuff here
  if(startX>ox && startX<ox+width && startY>oy && startY<=oy+height){
      isDown=true;
  }
}

function handleMouseUpOut(e){
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();
  // Put your mouseup stuff here
  isDown=false;
}

function handleMouseMove(e){
  if(!isDown){return;}
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();
  //
  mouseX=parseInt(e.clientX-offsetX);
  mouseY=parseInt(e.clientY-offsetY);
  // Put your mousemove stuff here
  var dx=mouseX-startX;
  var dy=mouseY-startY;
  startX=mouseX;
  startY=mouseY;
  //
  ox+=dx;
  oy+=dy;
  draw();
}
body{ background-color: ivory; }
canvas{border:1px solid red; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
Mode:&nbsp
<input type=radio name=group1 value=Viewport checked>Viewport
<input type=radio name=group1 value=Drag>Drag clip
<hr>
"Viewport" reveals different part of the image underneath.
<br>
"Drag clip" drags the same clipped image part around the canvas.
<hr>
<canvas id="canvas" width=300 height=300></canvas>

[Addition: Images clipped into html5 canvas]

You can draw a clipped image onto an html5 canvas (1 canvas per image) and then use the canvas just like you would use an image.

enter image description hereenter image description here

var img=new Image();
img.onload=start;
img.src="https://sarasoueidan.com/demos/css-svg-clipping/html-img-clipped-with-css/flowers.jpg";
function start(){
    var container=document.getElementById('container');
    for(var i=0;i<6;i++){
        makeClippedCanvas(img,100,100,container);
    }
}


function makeClippedCanvas(img,w,h,container){
    var c=document.createElement("canvas");
    var ctx=c.getContext("2d");
    c.width=w;
    c.height=h;
    //
    ctx.moveTo(50,0);
    ctx.bezierCurveTo(100,5, 100,15, 100,50);
    ctx.bezierCurveTo(100,85, 100,95, 50,100);
    ctx.bezierCurveTo(0,95, 0,85, 0,50);
    ctx.bezierCurveTo(0,15, 0,5, 50,0);
    ctx.closePath();
    ctx.stroke();
    ctx.clip();
    ctx.drawImage(img,0,0);
    container.appendChild(c);
    return(c);
}
body{ background-color: ivory; }
#container{width:50%; border:1px solid blue;}
canvas{margin:5px;}
<h4>Resize the window</h4>
<div id=container></div>

Upvotes: 1

Related Questions