-->

How to make an eraser with kineticjs 5.1.0 acting

2019-06-11 23:03发布

问题:

I want to make an eraser with kineticjs, but i have some problem.

The eraser need to be efficient on multiple layer (layerA, layerB in my code) and with a simple button or some check box I can choose if the eraser will work on the first or second layer, and why not on all of them. I also need to keep them draggable.

her is a jsfiddle that show what I've explained:

http://jsfiddle.net/junkees/jA2V8/2/

     var stage = new Kinetic.Stage({
    container: 'container',
    width: 400,
    height: 500
});
var layerA = new Kinetic.Layer();
var imageObj = new Image();
imageObj.onload = function() {
  var x = new Kinetic.Image({
    x: 0,
    y: 0,
    image: imageObj,    
    draggable:false
  });

  // add the shape to the layer
  layerA.setListening(false);
  layerA.add(x);


  // add the layer to the stage
  stage.add(layerA);
  layerA.setZIndex(10);
  //layerA.draw();
};
imageObj.src="https://imagizer.imageshack.us/v2/595x397q90/707/u8q3.jpg"

var layerB = new Kinetic.Layer();
var imagj = new Image();
imagj.onload = function() {
  var x = new Kinetic.Image({
    x: 0,
    y: 0,
    image: imagj,
  });

  // add the shape to the layer
  layerB.setDraggable(true);
  layerB.setListening(true);
  layerB.add(x);


  // add the layer to the stage
  stage.add(layerB);
  layerB.setZIndex(100);

  layerB.draw();
};
imagj.src = 'http://jsfiddle.net/img/initializing.png';

i whant to be able to erase the frog (or something other of her layer) and to erase a part of the cup of coffee(second layer) with my mouse, with a circle (the radius doesn't matter, i'll make a jquery slider to define it's size ;) )

I'm using the latest version of kineticjs, the 5.1.0

sorry for all the previous post about this question, I've read them but them didn't work for me because of the version. I've already search everywhere google let me search and didn't found something that can help me so I ask her my question

Here I've found something that partially works:

http://jsfiddle.net/junkees/jA2V8/3/

In this one I've created a new shape and attach it to the layer, because the shape and the image are both in the same layer and the layer is draggable so it'll drag both of them like you can see but it's just a micro part of what I want :/

I've edited my code and her I can say little by little i'll success to do it! her the new jsfiddle: http://jsfiddle.net/junkees/jA2V8/5/ and her some problem:

  • even if i've set the destination out I can't erase, juste write
  • I really don't see how to proceed when i whant to stop to "erase"

Am I in the right way to finish this?

回答1:

globalCompositeOperation is used to "erase" existing canvas pixels using new drawings (like your circle-dragged-with-mouse).

KineticJS is not yet well suited to creating an "eraser" tool. That's because it doesn't yet support globalCompositeOperation.

A workaround is to create a Kinetic.Shape object which lets you draw with native canvas commands.

Then you get a reference to a native canvas context while using a Kinetic.Shape like this:

var ctx=yourLayer.getContext()._context;

With this native canvas context you can use globalCompositeOperation to turn the custom shape into an eraser.

ctx.globalCompositeOperation='destination-out';

// now any new drawings will erase existing pixels

You will need to have a Kinetic.Shape "eraser" on every layer that you want to penetrate with the erasings.

Here is a Demo: http://jsfiddle.net/m1erickson/tf7m3/

  • The left 25% of the image is the bottom layer (showing the original image)
  • The next 25% shows the middle layer (a red rect at 50% opacity)
  • The next 25% shows both the middle layer & top layer (top=a blue rect at 50% opacity)
  • The last 25% shows the top layer (again, the blue rect at 50% opacity)

The circles show the Kinetic.Shape erasers on both the middle and top layers in action. They use 'destination-out' compositing to erase the red and blue rectangles on the middle & top layers. The result is that the bottom layer shows through the "erased" circles.

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:219px;
}
</style>        
<script>
$(function(){

    var stage = new Kinetic.Stage({
        container: 'container',
        width: 350,
        height: 219
    });
    var bottomlayer = new Kinetic.Layer();
    stage.add(bottomlayer);
    var middlelayer = new Kinetic.Layer();
    stage.add(middlelayer);
    var toplayer = new Kinetic.Layer();
    stage.add(toplayer);

    var sw=stage.width();
    var sh=stage.height();
    var cutouts=[];
    var PI2=Math.PI*2;

    var img=new Image();
    img.onload=start;
    img.src="https://dl.dropboxusercontent.com/u/139992952/stack1/landscape1.jpg";
    function start(){
        var kImage=new Kinetic.Image({
            image:img,
        });
        bottomlayer.add(kImage);
        bottomlayer.draw();
        middlelayer.draw();
        toplayer.draw();
    };

    var midRed=new Kinetic.Rect({
        x:sw/4,y:0,width:stage.width()/2,height:stage.height(),
        fill:"red",
        opacity:0.50
    });
    middlelayer.add(midRed);

    var middleEraser=new Kinetic.Shape({
      x:0,y:0,
      fill:"blue",
      drawFunc: function(context) {
        var ctx=middlelayer.getContext()._context;
        ctx.save();
        ctx.globalCompositeOperation="destination-out";
        ctx.beginPath();
        for(var i=0;i<cutouts.length;i++){
            var cut=cutouts[i];
            ctx.arc(cut.x,cut.y,15,0,PI2);
            ctx.closePath();
        }
        ctx.fill();
        ctx.restore();
      }
    });
    middlelayer.add(middleEraser);



    var topBlue=new Kinetic.Rect({
        x:stage.width()/2,y:0,width:stage.width()/2,height:stage.height(),
        fill:"blue",
        opacity:0.50
    });
    toplayer.add(topBlue);
    toplayer.draw();

    var topEraser=new Kinetic.Shape({
      x:0,y:0,
      fill:"blue",
      drawFunc: function(context) {
        var ctx=toplayer.getContext()._context;
        ctx.save();
        ctx.globalCompositeOperation="destination-out";
        ctx.beginPath();
        for(var i=0;i<cutouts.length;i++){
            var cut=cutouts[i];
            ctx.arc(cut.x,cut.y,15,0,PI2);
            ctx.closePath();
        }
        ctx.fill();
        ctx.restore();
      }
    });
    toplayer.add(topEraser);

    stage.on('contentClick',function(){
        var pos=stage.getPointerPosition();
        var mouseX=parseInt(pos.x);
        var mouseY=parseInt(pos.y);
        cutouts.push({x:mouseX,y:mouseY});
        middlelayer.draw();
        toplayer.draw();
    });

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

</script>       
</head>
<body>
    <h4>Click to "erase" top 2 layers & reveal bottom image</h4>
    <div id="container"></div>
</body>
</html>