Advertisement
Alhadis

HTML5 Canvas textArea function

Feb 2nd, 2014
267
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /** Returns the context's current font style as readable properties. */
  2. CanvasRenderingContext2D.prototype.getFontStyle =   function(){
  3.     var e   =   document.createElement("div");
  4.     e.style.cssText =   "font: "+this.font+" !important;";
  5.  
  6.     this.canvas.appendChild(e);
  7.     var s   =   window.getComputedStyle(e),
  8.         o   =   {
  9.             fontFamily:     s.fontFamily,
  10.             fontSize:       s.fontSize,
  11.             fontStyle:      s.fontStyle,
  12.             fontVariant:    s.fontVariant,
  13.             fontWeight:     s.fontWeight,
  14.             lineHeight:     s.lineHeight
  15.         };
  16.     this.canvas.removeChild(e);
  17.     return o;
  18. };
  19.  
  20.  
  21.  
  22. /**
  23.  *  Draws a rectangular region of text.
  24.  *
  25.  *  @param {string|array} text Textual content, expressed as either a string or an array of substrings representing each word.
  26.  *  @param {number} x coordinate of the textarea.
  27.  *  @param {number} y coordinate of the textarea.
  28.  *  @param {number} w Textarea's width
  29.  *  @param {number} h Textarea's height
  30.  *  @param {number} leading Multiplier adjusting the textarea's line height. 1.5 is approximately equal to 1.5em, etc. Default: 1
  31.  *  @param {number} indent Leading indentation to be applied to the first line. Defaults to 0.
  32.  *
  33.  *  @return {object} A hash containing the following properties:
  34.  *      x, y:       Coordinates that the textarea last finished drawing text. (relative to the canvas object)
  35.  *      remainder:  An array of remaining words that couldn't fit within the designated textarea (if any).
  36.  **/
  37. CanvasRenderingContext2D.prototype.textArea =   function(text, x, y, w, h, leading, indent){
  38.     var words       =   text instanceof Array ? text : text.split(/\b(?=\S)|(?=\s)/g),
  39.         font        =   this.getFontStyle(),
  40.         baseline    =   this.textBaseline,
  41.         rowLength   =   (indent || 0),
  42.         totalHeight =   0,
  43.         fontSize    =   parseInt(font.fontSize),
  44.         leading     =   (leading || 1) * fontSize,
  45.         m, i, diff, s, breakPoint, w_l, w_r;
  46.  
  47.     this.textBaseline   =   "top";
  48.     for(i = 0; i < words.length; ++i){
  49.  
  50.         /** Newline: don't bother measuring, just increase the total height. */
  51.         if("\n" === words[i]){
  52.             rowLength   =   0;
  53.             totalHeight +=  leading;
  54.  
  55.             /** If the newline's pushed the rest of the text outside the drawing area, abort. */
  56.             if(totalHeight + leading >= h) return {
  57.                 x:          rowLength + x,
  58.                 y:          totalHeight + y,
  59.                 remainder:  words.slice(i)
  60.             }
  61.             continue;
  62.         }
  63.  
  64.  
  65.         /** Strip any leading tabs. */
  66.         if(!rowLength && /^\t+/.test(words[i]))
  67.             words[i]    =   words[i].replace(/^\t+/, "");
  68.  
  69.  
  70.         m   =   this.measureText(words[i]).width;
  71.  
  72.         /** This is one honkin' long word, so try and hyphenate it. */
  73.         if((diff = w - m) <= 0){
  74.             diff            =   Math.abs(diff);
  75.  
  76.             /** Figure out which end of the word to start measuring from. Saves a few extra cycles in an already heavy-duty function. */
  77.             if(diff - w <= 0)   for(s = words[i].length; s; --s){
  78.                 if(w > this.measureText(words[i].substr(0, s) + "-").width + fontSize){
  79.                     breakPoint  =   s;  break;
  80.                 }
  81.             }
  82.  
  83.             else for(s = 0; s < words[i].length; ++s){
  84.                 if(w < this.measureText(words[i].substr(0, s+1) + "-").width + fontSize){
  85.                     breakPoint  =   s;  break;
  86.                 }
  87.             }
  88.  
  89.             if(breakPoint){
  90.                 var w_l =   words[i].substr(0, s+1) + "-",
  91.                     w_r =   words[i].substr(s+1);
  92.  
  93.                 words[i]    =   w_l;
  94.                 words.splice(i+1, 0, w_r);
  95.                 m   =   this.measureText(w_l).width;
  96.             }
  97.         }
  98.  
  99.  
  100.         /** If there's no more room on the current line to fit the next word, start a new line. */
  101.         if(rowLength > 0 && rowLength + m >= w){
  102.  
  103.             /** We've run out of room. Return an array of the remaining words we couldn't fit. */
  104.             if(totalHeight + leading*2 >= h) return {
  105.                 x:          rowLength + x,
  106.                 y:          totalHeight + y,
  107.                 remainder:  words.slice(i)
  108.             };
  109.  
  110.             rowLength   =   0;
  111.             totalHeight +=  leading;
  112.  
  113.             /** If the current word is just a space, don't bother. Skip (saves a weird-looking gap in the text) */
  114.             if(" " === words[i]) continue;
  115.         }
  116.  
  117.  
  118.         /** Write another word and increase the total length of the current line. */
  119.         this.fillText(words[i], rowLength+x, totalHeight+y);
  120.         rowLength += m;
  121.     }
  122.  
  123.     /** Restore the context's text baseline property */
  124.     this.textBaseline   =   baseline;
  125.     return {x: rowLength+x, y: totalHeight+y, remainder:[]};
  126. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement