      1 /**
      2  * Hilo
      3  * Copyright 2015 alibaba.com
      4  * Licensed under the MIT License
      5  */
      6
      7 /**
      8  * <iframe src='../../../examples/Text.html?noHeader' width = '320' height = '240' scrolling='no'></iframe>
      9  * <br/>
     10  * @class Text class provide basic text-display function, use DOMElement for complex text-display.
     11  * @augments View
     12  * @mixes CacheMixin
     13  * @borrows CacheMixin#cache as #cache
     14  * @borrows CacheMixin#updateCache as #updateCache
     15  * @borrows CacheMixin#setCacheDirty as #setCacheDirty
     16  * @param {Object} properties Properties parameters for the object. Includes all writable properties.
     17  * @module hilo/view/Text
     18  * @requires hilo/core/Class
     19  * @requires hilo/core/Hilo
     20  * @requires hilo/view/View
     21  * @requires hilo/view/CacheMixin
     22  * @property {String} text Text to display.
     23  * @property {String} color Color of the text.
     24  * @property {String} textAlign Horizontal alignment way of the text. May be one of the following value:'start', 'end', 'left', 'right', and 'center'. Note:Need to specify the width property of the text to take effect
     25  * @property {String} textVAlign Vertical alignment way of the text. May be one of the following value:'top', 'middle', 'bottom'. Note:Need to specify the height property of the text to take effect.
     26  * @property {Boolean} outline Draw the outline of the text or fill the text.
     27  * @property {Number} lineSpacing The spacing between lines. Measured in px, default value is 0.
     28  * @property {Number} maxWidth The max length of the text, default value is 200.
     29  * @property {String} font Text's CSS font style, readonly! Use setFont function to set text font.
     30  * @property {Number} textWidth Width of the text, readonly! Works only on canvas mode.
     31  * @property {Number} textHeight Height of the text, readonly! Works only on canvas mode.
     32  */
     33 var Text = Class.create(/** @lends Text.prototype */{
     34     Extends: View,
     35     Mixes:CacheMixin,
     36     constructor: function(properties){
     37         properties = properties || {};
     38         this.id = this.id || properties.id || Hilo.getUid('Text');
     39         Text.superclass.constructor.call(this, properties);
     40
     41         // if(!properties.width) this.width = 200; //default width
     42         if(!properties.font) this.font = '12px arial'; //default font style
     43         this._fontHeight = Text.measureFontHeight(this.font);
     44     },
     45
     46     text: '',
     47     color: '#000',
     48     textAlign: null,
     49     textVAlign: null,
     50     outline: false,
     51     lineSpacing: 0,
     52     maxWidth: 200,
     53     font: null, //ready-only
     54     textWidth: 0, //read-only
     55     textHeight: 0, //read-only
     56
     57     /**
     58      * Set text CSS font style.
     59      * @param {String} font Text CSS font style to set.
     60      * @returns {Text} the Text object, chained call supported.
     61      */
     62     setFont: function(font){
     63         var me = this;
     64         if(me.font !== font){
     65             me.font = font;
     66             me._fontHeight = Text.measureFontHeight(font);
     67         }
     68
     69         return me;
     70     },
     71
     72     /**
     73      * Overwrite render function.
     74      * @private
     75      */
     76     render: function(renderer, delta){
     77         var me = this;
     78
     79         if(renderer.renderType === 'canvas'){
     80             if(this.drawable){
     81                 renderer.draw(me);
     82             }
     83             else{
     84                 me._draw(renderer.context);
     85             }
     86         }
     87         else if(renderer.renderType === 'dom'){
     88             var drawable = me.drawable;
     89             var domElement = drawable.domElement;
     90             var style = domElement.style;
     91
     92             style.font = me.font;
     93             style.textAlign = me.textAlign;
     94             style.color = me.color;
     95             style.width = me.width + 'px';
     96             style.height = me.height + 'px';
     97             style.lineHeight = (me._fontHeight + me.lineSpacing) + 'px';
     98
     99             domElement.innerHTML = me.text;
    100             renderer.draw(this);
    101         }
    102         else{
    103             //TODO:自动更新cache  TODO:auto update cache
    104             me.cache();
    105             renderer.draw(me);
    106         }
    107     },
    108
    109     /**
    110      * Draw text under the assigned render context.
    111      * @private
    112      */
    113     _draw: function(context){
    114         var me = this, text = me.text.toString();
    115         if(!text) return;
    116
    117         //set drawing style
    118         context.font = me.font;
    119         context.textAlign = me.textAlign;
    120         context.textBaseline = 'top';
    121
    122         //find and draw all explicit lines
    123         var lines = text.split(/\r\n|\r|\n|<br(?:[ \/])*>/);
    124         var width = 0, height = 0;
    125         var lineHeight = me._fontHeight + me.lineSpacing;
    126         var i, line, w, len, wlen;
    127         var drawLines = [];
    128
    129         for(i = 0, len = lines.length; i < len; i++){
    130             line = lines[i];
    131             w = context.measureText(line).width;
    132
    133             //check if the line need to split
    134             if(w <= me.maxWidth){
    135                 drawLines.push({text:line, y:height});
    136                 // me._drawTextLine(context, line, height);
    137                 if(width < w) width = w;
    138                 height += lineHeight;
    139                 continue;
    140             }
    141
    142             var str = '', oldWidth = 0, newWidth, j, word;
    143
    144             for(j = 0, wlen = line.length; j < wlen; j++){
    145                 word = line[j];
    146                 newWidth = context.measureText(str + word).width;
    147
    148                 if(newWidth > me.maxWidth){
    149                     drawLines.push({text:str, y:height});
    150                     // me._drawTextLine(context, str, height);
    151                     if(width < oldWidth) width = oldWidth;
    152                     height += lineHeight;
    153                     str = word;
    154                 }else{
    155                     oldWidth = newWidth;
    156                     str += word;
    157                 }
    158
    159                 if(j == wlen - 1){
    160                     drawLines.push({text:str, y:height});
    161                     // me._drawTextLine(context, str, height);
    162                     if(str !== word && width < newWidth) width = newWidth;
    163                     height += lineHeight;
    164                 }
    165             }
    166         }
    167
    168         me.textWidth = width;
    169         me.textHeight = height;
    170         if(!me.width) me.width = width;
    171         if(!me.height) me.height = height;
    172
    173         //vertical alignment
    174         var startY = 0;
    175         switch(me.textVAlign){
    176             case 'middle':
    177                 startY = me.height - me.textHeight >> 1;
    178                 break;
    179             case 'bottom':
    180                 startY = me.height - me.textHeight;
    181                 break;
    182         }
    183
    184         //draw background
    185         var bg = me.background;
    186         if(bg){
    187             context.fillStyle = bg;
    188             context.fillRect(0, 0, me.width, me.height);
    189         }
    190
    191         if(me.outline) context.strokeStyle = me.color;
    192         else context.fillStyle = me.color;
    193
    194         //draw text lines
    195         for(i = 0; i < drawLines.length; i++){
    196             line = drawLines[i];
    197             me._drawTextLine(context, line.text, startY + line.y);
    198         }
    199     },
    200
    201     /**
    202      * Draw a line of text under the assigned render context.
    203      * @private
    204      */
    205     _drawTextLine: function(context, text, y){
    206         var me = this, x = 0, width = me.width;
    207
    208         switch(me.textAlign){
    209             case 'center':
    210                 x = width >> 1;
    211                 break;
    212             case 'right':
    213             case 'end':
    214                 x = width;
    215                 break;
    216         }
    217
    218         if(me.outline) context.strokeText(text, x, y);
    219         else context.fillText(text, x, y);
    220     },
    221
    222     Statics: /** @lends Text */{
    223         /**
    224          * Measure the line height of the assigned text font style.
    225          * @param {String} font Font style to measure.
    226          * @return {Number} Return line height of the assigned font style.
    227          */
    228         measureFontHeight: function(font){
    229             var docElement = document.documentElement, fontHeight;
    230             var elem = Hilo.createElement('div', {style:{font:font, position:'absolute'}, innerHTML:'M'});
    231
    232             docElement.appendChild(elem);
    233             fontHeight = elem.offsetHeight;
    234             docElement.removeChild(elem);
    235             return fontHeight;
    236         }
    237     }
    238
    239 });
    240