how to add movements to bezier curve in kineticjs?

2019-07-04 04:36发布

问题:

I have done this without kinetic.This piece of code is working fab as I wanted. --fiddle without kineticjs. Take a look

Now I want to do the same code with kineticjs. Here is is what I have done till now--fiddle with kineticjs.

The lines don`t move at all?where am I going wrong?I have spent the entire day but couldnt figure out the problem.

This is my javascript with kinetic.results cannot be seen in fiddle because there is no option for including kineticjs.though I have put my code up there.Any help is welcome.

var stage=new Kinetic.Stage({
    container:'container',
    width:500,
    height:500
});

var layer=new Kinetic.Layer();

var bg=new Kinetic.Rect({
    x:0,
    y:0,
    width:stage.getWidth(),
    height:stage.getHeight(),
    fill: 'antiquewhite'
});
layer.add(bg);

var drawHair = function(canvas) {
    var context = canvas.getContext();
    context.beginPath();
    context.moveTo(this.attrs.sx, this.attrs.sy);
    context.bezierCurveTo(this.attrs.cp1x, this.attrs.cp1y, this.attrs.cp2x, this.attrs.cp2y, this.attrs.endx, this.attrs.endy);
    context.closePath();
    canvas.fillStroke(this);
};

function Hair(a, b, c, d, e, f, g, h) {
    return new Kinetic.Shape({
        drawFunc: drawHair,
        fill: '#000',
        lineJoin: 'round',
        stroke: 'grey',
        strokeWidth: 8,
        sx: 136 + a,//start position of curve.used in moveTo(sx,sy)
        sy: 235 + b,
        cp1x: 136 + c,//control point 1
        cp1y: 222 + d,
        cp2x: 136 + e,//control point 2
        cp2y: 222 + f,
        endx: 136 + g,//end points
        endy: 210 + h
    });
}

var hd =[];
function init(){//this function draws each hair/curve
        hd.push(new Hair(0, 0, 0, 0, 0, 0, 0, 0));
        layer.add(hd[0]);
        hd.push(new Hair(15, 0, 15, 0, 15, 0, 15, 0));
        layer.add(hd[1]);
        hd.push(new Hair(30, 0, 30, 0, 30, 0, 30, 0));
        layer.add(hd[2]);
        hd.push(new Hair(45, 0, 45, 0, 45, 0, 45, 0));
        layer.add(hd[3]);
        hd.push(new Hair(60, 0, 60, 0, 60, 0, 60, 0));
        layer.add(hd[4]);
        hd.push(new Hair(75, 0, 75, 0, 75, 0, 75, 0));
        layer.add(hd[5]);
        hd.push(new Hair(90, 0, 90, 0, 90, 0, 90, 0));
        layer.add(hd[6]);
        hd.push(new Hair(105, 0, 105, 0, 105, 0, 105, 0));
        layer.add(hd[7]);
        hd.push(new Hair(120, 0, 120, 0, 120, 0, 120, 0));
        layer.add(hd[8]);

    stage.add(layer);
}

    var bend1=0;
    var bend2=0;
    var bend3=0;
    var bend4=0;
    var bend5=0;
    var bend6=0;
    var bend7=0;
    var bend8=0;
    var bend9=0;

stage.on('mousemove', function() {
    var ref1=135;//this is ref point for  hair or curve no 1
    var ref2=150;//hair no 2 and so on
    var ref3=165;
    var ref4=180;
    var ref5=195;
    var ref6=210;
    var ref7=225;
    var ref8=240;
    var ref9=255;
    var pos = stage.getMousePosition();
    console.log("x="+pos.x+"&&"+"y="+pos.y)
    if(between(pos.x,115,270) && between(pos.y,205,236))
    {
        if(pos.x>=ref1 && pos.x<=145){bend1=(pos.x-ref1)*(2.2);}
        if(pos.x<=ref1 && pos.x>=125){bend1=(pos.x-ref1)*(2.2);}

        if(pos.x>=ref2 && pos.x<=160){bend2=(pos.x-ref2)*(2.2);}
        if(pos.x<=ref2 && pos.x>=140){bend2=(pos.x-ref2)*(2.2);}

        if(pos.x>=ref3 && pos.x<=175){bend3=(pos.x-ref3)*(2.2);}
        if(pos.x<=ref3 && pos.x>=155){bend3=(pos.x-ref3)*(2.2);}

        if(pos.x>=ref4 && pos.x<=190){bend4=(pos.x-ref4)*(2.2);}
        if(pos.x<=ref4 && pos.x>=170){bend4=(pos.x-ref4)*(2.2);}

        if(pos.x>=ref5 && pos.x<=205){bend5=(pos.x-ref5)*(2.2);}
        if(pos.x<=ref5 && pos.x>=185){bend5=(pos.x-ref5)*(2.2);}

        if(pos.x>=ref6 && pos.x<=220){bend6=(pos.x-ref6)*(2.2);}
        if(pos.x<=ref6 && pos.x>=200){bend6=(pos.x-ref6)*(2.2);}

        if(pos.x>=ref7 && pos.x<=235){bend7=(pos.x-ref7)*(2.2);}
        if(pos.x<=ref7 && pos.x>=215){bend7=(pos.x-ref7)*(2.2);}

        if(pos.x>=ref8 && pos.x<=250){bend8=(pos.x-ref8)*(2.2);}
        if(pos.x<=ref8 && pos.x>=230){bend8=(pos.x-ref8)*(2.2);}

        if(pos.x>=ref9 && pos.x<=265){bend9=(pos.x-ref9)*(2.2);}
        if(pos.x<=ref9 && pos.x>=245){bend9=(pos.x-ref9)*(2.2);}
    }
    hd.push(new Hair(0, 0, 0, 0, 0, 0, 0+bend1, 0));
    layer.add(hd[0]);
    hd.push(new Hair(15, 0, 15, 0, 15, 0, 15+bend2, 0));
    layer.add(hd[1]);
    hd.push(new Hair(30, 0, 30, 0, 30, 0, 30+bend3, 0));
    layer.add(hd[2]);
    hd.push(new Hair(45, 0, 45, 0, 45, 0, 45+bend4, 0));
    layer.add(hd[3]);
    hd.push(new Hair(60, 0, 60, 0, 60, 0, 60+bend5, 0));
    layer.add(hd[4]);
    hd.push(new Hair(75, 0, 75, 0, 75, 0, 75+bend6, 0));
    layer.add(hd[5]);
    hd.push(new Hair(90, 0, 90, 0, 90, 0, 90+bend7, 0));
    layer.add(hd[6]);
    hd.push(new Hair(105, 0, 105, 0, 105, 0, 105+bend8, 0));
    layer.add(hd[7]);
    hd.push(new Hair(120, 0, 120, 0, 120, 0, 120+bend9, 0));
    layer.add(hd[8]);
    stage.add(layer);
    console.log("bend1="+bend1);

});

function between(val, min, max) {
    return val >= min && val <= max;
}

window.onload = function() {
    init();
};

回答1:

Problem:

You are adding 9 additional Shape objects with every stage mousemove event.

That means you quickly create hundreds (thousands) of hairs.

Start with a redesign where you have only 9 hairs.

During mousemove events, you would respond by changing each hair's bend in their drawFunc.

[ Edited to include example code ]

You can create a “smart” hair that listens if the mouse is over it and bends itself accordingly.

Then you can add as many smart hairs as you need.

You don't need to track what they're doing, because each one contains enough info+code to bend itself properly.

Since this is just an instructional example, I’ve simplified your curves into a 2 part line which is vertical at the bottom and “bent” at the top.

Here’s the draw function for the custom hair shape. The top of the line is “bent” towards the mouse position. If the mouse has moved inside the hairs response area, the hairs endX property will be set to the mouseX position. This causes the hair to be bent towards the mouseX.

drawFunc: function(canvas){

    if(mouseX>=this.attrs.respondLeft && mouseX<=this.attrs.respondRight){
        this.attrs.endX=mouseX;
    }
    var context = canvas.getContext();
    context.beginPath();
    context.moveTo(this.attrs.startX,this.attrs.bottomY);
    context.lineTo(this.attrs.startX,this.attrs.midY);
    context.lineTo(this.attrs.endX,this.attrs.topY);
    canvas.fillStroke(this);
},

Since we want the hair to bend itself as the mouse moves over it, we add a mousemove event handler for the stage. When the mouse moves the mouseX position is updated. When the hair is redrawn, it will be bent to the mouseX.

stage.on('mousemove', function() {

    // set the endX where the hair will bend to
    mouseX=stage.getMousePosition().x;

    // redraw the layer
    layer.draw();

});

The working code below is a little more complicated because each hair stores its own info about how to draw itself and about its hit area.

Here is code and a Fiddle: http://jsfiddle.net/m1erickson/ey38w/

<!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-v4.5.1.min.js"></script>

<style>
#container{
  border:solid 1px #ccc;
  margin-top: 10px;
  width:300px;
}
</style>        
<script>
$(function(){

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

    var mouseX;

    var hair1=addHair(100,200,20,150);
    var hair2=addHair(120,200,20,150);
    var hair3=addHair(140,200,20,150);
    layer.draw();

    function addHair(x,y,width,height){
        var shape=new Kinetic.Shape({
            drawFunc: function(canvas){

                if(mouseX>=this.attrs.respondLeft && mouseX<=this.attrs.respondRight){
                    this.attrs.endX=mouseX;
                }
                var context = canvas.getContext();
                context.beginPath();
                context.moveTo(this.attrs.startX,this.attrs.bottomY);
                context.lineTo(this.attrs.startX,this.attrs.midY);
                context.lineTo(this.attrs.endX,this.attrs.topY);
                canvas.fillStroke(this);
            },
            lineJoin: 'round',
            stroke: 'grey',
            strokeWidth: 12,
            respondWidth:width,
            respondLeft:x-width/2,
            respondRight:x+width/2,
            respondHeight:height,
            respondTop:y-100,
            topY:y-100,
            midY:y-50,
            bottomY:y,
            startX:x,
            endX:x
        });
        layer.add(shape);
        return(shape);
    }

    // "bend" when mouse is inside any hairs hit boundaries 
    stage.on('mousemove', function() {
        // set the endX where the hair will bend to
        mouseX=stage.getMousePosition().x;
        layer.draw();
    });


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

</script>       
</head>

<body>
    <div id="container"></div>
</body>
</html>

[Additional words of encouragement]

This pattern will work for you.

Yes…even for your bezier curves.

But this is just a starting point that you must adjust for your own project.

For example, in your original code you went through these steps

  • Listen for mousemove events and handle them (your moving function),
  • Determine if the mouse position should affect your hairs,
  • If so, recalculate the control+end points (your init function).
  • Redraw the hairs (only now you know to just redraw the existing hairs--not create new ones),

So modify these code pieces into this pattern!

No, you can't just cut-and-paste. You will have to modify, improve, adapt.

To start, get 1 Bezier hair working the way you like.

Then--not before, take this hint:

Instead of having a separate Kinetic Shape for each hair, create one Shape object and draw all you hairs in its drawFunc. This is more performant. Also, then you can listen for mousemove on that one shape instead of the whole canvas. Again, more performant.

Above all—Learn!: (1)Experiment (2)Test, (3)Adapt, (4)Don't Give Up, (5)Repeat at#1.



回答2:

KineticJS now supports Spline tweening, which means that you can easily animate the points along a curve. Check out this example:

http://www.html5canvastutorials.com/labs/html5-canvas-animated-clown-face/