0

Question Background: I am working on a site that, along with other data, draws vertical and horizontal lines on an HTML canvas.  That page can be converted to a PDF file when the user wants to download the report.  Originally we were using one default style line to draw the graph values.  Recently we added a second type of data on the graph and used context.setLineDash([x,x]) to draw dashed lines for the second data type.  This works great in browsers.  However, when the PDF converter software tries to convert a report with dashed lines, the dashed lines do not show up in the resulting PDF.

After some troubleshooting, I narrowed the problem down to the setLineDash() property.  It appears our converter sofware can understand normal style lines but does not understand the setLineDash() property.  The converter software is several years old and I have been informed that an updated version of the converter will not be bought.  I also discovered that there is no support for our version from the creator.

Question:  Since I am unable to update our HTML to PDF converter software or get support for it directly, can anyone provide an example of an alternative way to draw a dashed line on a canvas without using setLineDash()?

EDIT

@K3N,

As per the instructions on the notification I received when you marked this question a duplicate of this other question, I am editing to explain how it is different.

I believe that though the answers to both questions will likely be similar, my question is not a duplicate of the question you indicated.  I concede that both questions are asking for a way to draw dashed lines on a canvas.  However, the other question is asking how to implement a dashed line by any method.  My question specifically states that I cannot use the setLineDash() property to draw a dashed line.  This difference limits the possible answers and I believe it is enough to make both questions sufficiently distinct.

Ben Bloodworth
  • 900
  • 11
  • 26
  • 1
    So what's wrong with [this](https://stackoverflow.com/a/4577326), [this](https://stackoverflow.com/a/4663129) or [this](https://stackoverflow.com/a/7210370) answer to the linked question? Those are three out of the current top four answers, and none of them use `setLineDash()`. It seems to me that your question is essentially a specific case of the more general question it's marked as a duplicate of, and it happens to be a case that is well covered by the existing answers. – Ilmari Karonen Sep 20 '17 at 20:29
  • @Ilmari Karonen, a fair point. I was interpreting the word _duplicate_ using its dictionary definition. I was under the impression that duplicate questions were exact copies of other questions (like if someone were to copy and paste someone else's question text.) I will try to keep in mind for future questions that it is apparently more of a fluid "duplicate" than an exact match. – Ben Bloodworth Sep 20 '17 at 22:04

2 Answers2

4

You can create line segments.

The function will draw a dashed line from the info in the dashArr eg [2,2] will draw a line 2 pixels then a gap 2 pixels and repeat.

function dashedLine(x1,y1,x2,y2,dashArr){
    // get the normalised line vector from start to end
    var nx = x2 - x1;
    var ny = y2 - y1;
    const dist = Math.sqrt(nx * nx + ny * ny);  // get the line length
    nx /= dist;
    ny /= dist;
    var dashIdx = 0;  // the index into the dash array
    var i = 0;        // the current line position in pixels
    ctx.beginPath();  // start a path
    while(i < dist){   // do while less than line length
         // get the line seg dash length
         var dashLen = dashArr[(dashIdx ++) % dashArr.length];
         // draw the dash
         ctx.moveTo(x1 + nx * i, y1 + ny * i);
         i = Math.min(dist,i + dashLen);
         ctx.lineTo(x1 + nx * i, y1 + ny * i);
         // add the spacing
         i += dashArr[(dashIdx ++) % dashArr.length];
         if(i <= 0) { // something is wrong so exit rather than endless loop
              break;
         }
     }
     ctx.stroke();  // stroke
}

    function dashedLine(x1,y1,x2,y2,dashArr){

        var nx = x2 - x1;
        var ny = y2 - y1;
        const dist = Math.sqrt(nx * nx + ny * ny);
        nx /= dist;
        ny /= dist;
        var dashIdx = 0;
        var i = 0;
        ctx.beginPath();
        while(i < dist){
             var dashLen = dashArr[(dashIdx ++) % dashArr.length];
             ctx.moveTo(x1 + nx * i, y1 + ny * i);
             i = Math.min(dist,i + dashLen);
             ctx.lineTo(x1 + nx * i, y1 + ny * i);
             i += dashArr[(dashIdx ++) % dashArr.length];
             if(i <= 0) { // something is wrong so exit rather than endless loop
                  break;
             }
         }
         ctx.stroke()
    }
    
    
const ctx = canvas.getContext("2d");
dashedLine(0,0,300,150,[5,5]);
canvas { border : 2px solid black; }
<canvas id="canvas"></canvas>
Blindman67
  • 51,134
  • 11
  • 73
  • 136
  • 1
    If OP is only talking about straight `lineTo` this is good, but I fear he should have used the word "path". – Kaiido Sep 19 '17 at 23:02
  • @Kaiido The OP's question is " can anyone provide an example of an alternative way to draw a dashed line on a canvas " and if he wants to draw a path, then repeat the line using distance traveled at the end of one to start the next. – Blindman67 Sep 20 '17 at 07:10
  • Yes, that's what he said, and I only said that I hoped it's what he really said because otherwise no, when I use the word *"path"* it's explicitly to include other path methods, and I don't think it's as easy as *"repeat the line using distance traveled at the end of one to start the next"*. or can you prove me wrong with [this simple path](https://jsfiddle.net/rs4q6yqj/)? That's all I said, and I did upvote because yes, as it currently stands, and if we take the words for what they are, then your answer is *"good"*. But if he used *"line"* because he had *set**Line**Dash* in his brain... – Kaiido Sep 20 '17 at 07:18
  • @Kaiido For a path the answer is it can not be done as you can not access the path data without rewriting all context2D functions that affect or create a path, arc, ellipse, lineTo, moveTo, rect, strokeRect, strokeText, quadraticCurveTo, bezierCurveTo, scale, translate, transform, setTransform, stroke(Path2D), save, restore, did I miss any. It would be easier to modify the failing PDF writer, or forget the dash and use colour. The line answer provides a simple quick solution for paths created from line segments, and looking at the dup answer (65+ for bad buggy code), this is much more robust. – Blindman67 Sep 20 '17 at 11:44
  • @kaiido, thanks for pointing out the ambiguity regarding line type in my original question. I've edited that portion of my question to be more specific regarding the lines. – Ben Bloodworth Sep 20 '17 at 17:03
0

I was also facing a similar problem and I used a different approach to solve this problem. I am posting it in case someone else is having the similar problem.

You can set the stroke pattern on canvas context. Stroke pattern can be any canvas pattern. So here I created an image of 1 pixel height and 6 pixel width. First three pixels were black and other three were white. Now I created the image to create a repeating pattern.

var linePattern;
imageToUsedAsPattern.onload = function() {
    linePattern = context.createPattern(imageToUsedAsPattern, "repeat");
    context.strokeStyle=linePattern; 
}
var imageToUsedAsPattern = new Image();
imageToUsedAsPattern.src = "images/linePatterns.jpg";

Now all the calls to context.stroke will use the pattern to draw strokes. Like if you create a line from the top left corner of the canvas to the bottom right corner it will be a dashed line.

context.moveTo(0,0);
context.lineTo(canvas.width,canvas.height);
context.stroke();

See the full explanation at the following link https://shamailamahmood.blogspot.com/2019/02/html5-canvas-drawing-draw-dotted-or.html

Shamaila Tahir
  • 988
  • 2
  • 8
  • 17