打开以后首先按Ctrl + N,建一个 8像素 X 8像素的图片,背景选择透明,如图所示:
接下来还需要拓宽,方法是一样的,只不过这次的模式变换的方案是把相邻两个白色之间的黑色涂成白色。比如第五次拓展,其模式是这个样子:
《script》 function getBinary(sText){ alert(sText); }; var strTest = "dknt没有任何含义"; getBinary(strTest ); 《script》
提示:您可以先修改部分代码再运行
为了获得二进制表示,我们要一个字符一个字符的进行,不能着急,首先要获得每个字符对应的Unicode编码。
《script》 function getBinary(sText){ for(var i = 0; i < sText.length; i++){ alert( sText.charCodeAt( i ) ); } }; var strTest = "dknt没有任何含义"; getBinary(strTest ); 《script》
大于 255 的显然是占用两个字节的字符。要想办法分成单一字节的两个数据,以使程序流易于自动化一些。可以使用双字节数值与 二进制 1111111100000000 相与再右移8位来获得第一个字节。直接与11111111相与就可以得到第二个字节的数据。使用十六进制数可能更方便一点。1111111100000000 的十六进制表示为 FF00。11111111显然就是 FF了。
J(ava)script中,用0x前缀表示十六进制数。我们可以实践一下下面的代码。
《script》 function getBinary(sText){ for(var i = 0; i < sText.length; i++){ var iDecimalUnicode =sText.charCodeAt( i ); if( iDecimalUnicode > 255 ){ alert( (iDecimalUnicode & 0xFF00) >> 8); alert( iDecimalUnicode & 0xFF ); }else{ alert( iDecimalUnicode ); } } }; var strTest = "dknt没有任何含义"; getBinary(strTest ); 《script》
可以看到现在每个数都是小于255的了。
注意,(iDecimalUnicode & 0xFF00) >> 8 中,>> 的优先级比 & 高,所以按照我们的目的,(iDecimalUnicode & 0xFF00) 一定要有括号。
我们希望能有个统一的处理逻辑,把每个字节分成两部分,每个部分用十六进制的1位就可以表示,换句话说,就是每部分都是一个不超过16的十进制数。类似Ruby中的代码段数据类型,在J(ava)script中,也可以用匿名函数来实现类似的功能。我们可以建一个名为tmpOP变量来承接这个匿名函数,然后利用它来简化程序逻辑。此外,我们应该有个东西来储存分解出来的结果。那就用个result数组来装吧。另外按照语义,我们这个函数做的已经不仅仅是转化二进制了,而是转化成意义上的十六进制位了。我们应该是恨敏捷的,所以把函数名改成getHexes吧。
《script》 function getHexes(sText){ var aResult = []; var tmpOP = function(iByte){ aResult.push( (iByte & 0xF0) >> 4 ); aResult.push( iByte & 0xF ); }; for(var i = 0; i < sText.length; i++){ var iDecimalUnicode =sText.charCodeAt( i ); if( iDecimalUnicode > 255 ){ tmpOP( (iDecimalUnicode & 0xFF00) >> 8); tmpOP( iDecimalUnicode & 0xFF ); }else{ tmpOP( iDecimalUnicode ); } } alert(aResult); }; var strTest = "dknt没有任何含义"; getHexes(strTest ); 《script》
很高兴看到现在就弹出一个alert吧,刚才那么多alert是很闹心。我很抱歉。这次因为我们使用了alert一个数组,感觉整齐一点。
现在发现数组的每一个元素都是小于16了吧,很好,大象快装进去了。
有一个问题,我们不能把字符串的每个字符都转化成条形码,若是一个1万多字的文章怎么办,那不扯呢吗。所以我们要限制一下处理的字符数。以条形码的视点来看,似乎宽度应该是固定的,也就是说我们用以对应的 aResult 数组的长度应该是固定的。那也好办,在我们的 tmpOP 里控制一下就行了。我们可以假设我们只需要8个十六进制位来生成条形码。可以在getHexes里加一个 iMaxLength 参数来控制。
如下:
《script》 function getHexes(sText, iMaxLength){ var aResult = []; var tmpOP = function(iByte){ aResult.push( (iByte & 0xF0) >> 4 ); if( aResult.length > iMaxLength ) return 0; aResult.push( iByte & 0xF ); if( aResult.length > iMaxLength ) return 0; return 1; }; for(var i = 0; i < sText.length; i++){ var iDecimalUnicode =sText.charCodeAt( i ); if( iDecimalUnicode > 255 ){ if( !tmpOP( (iDecimalUnicode & 0xFF00) >> 8) ) break;; if( !tmpOP( iDecimalUnicode & 0xFF ) ) break; }else{ if( !tmpOP( iDecimalUnicode ) ) break; } } alert(aResult); }; var strTest = "dknt没有任何含义"; var iWidth = 8; getHexes(strTest, iWidth); 《script》
现在确实只有8个小于16的数了。
在 tmpOP 中,发现 aResult 数组的长度超过最大值,就返回一个0,外面发现这个0以后,就直接退出循环,因为没有必要再继续往下取字符了。
有些地方略显不妥,本着精益求精的精神,我们要把我们的程序效率提高提高。首先,我们知道了位相与的目的,就可以写一些更直接处理的代码,因为我们把处理双字节时,为了分成两个单字节,实际上多与运算了一次,和后面的分解双十六进制位有重复的位相与。说俗了就是多干了一次没用的事。不如一次就分解出4个十六进制位。
此外,我们总是向数组询问length属性来获知数组长度,要知道数组做这件事是很累的,反正我们也有条件自己心理有数,为什么还要总问它呢。
基于这两点,我们把程序改动如下:
《script》 function getHexes(sText, iMaxLength){ var aResult = [], aPos=[0xF, 0xF0, 0xF00, 0xF000], iLength = 0; var tmpOP = function(iByte, iPos){ aResult.push( (iByte & aPos[iPos]) >> iPos * 4 ); iLength++ if( iLength == iMaxLength ) return 0; return 1; }; for(var i = 0; i < sText.length; i++){ var iDecimalUnicode =sText.charCodeAt( i ); if( iDecimalUnicode > 255 ){ if( !tmpOP( iDecimalUnicode , 3) ) break;; if( !tmpOP( iDecimalUnicode , 2) ) break; if( !tmpOP( iDecimalUnicode , 1) ) break; if( !tmpOP( iDecimalUnicode , 0) ) break; }else{ if( !tmpOP( iDecimalUnicode , 1) ) break;; if( !tmpOP( iDecimalUnicode , 0) ) break; } } alert(aResult); }; var strTest = "dknt没有任何含义"; var iWidth = 8; getHexes(strTest, iWidth); 《script》
看到了效果跟上一个是一样的,说明我们没改错。其中,aPos数组就可以储存掩码,数组的索引 X 4 就是需要右移的位数。tmpOP( iDecimalUnicode , i) 就表示取 iDecimalUnicode 从右边数第i个十六进制位(第0个就是最右边的1个十六进制位)。
大象是勉勉强强塞进去了,下面我们就把活做的利索点,把冰箱门儿带上。要不条形码还没露面,我们怎么收场?
第三步,把冰箱门儿带上——封装和测试用例
接下来的工作重点就是要把条形码做出来。为了测试效果,我们还需要一个用户界面。
皮之不存,毛之焉附,首先做一个界面。随便做一个普通页面就行了。然后在上面安放一个文本框,一个触发按钮,一个条形码显示区域。
Barcode Test Case
我们需要把大象移植过来,加在我们的界面上,此外我们还需要让按钮能触发getHexes函数,那就加一个 onclick方法吧。
Barcode Test Case 《script》 function getHexes(sText, iMaxLength){ var aResult = [], aPos=[0xF, 0xF0, 0xF00, 0xF000], iLength = 0; var tmpOP = function(iByte, iPos){ aResult.push( (iByte & aPos[iPos]) >> iPos * 4 ); iLength++ if( iLength == iMaxLength ) return 0; return 1; }; for(var i = 0; i < sText.length; i++){ var iDecimalUnicode =sText.charCodeAt( i ); if( iDecimalUnicode > 255 ){ if( !tmpOP( iDecimalUnicode , 3) ) break;; if( !tmpOP( iDecimalUnicode , 2) ) break; if( !tmpOP( iDecimalUnicode , 1) ) break; if( !tmpOP( iDecimalUnicode , 0) ) break; }else{ if( !tmpOP( iDecimalUnicode , 1) ) break;; if( !tmpOP( iDecimalUnicode , 0) ) break; } } alert(aResult); }; var strTest = "dknt没有任何含义"; var iWidth = 8; 《script》
点击Generate按钮可以发现,我们之前的程序逻辑仍然生效。说明移植成功。
问题很大,getHexes始终操作的是一个固定的变量值,怎么让它能操作界面上的值呢?可以操作DOM来获取界面上的值。要使用DOM来操作,最简单的方法就是给所关注的元素上添加 id 属性。此外在 iWidth 这个变量在我们的界面中没有接口,看来是忘了,不过这个忘了很正常,当初根据我们的界面设计语义本来就没有这个内容。我们确实很敏捷,马上添加上去就行了。
Barcode Test Case 《script》 function getHexes(sText, iMaxLength){ var aResult = [], aPos=[0xF, 0xF0, 0xF00, 0xF000], iLength = 0; var tmpOP = function(iByte, iPos){ aResult.push( (iByte & aPos[iPos]) >> iPos * 4 ); iLength++ if( iLength == iMaxLength ) return 0; return 1; }; for(var i = 0; i < sText.length; i++){ var iDecimalUnicode =sText.charCodeAt( i ); if( iDecimalUnicode > 255 ){ if( !tmpOP( iDecimalUnicode , 3) ) break;; if( !tmpOP( iDecimalUnicode , 2) ) break; if( !tmpOP( iDecimalUnicode , 1) ) break; if( !tmpOP( iDecimalUnicode , 0) ) break; }else{ if( !tmpOP( iDecimalUnicode , 1) ) break;; if( !tmpOP( iDecimalUnicode , 0) ) break; } } alert(aResult); }; 《script》
Text: Width:
注意,我们已经把<script /> 标签的
var strTest = "dknt没有任何含义";
var iWidth = 8;
两句去掉了。因为他们确实没有什么用了,我们已经不从那里获得数据了。
这回,如果你改动两个文本框中的文字,将会看到另外一组十六进制位。此外,我们觉得加一个对文本框的说明更好一些,所以就在前面加了个p.
现在我们发现似乎把一大串字符写在onlick里似乎有点不自然,如果将来逻辑更复杂了将很难维护,不如就单建个函数专门负责此事。它也可以包含更复杂的调度逻辑。此外,我们对两个文本框的类型没有验证,如果输入的不是我们想要的数据类型怎么办?所以还要加上判断逻辑。所以修改如下:
Barcode Test Case 《script》 function getHexes(sText, iMaxLength){ var aResult = [], aPos=[0xF, 0xF0, 0xF00, 0xF000], iLength = 0; var tmpOP = function(iByte, iPos){ aResult.push( (iByte & aPos[iPos]) >> iPos * 4 ); iLength++ if( iLength == iMaxLength ) return 0; return 1; }; for(var i = 0; i < sText.length; i++){ var iDecimalUnicode =sText.charCodeAt( i ); if( iDecimalUnicode > 255 ){ if( !tmpOP( iDecimalUnicode , 3) ) break;; if( !tmpOP( iDecimalUnicode , 2) ) break; if( !tmpOP( iDecimalUnicode , 1) ) break; if( !tmpOP( iDecimalUnicode , 0) ) break; }else{ if( !tmpOP( iDecimalUnicode , 1) ) break;; if( !tmpOP( iDecimalUnicode , 0) ) break; } } alert(aResult); }; function GenerateBarCode(){ var sText = document.getElementById('text').value, iWidth = parseInt( document.getElementById('width').value ); sText = sText.replace(/(^s+|s+$)/ig, ''); iWidth = iWidth || 0; if( iWidth > 20 || iWidth < 0) return false; if(sText.length < iWidth ) return false; getHexes(sText, iWidth) } 《script》
Text: Width:
GenerateBarCode 要去掉text左右的空格,然后还要检查width是否是有效值(这里最大设为20,不过你可以随便改,太大似乎就有点变态了)。
然而我们的条形码还是没出来,但是我们已经恨厌倦alert了,这次一定要让getHexes返回一个数组给GenerateBarCode,然后让GenerateBarCode进行后续处理。
Barcode Test Case 《script》 function getHexes(sText, iMaxLength){ var aResult = [], aPos=[0xF, 0xF0, 0xF00, 0xF000], iLength = 0; var tmpOP = function(iByte, iPos){ aResult.push( (iByte & aPos[iPos]) >> iPos * 4 ); iLength++ if( iLength == iMaxLength ) return 0; return 1; }; for(var i = 0; i < sText.length; i++){ var iDecimalUnicode =sText.charCodeAt( i ); if( iDecimalUnicode > 255 ){ if( !tmpOP( iDecimalUnicode , 3) ) break;; if( !tmpOP( iDecimalUnicode , 2) ) break; if( !tmpOP( iDecimalUnicode , 1) ) break; if( !tmpOP( iDecimalUnicode , 0) ) break; }else{ if( !tmpOP( iDecimalUnicode , 1) ) break;; if( !tmpOP( iDecimalUnicode , 0) ) break; } } return aResult ; }; function GenerateBarCode(){ var sText = document.getElementById('text').value, iWidth = parseInt( document.getElementById('width').value ); sText = sText.replace(/(^s+|s+$)/ig, ''); iWidth = iWidth || 0; if( iWidth > 20 || iWidth < 1) return false; var aHexes = getHexes(sText, iWidth), sDivString=''; for (var i = 0; i < iWidth; i++){ sDivString += "" } document.getElementById('BarCode_Field').innerHTML = sDivString; } var gifURL = "/upload/200742411119165.gif"; 《script》
Text: Width:
GenerateBarCode接到getHexes传过来的数组以后开始使用其中的十六进制位构造DIV小单元。其中,我们用 background-image 来指明背景文件的位置,正好我刚才上传了做好的gif文件,用gifURL保存它的位置。background-position-x表示背景图片水平方向偏移,我们用十六进制位(范围是0-15) X 8 (即gif小单元的像素宽度) 正好就可以让我们想要的gif小单元作为当前p的背景了。这就是我们的gif为什么要做成那样的原因。实际上,之所以要把所有的小单元放在一个图片里,主要是为了节省I/O调用的次数,提高效率。
GenerateBarCode中的for循环,终止条件是iWidth,以便让sText补足iWidth位时,也能显示出 iWidth 位来,因为数组空元素的默认值可以返回0。
我们给承接结果的p赋以id为BarCode_Field,将构造好的HTML片段放在这个p中,页面就可以呈现出条形码了。
然而似乎还是没看到条形码。那当然了,我们的gif背景透明色已经让页面的背景白色透过来了,白成一片了,当然看不着。我们得改一下Body的背景颜色。如下:
Barcode Test Case 《script》 function getHexes(sText, iMaxLength){ var aResult = [], aPos=[0xF, 0xF0, 0xF00, 0xF000], iLength = 0; var tmpOP = function(iByte, iPos){ aResult.push( (iByte & aPos[iPos]) >> iPos * 4 ); iLength++ if( iLength == iMaxLength ) return 0; return 1; }; for(var i = 0; i < sText.length; i++){ var iDecimalUnicode =sText.charCodeAt( i ); if( iDecimalUnicode > 255 ){ if( !tmpOP( iDecimalUnicode , 3) ) break;; if( !tmpOP( iDecimalUnicode , 2) ) break; if( !tmpOP( iDecimalUnicode , 1) ) break; if( !tmpOP( iDecimalUnicode , 0) ) break; }else{ if( !tmpOP( iDecimalUnicode , 1) ) break;; if( !tmpOP( iDecimalUnicode , 0) ) break; } } return aResult ; }; function GenerateBarCode(){ var sText = document.getElementById('text').value, iWidth = parseInt( document.getElementById('width').value ); sText = sText.replace(/(^s+|s+$)/ig, ''); iWidth = iWidth || 0; if( iWidth > 20 || iWidth < 1) return false; var aHexes = getHexes(sText, iWidth), sDivString=''; for (var i = 0; i < iWidth; i++){ sDivString += "" } document.getElementById('BarCode_Field').innerHTML = sDivString; } var gifURL = "/upload/200742411119165.gif"; 《script》
Text: Width:
大功告成。