Believer
Believer

Reputation: 182

How to create line in that is user defined

I have this code:

sample.beginPath();
sample.moveTo(X1.x,Y1.x );
sample.lineTo(X2.x,Y2.y);
sample.stroke();

sample.beginPath();
sample.arc(X1.x, Y1.y, 4, 0, 2 * Math.PI, false);
sample.fill();
sample.lineWidth = 1;
sample.stroke();

sample.beginPath();
sample.arc(X2.x, Y2.y, 4, 0, 2 * Math.PI, false);
sample.fill();
sample.lineWidth = 1;
sample.stroke();

which will create this:

enter image description here

What I want is to have this:

enter image description here

Please assume that it is straight line and the circles are correctly drawn.

Note: Line is still connected in the infinity line

Upvotes: 3

Views: 99

Answers (3)

Blindman67
Blindman67

Reputation: 54026

Using canvas context render calls can be slow if you are doing them many times for complex shapes. One solution is to draw once and then copy via the drawImage() method.

Below is an rough idea of how it's done. The object PatternedLine will draw a line from a image/canvas/svg by repeating the bitmap along the line. You can provide a image, or a pattern callback function to define the pattern.

The pattern can be anything, maybe text, an image of wire, or railway track.

It is much faster than using canvas 2d context render calls. Easy to adapt to animating the pattern, scaling the, and much more. Even play a video as your line pattern if you wish.

// PatternedLine
// width,height are width and height of the repeating pattern
// width,height are optional if you  provide an image
// pattern is the function to create the pattern or an image/svg/canvas
// pattern function callback has 3 arguments, ctx,w,h
// ctx is the context to draw to and w and h are the width and height
function PatternedLine(pattern,width,height){
    var canvas,w,h;
    if(typeof pattern !== "function"){ // if not a function than its an image
        canvas = pattern;  // set the pattern
        w = canvas.width;  // get the width and height
        h = canvas.height;
    }else{
        canvas = document.createElement("canvas"); // create a new canvas 
        canvas.width = width;  // set the width and height
        canvas.height = height;
        w = width;
        h = height;
        var ctx = canvas.getContext("2d"); // create the context
        pattern(ctx, w, h);  // draw the pattern
    }
    // draw the pattern on targetCtx
    // line starting at x1,y1 ending at x2,y2
    this.drawLine = function(targetCtx, x1, y1, x2, y2){
        // get the length of the line
        var dist = Math.sqrt(Math.pow(x1 - x2, 2)+Math.pow(y1 - y2, 2));  
        if(dist > 0){  // if it has length
            // get the direction of the line
            var ang = Math.acos((x2 - x1) / dist);
            if (y2 - y1 > 0) ang = - ang;
            // set the transform to the start of the line
            targetCtx.setTransform(1, 0, 0, 1, x1, y1);
            targetCtx.rotate(-ang);    // rotate along the line
            x1 = 0;  // set start of line segments
            while(x1 <= dist-w){ // draw all segments but the last
                targetCtx.drawImage(canvas,x1, -h/2, w, h ); // draw the segment
                x1 += w;  // step to the next segment
            }
            // check if the tail segment needs drawing
            if(x1 < dist){
                var lw = dist-x1;  // get the length of the last segment
                // draw it
                targetCtx.drawImage(canvas,0,0,lw,h,x1, -h/2, lw, h ); 
            }
            // revert the transform
            // you could use restore and save at the start
            targetCtx.setTransform(1,0,0,1,0,0);
        }
    }
}

// function to create the pattern
function pattern(ctx,w,h){
    ctx.strokeStyle = "black";
    ctx.lineWidth = 3;
    ctx.setTransform(1,0,0,1,0,h/2);
    ctx.beginPath();
    ctx.moveTo(0,0);
    ctx.lineTo(w,0);
    ctx.stroke();
    ctx.fillStyle = "white"
    ctx.beginPath();
    ctx.arc(w/2,0,10,0,Math.PI*2);
    ctx.fill();
    ctx.stroke();
}

