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