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类提供简单的文字显示功能。复杂的文本功能可以使用DOMElement。
 11  * @augments View
 12  * @param {Object} properties 创建对象的属性参数。可包含此类所有可写属性。
 13  * @module hilo/view/Text
 14  * @requires hilo/core/Class
 15  * @requires hilo/core/Hilo
 16  * @requires hilo/view/View
 17  * @requires hilo/view/CacheMixin
 18  * @property {String} text 指定要显示的文本内容。
 19  * @property {String} color 指定使用的字体颜色。
 20  * @property {String} textAlign 指定文本的对齐方式。可以是以下任意一个值:'start', 'end', 'left', 'right', and 'center'。
 21  * @property {String} textVAlign 指定文本的垂直对齐方式。可以是以下任意一个值:'top', 'middle', 'bottom'。
 22  * @property {Boolean} outline 指定文本是绘制边框还是填充。
 23  * @property {Number} lineSpacing 指定文本的行距。单位为像素。默认值为0。
 24  * @property {Number} maxWidth 指定文本的最大宽度。默认值为200。
 25  * @property {String} font 文本的字体CSS样式。只读属性。设置字体样式请用setFont方法。
 26  * @property {Number} textWidth 指示文本内容的宽度,只读属性。仅在canvas模式下有效。
 27  * @property {Number} textHeight 指示文本内容的高度,只读属性。仅在canvas模式下有效。
 28  */
 29 var Text = Class.create(/** @lends Text.prototype */{
 30     Extends: View,
 31     Mixes:CacheMixin,
 32     constructor: function(properties){
 33         properties = properties || {};
 34         this.id = this.id || properties.id || Hilo.getUid('Text');
 35         Text.superclass.constructor.call(this, properties);
 36 
 37         // if(!properties.width) this.width = 200; //default width
 38         if(!properties.font) this.font = '12px arial'; //default font style
 39         this._fontHeight = Text.measureFontHeight(this.font);
 40     },
 41 
 42     text: null,
 43     color: '#000',
 44     textAlign: null,
 45     textVAlign: null,
 46     outline: false,
 47     lineSpacing: 0,
 48     maxWidth: 200,
 49     font: null, //ready-only
 50     textWidth: 0, //read-only
 51     textHeight: 0, //read-only
 52 
 53     /**
 54      * 设置文本的字体CSS样式。
 55      * @param {String} font 要设置的字体CSS样式。
 56      * @returns {Text} Text对象本身。链式调用支持。
 57      */
 58     setFont: function(font){
 59         var me = this;
 60         if(me.font !== font){
 61             me.font = font;
 62             me._fontHeight = Text.measureFontHeight(font);
 63         }
 64 
 65         return me;
 66     },
 67 
 68     /**
 69      * 覆盖渲染方法。
 70      * @private
 71      */
 72     render: function(renderer, delta){
 73         var me = this, canvas = renderer.canvas;
 74 
 75         if(renderer.renderType === 'canvas'){
 76             me._draw(renderer.context);
 77         }
 78         else if(renderer.renderType === 'dom'){
 79             var drawable = me.drawable;
 80             var domElement = drawable.domElement;
 81             var style = domElement.style;
 82 
 83             style.font = me.font;
 84             style.textAlign = me.textAlign;
 85             style.color = me.color;
 86             style.width = me.width + 'px';
 87             style.height = me.height + 'px';
 88             style.lineHeight = (me._fontHeight + me.lineSpacing) + 'px';
 89 
 90             domElement.innerHTML = me.text;
 91             renderer.draw(this);
 92         }
 93         else{
 94             //TODO:自动更新cache
 95             me.cache();
 96             renderer.draw(me);
 97         }
 98     },
 99 
100     /**
101      * 在指定的渲染上下文上绘制文本。
102      * @private
103      */
104     _draw: function(context){
105         var me = this, text = me.text.toString();
106         if(!text) return;
107 
108         //set drawing style
109         context.font = me.font;
110         context.textAlign = me.textAlign;
111         context.textBaseline = 'top';
112 
113         //find and draw all explicit lines
114         var lines = text.split(/\r\n|\r|\n|<br(?:[ \/])*>/);
115         var width = 0, height = 0;
116         var lineHeight = me._fontHeight + me.lineSpacing;
117         var i, line, w;
118         var drawLines = [];
119 
120         for(i = 0, len = lines.length; i < len; i++){
121             line = lines[i];
122             w = context.measureText(line).width;
123 
124             //check if the line need to split
125             if(w <= me.maxWidth){
126                 drawLines.push({text:line, y:height});
127                 // me._drawTextLine(context, line, height);
128                 if(width < w) width = w;
129                 height += lineHeight;
130                 continue;
131             }
132 
133             var str = '', oldWidth = 0, newWidth, j, word;
134 
135             for(j = 0, wlen = line.length; j < wlen; j++){
136                 word = line[j];
137                 newWidth = context.measureText(str + word).width;
138 
139                 if(newWidth > me.maxWidth){
140                     drawLines.push({text:str, y:height});
141                     // me._drawTextLine(context, str, height);
142                     if(width < oldWidth) width = oldWidth;
143                     height += lineHeight;
144                     str = word;
145                 }else{
146                     oldWidth = newWidth;
147                     str += word;
148                 }
149 
150                 if(j == wlen - 1){
151                     drawLines.push({text:str, y:height});
152                     // me._drawTextLine(context, str, height);
153                     if(str !== word && width < newWidth) width = newWidth;
154                     height += lineHeight;
155                 }
156             }
157         }
158 
159         me.textWidth = width;
160         me.textHeight = height;
161         if(!me.width) me.width = width;
162         if(!me.height) me.height = height;
163 
164         //vertical alignment
165         var startY = 0;
166         switch(me.textVAlign){
167             case 'middle':
168                 startY = me.height - me.textHeight >> 1;
169                 break;
170             case 'bottom':
171                 startY = me.height - me.textHeight;
172                 break;
173         }
174 
175         //draw background
176         var bg = me.background;
177         if(bg){
178             context.fillStyle = bg;
179             context.fillRect(0, 0, me.width, me.height);
180         }
181 
182         if(me.outline) context.strokeStyle = me.color;
183         else context.fillStyle = me.color;
184 
185         //draw text lines
186         for(var i = 0; i < drawLines.length; i++){
187             var line = drawLines[i];
188             me._drawTextLine(context, line.text, startY + line.y);
189         }
190     },
191 
192     /**
193      * 在指定的渲染上下文上绘制一行文本。
194      * @private
195      */
196     _drawTextLine: function(context, text, y){
197         var me = this, x = 0, width = me.width;
198 
199         switch(me.textAlign){
200             case 'center':
201                 x = width >> 1;
202                 break;
203             case 'right':
204             case 'end':
205                 x = width;
206                 break;
207         };
208 
209         if(me.outline) context.strokeText(text, x, y);
210         else context.fillText(text, x, y);
211     },
212 
213     Statics: /** @lends Text */{
214         /**
215          * 测算指定字体样式的行高。
216          * @param {String} font 指定要测算的字体样式。
217          * @return {Number} 返回指定字体的行高。
218          */
219         measureFontHeight: function(font){
220             var docElement = document.documentElement, fontHeight;
221             var elem = Hilo.createElement('div', {style:{font:font, position:'absolute'}, innerHTML:'M'});
222 
223             docElement.appendChild(elem);
224             fontHeight = elem.offsetHeight;
225             docElement.removeChild(elem);
226             return fontHeight;
227         }
228     }
229 
230 });