// how to use
var line = new PatternedLine(pattern,100,25); // create a new pattern
var px = Math.random()*400+50;  // get some points
var py = Math.random()*400+50;
var px1 = Math.random()*400+50;
var py1 = Math.random()*400+50;
line.drawLine(ctx,px,py,px1,py1); // draw the patterned line

Upvotes: 0

Kaiido
Kaiido

Reputation: 136638

Instead of a while loop, you can use Math to do it :

Using this answer's algo, we can do

var sample = canvas.getContext("2d");

function drawLine(x1, y1, x2, y2) {

  var segLength = Math.sqrt(Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2)),
    startDist = segLength * -2,
    endDist = Math.sqrt(Math.pow((x2 - canvas.width), 2) + Math.pow((y2 - canvas.height), 2));

  var rX1 = x2 + (x2 - x1) / segLength * startDist;
  var rY1 = y2 + (y2 - y1) / segLength * startDist;

  var rX2 = x2 + (x2 - x1) / segLength * endDist;
  var rY2 = y2 + (y2 - y1) / segLength * endDist;

  sample.strokeStyle = '#000000';
  sample.beginPath();
  sample.moveTo(rX1, rY1);
  sample.lineTo(rX2, rY2);
  sample.lineWidth = 2;
  sample.stroke();

  sample.beginPath();
  sample.arc(x1, y1, 4, 0, 2 * Math.PI, false);
  sample.fillStyle = "#FFFFFF";
  sample.fill();
  sample.lineWidth = 1;
  sample.stroke();

  sample.beginPath();
  sample.arc(x2, y2, 4, 0, 2 * Math.PI, false);
  sample.fillStyle = "#FFFFFF";
  sample.fill();
  sample.lineWidth = 1;
  sample.stroke();
}



drawLine(50, 100, 110, 105);
canvas{border:1px solid;}
<canvas id="canvas" width="400" height="200"></canvas>

Upvotes: 1

Maximillian Laumeister
Maximillian Laumeister

Reputation: 20359

Basically your code just needed to be run in two loops - one that draws copies of the line segment in the forwards direction, and one that draws copies of the line segment in the backwards direction.

This modified version draws an infinite line by drawing forwards and backwards until it hits the edges of the canvas.

Here is a screenshot of the actual output:

screenshot

And here is a live demo of the final working solution:

var canvas = document.getElementById("thecanvas");
var sample = canvas.getContext("2d");

function drawLine(x1, y1, x2, y2) {
    sample.strokeStyle = '#000000';
    
    sample.beginPath();
    sample.moveTo(x1, y1);
    sample.lineTo(x2, y2);
    sample.lineWidth = 2;
    sample.stroke();

    sample.beginPath();
    sample.arc(x1, y1, 4, 0, 2 * Math.PI, false);
    sample.fillStyle = "#FFFFFF";
    sample.fill();
    sample.lineWidth = 1;
    sample.stroke();
}

function drawInfLine(x1, y1, x2, y2) {
    var xstep = x2 - x1;
    var ystep = y2 - y1;
    
    var lastx = x1;
    var lasty = x2;
    var currx;
    var curry; // yum
    
    // Draw forwards
    while (lastx <= canvas.width && lasty <= canvas.height) {
        currx = lastx + xstep;
        curry = lasty + ystep;
        drawLine(lastx, lasty, currx, curry);
        lastx = currx;
        lasty = curry;
    }
    
    // Reset initial drawing point
    lastx = x1;
    lasty = x2;
    
    // Draw backwards
    while (lastx >= 0 && lasty >= 0) {
        currx = lastx - xstep;
        curry = lasty - ystep;
        drawLine(lastx, lasty, currx, curry);
        lastx = currx;
        lasty = curry;
    }
}

drawInfLine(50, 0, 110, 5);
<canvas id="thecanvas" width="400" height="200"></canvas>

JSFiddle Version: https://jsfiddle.net/k83153br/2/

Upvotes: 2

Related Questions