Ext提供了一个很有用的工具类Ext.util.TextMetrics,利用该类可以很方便的为一段文字提供一个精确象素级的测量,以便可以得到某段文字的高度和宽度。该类的实现采用了单例模式,即当调用该类时,该类内部属性shared已实例,下次调用时不需再实例化。先看函数的定义
Ext.util.TextMetrics = function(){ var shared; return { measure : function(el, text, fixedWidth){ if(!shared){ shared = Ext.util.TextMetrics.Instance(el, fixedWidth); } shared.bind(el); shared.setFixedWidth(fixedWidth || 'auto'); return shared.getSize(text); }, createInstance : function(el, fixedWidth){ return Ext.util.TextMetrics.Instance(el, fixedWidth); } }; }();
该函数是自执行函数,执行完后该类提供了两个方法measure和createInstance,分别用来测量元素el的大小和创建实例。
方法measure中三个参数为el测试的DOM对象,text要测量的文字,fixedWidth设置该值可以根据该值压缩文字的多行显示,从而根据行数的不同返回文本的高度,不设置时会默认根据文本显示的行数返回内容的高度。该方法首先实例化要测量的对象,然后绑定el,绑定的过程会设置其与字体相关的css样式,最后返回一段文字的大小。
上面方法中调用的函数实际上在Ext.util.TextMetrics.Instance中定义,函数主体如下
Ext.util.TextMetrics.Instance = function(bindTo, fixedWidth){ ... var instance = { ... }; instance.bind(bindTo); return instance; };
该函数实际上返回闭包变量instance,Ext.util.TextMetrics.Instance中封装了要实现的函数,首先看Instance中的变量
var ml = new Ext.Element(document.createElement('div')); document.body.appendChild(ml.dom); ml.position('absolute'); ml.setLeftTop(-1000, -1000); ml.hide(); if(fixedWidth){ ml.setWidth(fixedWidth); }
该类定义了辅助div,隐藏显示,用来存放要测试的文字,下面看闭包变量instance中的函数
/** * 返回一个指定文字的尺寸。该文字内置元素的样式和宽度属性 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p> * Returns the size of the specified text based on the internal element's style and width properties * @param {String} text 要测量的文字 The text to measure * @return {Object} An object containing the text's size {width: (width), height: (height)} */ getSize : function(text){ ml.update(text); var s = ml.getSize(); ml.update(''); return s; },
注意返回大小后把ml内容清空
/** * 绑定某个样式的TextMetrics实例,使得被渲染之文字重新获得CSS样式。 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p> * Binds this TextMetrics instance to an element from which to copy existing CSS styles * that can affect the size of the rendered text * @param {String/HTMLElement} el The element, dom node or id */ bind : function(el){ ml.setStyle( Ext.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing') ); },
绑定时只设置与字体相关的属性
/** * 对内置的测量元素设置一个固定的宽度。 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p> * Sets a fixed width on the internal measurement element. If the text will be multiline, you have * to set a fixed width in order to accurately measure the text height. * @param {Number} width The width to set on the element */ setFixedWidth : function(width){ ml.setWidth(width); }, /** * 返回指定文字的宽度 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p> * Returns the measured width of the specified text * @param {String} text The text to measure * @return {Number} width The width in pixels */ getWidth : function(text){ ml.dom.style.width = 'auto'; return this.getSize(text).width; }, /** * 返回指定文字的高度,对于多行文本,有可能需要调用 {@link #setFixedWidth} 。 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p> * Returns the measured height of the specified text. For multiline text, be sure to call * {@link #setFixedWidth} if necessary. * @param {String} text The text to measure * @return {Number} height The height in pixels */ getHeight : function(text){ return this.getSize(text).height; }
最后为对象Ext.Element提供了一个测量文本宽度的方法
Ext.Element.addMethods({ /** * 返回传入文本的宽度,或者该元素下文本的宽度。用该方法即可 * Returns the width in pixels of the passed text, or the width of the text in this Element. * @param {String} 要测试的内容 text The text to measure. Defaults to the innerHTML of the element. * @param {Number} min (Optional) The minumum value to return. * @param {Number} max (Optional) The maximum value to return. * @return {Number} The text width in pixels. * @member Ext.Element getTextWidth */ getTextWidth : function(text, min, max){ return (Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width).constrain(min || 0, max || 1000000); } });
下面综合看一个例子
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Ext.util.TextMetrics测试</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css"
href="../ext-3.3.1/resources/css/ext-all.css" />
<script type="text/javascript"
src="../ext-3.3.1/adapter/ext/ext-base-debug.js"></script>
<script type="text/javascript"
src="../ext-3.3.1/ext-all-debug-w-comments.js"></script>
<script type="text/javascript"
src="../ext-3.3.1/src/locale/ext-lang-zh_CN.js"></script>
<script type="text/javascript" src="../ext-3.3.1/src/debug.js"></script>
<script type="text/javascript">
Ext.onReady(function() {
Ext.BLANK_IMAGE_URL = '../ext-3.3.1/resources/images/default/s.gif';
Ext.QuickTips.init();
var textEl = Ext.fly('text');
var textWidth = textEl.getTextWidth();
alert(textWidth);
textEl = Ext.fly('text2');
var size = Ext.util.TextMetrics.measure(textEl, textEl.dom.innerHTML);
alert('width:'+size.width+' height:'+size.height);
//多行的测试,设置宽度时会压缩内容,再根据压缩后的行数返回其高度
textEl = Ext.fly('text3');
var size = Ext.util.TextMetrics.measure(textEl, textEl.dom.innerHTML,50);
alert('多行的测试 width:'+size.width+' height:'+size.height);
//返回一个唯一的TextMetrics实例,直接绑定到某个元素和复用,
//这样会减少在每个测量上初始样式属性的多次调用。
var metrics=Ext.util.TextMetrics.createInstance('text3');
metrics.setFixedWidth(100);
var size=metrics.getSize("中华人民共和国中华人民共和国"
+"中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国"
+"中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国");
Ext.MessageBox.alert("getsize",String.format("width:{0}px\theight:{1}px",size.width,size.height))
});
</script>
</head>
<body>
<div id='text'>
要测试的文本要测试的文本要测试的文本
</div>
<div id='text2'>
要测试的文本要测试的文本要测试的文本
</div>
<div id='text3'>
要测试的文本要测试的文本要测试的文本
要测试的文本要测试的文本要测试的文本
要测试的文本要测试的文本要测试的文本
</div>
</body>
</html>
工具类Ext.util.TextMetrics到此分析完毕。