Question

Is it possible to move an image behind a mask without moving the mask itself ? I am looking for an operation which allow to move an image behind a mask and it should be accurate and smooth.

The best answer I am looking is to mask an Kinetic.Image object. Kinetic.Image is draggable and need to worry about it's movement. Please let me know if it's really possible to mask Kinetic.Image Object ?

Was it helpful?

Solution

A Demo: http://jsfiddle.net/m1erickson/u28MS/

Use a Kinetic.Shape to get access to the canvas context and then create a clipping region.

  • Create a new Kinetic.Shape

  • Define your non-rectangular path in the shape

  • Call clip() to restrict drawing to that path.

  • Draw the image into the clipping region.

  • Give the image x & y properties so that the image can be draw

Here's what that looks like in code:

// create a Kinetic.Shape which gives you access
// to a context to draw on

clippingShape = new Kinetic.Shape({
    sceneFunc: function(context) {

      // define your path here
      // context.beginPath(); ...

      // make your path a clipping region

      context.clip();

      // draw the image inside the clipping region
      // img.x & img.y are offsets which can be used
      // to "drag" the image around the clipping region

      context.drawImage(img,img.x,img.y);

      // KineticJS specific context method
      context.fillStrokeShape(this);

    },
    stroke: 'black',
    strokeWidth: 4,
    listening:false
});

Listen for mouse events on the stage to cause the image to reposition when drawn in the Shape.

  • In mousedown: Save the mouse position and set a flag indicating the drag has begun.

  • In mousemove: Calc how much the mouse has moved and offset the image's x/y by that distance.

  • In mouseup: clear the dragging flag since the drag is over.

The mouse event handlers look like this:

var isdown=false;

stage.getContent().onmousedown=function(e){ 
    var pos=stage.getPointerPosition();
    img.lastX=parseInt(pos.x);
    img.lastY=parseInt(pos.y);
    isdown=true; 
};
stage.getContent().onmouseup=function(e){ 
    isdown=false; 
};
stage.getContent().onmousemove=function(e){
    if(!isdown){return;}
    var pos=stage.getPointerPosition();
    var mouseX=parseInt(pos.x);
    var mouseY=parseInt(pos.y);
    var dx=mouseX-img.lastX;
    var dy=mouseY-img.lastY;
    img.lastX=mouseX;
    img.lastY=mouseY;
    img.x+=dx;
    img.y+=dy;
    layer.draw();
};

[ Previous version of answer -- replaced with new answer above after questioners commments ]

This kind of clipping is traditionally done with a foreground image that contains a transparent "viewport" which lets the user see a portion of the background image beneath.

A Demo: http://jsfiddle.net/m1erickson/2f9yu/

enter image description here

Create a draggable background image on a bottom layer:

// create a background layer

var bottomLayer=new Kinetic.Layer();
stage.add(bottomLayer);

// put a draggable image on the background layer

var city=new Kinetic.Image({ image:bk,x:0,y:0,draggable:true,width:700,height:440, });
bottomLayer.add(city);
bottomLayer.draw();

Create a non-draggable foreground image on a top layer.

The top image has a transparent "viewport".

Important: the top layer does not listen for events, so dragging moves the bottom image, not the top image.

// create a top layer that does not respond to mouse events
// any mouse events will filter down to the background image
// this enables the background to be dragged even while behind the top image

var topLayer=new Kinetic.Layer({listening:false,});
stage.add(topLayer);

// create a top image with transparent pixels 
// used as a viewport to see a portion of the bottom image

var mirror=new Kinetic.Image({ image:viewport,x:0,y:0 });
topLayer.add(mirror);
topLayer.draw();

Example code:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Prototype</title>
    <script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
    <script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v5.0.1.min.js"></script>
<style>
body{padding:20px;}
#container{
  border:solid 1px #ccc;
  margin-top: 10px;
  width:350px;
  height:300px;
}
</style>        
<script>
$(function(){

    var stage = new Kinetic.Stage({
        container: 'container',
        width: 350,
        height: 300
    });
    var layer = new Kinetic.Layer();
    stage.add(layer);

    var bottomLayer=new Kinetic.Layer();
    stage.add(bottomLayer);
    var topLayer=new Kinetic.Layer({listening:false,});
    stage.add(topLayer);

    var loadedCount=0;
    //
    var bk=new Image();
    bk.onload=start;
    bk.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/desert1.jpg";
    //
    var viewport=new Image();
    viewport.onload=start;
    viewport.src="https://dl.dropboxusercontent.com/u/139992952/multple/car4.png";

    function start(){
        if(++loadedCount<2){return;}

        var city=new Kinetic.Image({ image:bk,x:0,y:0,draggable:true,width:700,height:440, });
        bottomLayer.add(city);
        bottomLayer.draw();

        var mirror=new Kinetic.Image({ image:viewport,x:0,y:0 });
        topLayer.add(mirror);
        topLayer.draw();

    }


}); // end $(function(){});

</script>       
</head>
<body>
    <h4>Drag to move the background image in the mirror</h4>
    <div id="container"></div>
</body>
</html>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top