0

Im trying to create a rope in Fabric. Everything worked fine when it was a straight rope, but if i make it curves a little bit, it becomes like this

https://i.stack.imgur.com/EZeSb.png

i lost a part of my rope pattern when it curved.

So i tried to split it to multiple small rectangles, and add the pattern to all of those, but it doesnt look natural, all the rectangles in a messy coordinate system.

So is there anyway to create a normal curve rope ? thank you!

nam ngo
  • 31
  • 1
  • 6

1 Answers1

3

enter image description here

Yes you can, but its quite complicated and the curve must be relatively "gentle" (no sharp turns).

The curve you use in your question would work fine.

You will have to draw the curved rope on a native html5 canvas and then display that native canvas on FabricJS.

See this link on how to draw a gradient across a curve:

Gradient Stroke Along Curve in Canvas.

Then incrementally draw the rope image along the curve instead of the gradient:

  1. Start with a horizontal rope image,
  2. You're going to slice it vertically into 1px lengths, and
  3. Incrementally draw the rope-slices along the curve with each slice tangent to the curve's angle at each new point on the curve.

You can accomplish #3 by using transformations:

  1. Reset the canvas origin to the a point on the curve: context.translate(curveX,curveY)
  2. Rotate the canvas by the angle tangent to the curve: context.rotate(tangentAngle).
  3. Draw the next slice of the image using the clipping version of drawImage:

    drawImage(ropeImg,sliceX,0,1,ropeImg.height,0,0,1,ropeImg.height);
    
  4. Clean up by undoing the transformations: context.setTransform(1,0,0,1,0,0);

  5. Repeat #1 with the next point on the curve and with the next slice of the rope.
  6. Continue for all the points on the curve.

At this point you have a curved rope on a native html5 canvas.

Create a new FabricJS image using nativeCanvas.toDataURL() as its image source.

Example code and a Demo

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;

// variables defining a cubic bezier curve
var PI2=Math.PI*2;
var s={x:20,y:50};
var c1={x:200,y:70};
var c2={x:40,y:230};
var e={x:270,y:250};

// an array of points plotted along the bezier curve
var points=[];

// we use PI often so put it in a variable
var PI=Math.PI;

// plot 400 points along the curve
// and also calculate the angle of the curve at that point
// NOTE: You may need to adjust the point count (==100 here)
//      if the curve is much shorter or longer than this demo's curve
for(var t=0;t<=100;t+=0.25){

  var T=t/100;

  // plot a point on the curve
  var pos=getCubicBezierXYatT(s,c1,c2,e,T);

  // calculate the tangent angle of the curve at that point
  var tx = bezierTangent(s.x,c1.x,c2.x,e.x,T);
  var ty = bezierTangent(s.y,c1.y,c2.y,e.y,T);
  var a = Math.atan2(ty, tx)-PI/2;

  // save the x/y position of the point and the tangent angle
  // in the points array
  points.push({
    x:pos.x,
    y:pos.y,
    angle:a
  });

}

var img=new Image();
img.onload=function(){
  slicer();
};
img.src='https://dl.dropboxusercontent.com/u/139992952/multple/rope.png';


function slicer(){

  // Note: increase the lineWidth if 
  // the gradient has noticable gaps 
  ctx.lineWidth=2;

  ctx.strokeStyle='skyblue';

  var sliceCount=0;

  // draw a gradient-stroked line tangent to each point on the curve
  for(var i=0;i<points.length;i++){
    var p=points[i];
    ctx.translate(p.x,p.y);
    ctx.rotate(p.angle-PI/2);
    // draw multiple times to fill gaps on outside of rope slices
    ctx.drawImage(img,sliceCount,0,1,img.height,0,0,1,img.height);
    ctx.drawImage(img,sliceCount,0,1,img.height,0,0,1,img.height);
    ctx.drawImage(img,sliceCount,0,1,img.height,0,0,1,img.height);
    ctx.setTransform(1,0,0,1,0,0);
    ++sliceCount;
    if(sliceCount>(img.width-1)){sliceCount=0;}
  }

}


//////////////////////////////////////////
// helper functions
//////////////////////////////////////////

// calculate one XY point along Cubic Bezier at interval T
// (where T==0.00 at the start of the curve and T==1.00 at the end)
function getCubicBezierXYatT(startPt,controlPt1,controlPt2,endPt,T){
  var x=CubicN(T,startPt.x,controlPt1.x,controlPt2.x,endPt.x);
  var y=CubicN(T,startPt.y,controlPt1.y,controlPt2.y,endPt.y);
  return({x:x,y:y});
}

// cubic helper formula at T distance
function CubicN(T, a,b,c,d) {
  var t2 = T * T;
  var t3 = t2 * T;
  return a + (-a * 3 + T * (3 * a - a * T)) * T
  + (3 * b + T * (-6 * b + b * 3 * T)) * T
  + (c * 3 - c * 3 * T) * t2
  + d * t3;
}

// calculate the tangent angle at interval T on the curve
function bezierTangent(a, b, c, d, t) {
  return (3 * t * t * (-a + 3 * b - 3 * c + d) + 6 * t * (a - 2 * b + c) + 3 * (-a + b));
};
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<h4>Source horizontal rope image</h4>
<img src='https://dl.dropboxusercontent.com/u/139992952/multple/rope.png'>
<h4>After slicing rope and drawing it along curve</h4>
<canvas id="canvas" width=300 height=300></canvas>
Community
  • 1
  • 1
markE
  • 102,905
  • 11
  • 164
  • 176
  • 1
    Thanks to your ideas, i made an other version (a chain) http://jsfiddle.net/ngonam22/s0fec5r0/3/ i will definitely take a look of your edited solution, again thank you so much!!! – nam ngo Sep 03 '15 at 07:55