├── example.png ├── LICENSE ├── README.md ├── index.html └── ctxtextpath.js /example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Viglino/Canvas-TextPath/HEAD/example.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Jean-Marc Viglino 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Canvas-TextPath](https://github.com/Viglino/Canvas-TextPath/) 2 | 3 | Adds extra functionality to the CanvasRenderingContext2D to draw text along a path. 4 | 5 | ![Text along the path](example.png?raw=true "Result") 6 | 7 | Check out the [demo!](https://viglino.github.io/Canvas-TextPath/) 8 | 9 | ## How it runs? 10 | 11 | As SVG does support text along paths, canvas doesn't have such native support. 12 | This contribution tries to fill the lack and gives you a `textPath` function to use with a CanvasRenderingContext2D. 13 | The method writes one character at a time rotated and scaled according to the path. 14 | 15 | NOTE: this extends the CanvasRenderingContext2D prototype. 16 | - It adds a `textPath` function to draw text along a path (as an Array of coordinates). 17 | - It adds `textOverflow`, `textJustify` and `textStrokeMin` properties to CanvasRenderingContext2D. 18 | 19 | It support native options include text alignment (left, center and right) and baseline positionning. 20 | Stroke and fill is done in one pass for performance purpose. 21 | 22 | 23 | ## Usage 24 | 25 | Include the following files in your page: 26 | ```html 27 | 28 | ``` 29 | Add a canvas on your page 30 | ```html 31 | 32 | ``` 33 | Then begin to draw in the canvas 34 | ```javascript 35 | window.onload = function() { 36 | var c = document.getElementById("myCanvas"); 37 | var ctx = c.getContext("2d"); 38 | 39 | // Draw support path 40 | ctx.strokeStyle = "red"; 41 | ctx.lineWidth=2; 42 | ctx.moveTo(10,60); 43 | ctx.lineTo(100,40); 44 | ctx.lineTo(190,60); 45 | ctx.stroke(); 46 | 47 | // Render text 48 | ctx.font = "24px Arial"; 49 | ctx.textAlign = "center"; 50 | ctx.textBaseline = "middle"; 51 | ctx.strokeStyle = "white"; 52 | ctx.lineWidth = 3; 53 | ctx.textPath ("Hello world", [10,60, 100,40, 190,60]); 54 | } 55 | 56 | ``` 57 | The path is an Array of coordinates [x1,y1, x2, y2, etc.] 58 | 59 | ![Text along the path](example.png?raw=true "Result") 60 | 61 | ## Properties 62 | 63 | Extra properties are added to the CanvasRenderingContext2D. 64 | 65 | * `textOverflow` string: specifies what happens if text overflows the path, default "" (means hidden). Possible values are 'visible' to show the text, 'ellipsis' to show "..." or a string that will be displayed at the end of the truncated text. Use an empy string to hide overflow content. 66 | * `textJustify` boolean: true for justifying text, default false. When false, it takes the textAlign propertie to align text. 67 | * `textStrokeMin` number: the minimum size (in pixel) of the path underneath the text is not displayed. 68 | 69 | If you specify a `lineWitdth` less than 0.1 no stroke is drawn. Use a `fillStyle` as 'transparent' to not fill the text 70 | 71 | ```javascript 72 | ctx.textOverflow = "ellipsis"; 73 | ctx.textJustify = true; 74 | ctx.textStrokeMin = 40; 75 | ctx.textPath ("Hello world", [10,60, 100,40, 190,60]); 76 | ``` 77 | 78 | 79 | ## License 80 | 81 | [MIT License](https://github.com/Viglino/Canvas-TextPath/blob/master/LICENSE) 82 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Canvas textPath 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 20 | 21 | 22 | Fork me on GitHub 23 |

Canvas textPath

24 |

25 | To render text along a path in a Canvas.
26 | It adds extra functionality to the CanvasRenderingContext2D by extending its prototype. 27 |
28 | Text: 29 |

30 |
    31 |
  1. align left
  2. 32 |
  3. align center
  4. 33 |
  5. align right
  6. 34 |
  7. justify
  8. 35 |
  9. overflow hidden
  10. 36 |
  11. overflow ellipsis
  12. 37 |
  13. overflow visible
  14. 38 |
39 | 40 | 41 | 121 | -------------------------------------------------------------------------------- /ctxtextpath.js: -------------------------------------------------------------------------------- 1 | /** Render text along a path in a Canvas 2 | * Adds extra functionality to the CanvasRenderingContext2D by extending its prototype. 3 | * Extent the global object with options: 4 | * - textOverflow {undefined|visible|ellipsis|string} the text to use on overflow, default "" (hidden) 5 | * - textJustify {undefined|boolean} used to justify text (otherwise use textAlign), default false 6 | * - textStrokeMin {undefined|number} the min length (in pixel) for the support path to draw the text upon, default 0 7 | * 8 | * @param {string} text the text to render 9 | * @param {Array} path an array of coordinates as support for the text (ie. [x1,y1,x2,y2,...] 10 | */ 11 | (function() 12 | { 13 | /* Usefull function */ 14 | function dist2D(x1,y1,x2,y2) 15 | { var dx = x2-x1; 16 | var dy = y2-y1; 17 | return Math.sqrt(dx*dx+dy*dy); 18 | } 19 | 20 | /* Add new properties on CanvasRenderingContext2D */ 21 | CanvasRenderingContext2D.prototype.textOverflow = ""; 22 | CanvasRenderingContext2D.prototype.textJustify = false; 23 | CanvasRenderingContext2D.prototype.textStrokeMin = 0; 24 | 25 | var state = []; 26 | var save = CanvasRenderingContext2D.prototype.save; 27 | CanvasRenderingContext2D.prototype.save = function() 28 | { state.push( 29 | { textOverflow: this.textOverflow, 30 | textJustify: this.textJustify, 31 | textStrokeMin: this.textStrokeMin, 32 | }); 33 | save.call(this); 34 | } 35 | 36 | var restore = CanvasRenderingContext2D.prototype.restore; 37 | CanvasRenderingContext2D.prototype.restore = function() 38 | { restore.call(this); 39 | var s = state.pop(); 40 | this.textOverflow = s.textOverflow; 41 | this.textJustify = s.textJustify; 42 | this.textStrokeMin = s.textStrokeMin; 43 | } 44 | 45 | /* textPath function */ 46 | CanvasRenderingContext2D.prototype.textPath = function (text, path) 47 | { // Helper to get a point on the path, starting at dl 48 | // (return x, y and the angle on the path) 49 | var di, dpos=0; 50 | var pos=2; 51 | function pointAt(dl) 52 | { if (!di || dpos+didl) break; 56 | pos += 2; 57 | if (pos>=path.length) break; 58 | dpos += di; 59 | } 60 | } 61 | 62 | var x, y, dt = dl-dpos; 63 | if (pos>=path.length) 64 | { pos = path.length-2; 65 | } 66 | 67 | if (!dt) 68 | { x = path[pos-2]; 69 | y = path[pos-1]; 70 | } 71 | else 72 | { x = path[pos-2]+ (path[pos]-path[pos-2])*dt/di; 73 | y = path[pos-1]+ (path[pos+1]-path[pos-1])*dt/di; 74 | } 75 | return [x, y, Math.atan2(path[pos+1]-path[pos-1], path[pos]-path[pos-2])]; 76 | } 77 | 78 | var letterPadding = this.measureText(" ").width *0.25; 79 | 80 | // Calculate length 81 | var d = 0; 82 | for (var i=2; i0.1) this.strokeText(letter,0,0); 136 | this.fillText(letter,0,0); 137 | this.restore(); 138 | start += wl+letterPadding*(letter==" "?2:1); 139 | } 140 | 141 | }; 142 | 143 | })(); --------------------------------------------------------------------------------