天天看點

聖誕節,把你的 JavaScript 代碼都裝扮成聖誕樹吧

下面分兩章節,分别講解如何使用js2image這個庫 和 js2image這個庫的原理。

聖誕節,把你的 JavaScript 代碼都裝扮成聖誕樹吧

js2image使用

js2image主要有兩個比較特殊的特性:

将任意js源碼 壓縮成 用代碼堆砌成圖形的最終代碼。例如聖誕樹,聖誕老人,代碼和圖檔都可以自定義。

壓縮後的js代碼格式雖然被破壞,但是仍然可以運作。這個是關鍵點!

壓縮後的示例可以檢視這些js(均來自搜車官網):

使用方式很簡單:

npm install js2image -g; 

然後在存在js的檔案夾中執行:

js2image -s ./resource/jquery.js 

或者針對某個目錄下所有的js執行(慎用),會深度周遊此目錄裡所有的js檔案然後壓縮出.xmas.js字尾的結果檔案。

js2image -s ./resource/ 

即可生成一個對應的 **.xmas.js 的檔案。

如果要将js2image內建到gulp或者其他nodes項目中,可以使用用子產品的形式:

var js2image = require("js2image");//擷取結果的    codejs2image.getcode("./resource/jquery.js","./resource/tree.png",  {}).then(function(code){  

   console.log(code); 

}) 

更多的資訊可以參照github上的文檔。

如果隻是要使用這個效果,看到這裡就ok啦,下面講解這個庫的原理,有些地方可能比較繞。

js2image實作原理

js2image的實作從宏觀來說,大體隻有3個要點。

從圖檔生成字元畫,這個有現成的庫。

把js代碼分割成一小塊一小塊,盡量小,然後用逐行填充的方式分别替換到上一步生成的字元畫裡去。

js代碼中有諸多不能分開的文法,分塊的時候要把這些文法保留在一個塊内。這個是這個庫的難點所在,也是代碼最多最繞的地方。

稍有想法的同學估計看到這裡基本已經明白是怎麼回事了,下面一一講解這3個要點。

① 從圖檔生成2值得字元畫

這裡用到了一個現成的npm包:image-to-ascii 。這個庫的作用是用指定的字元來還原一個圖像。而我們用這個庫來生成一個用

字元和空格

分别表示黑和白的字元畫,然後将字元畫的每一行分解成數組的一個元素,供第二步使用,這就是我們中間生成的一個struct,代碼見 utils/image-to-struct.js

② 分割js源碼成盡量小的小塊。

這是非常重要的一步,js代碼具體可以分解成多細的小塊呢?

看下面一段代碼:

                        !function             

                        (e,t             

                        ){ (             

                       "objec"           

                      +"t")  ==           

                     typeof           

                   module &&  (         

                  "objec"+"t")           

                == typeof module         

              .exports?module.       

            exports=e.document?t(e       

          ,!0):function(e){if(!e.     

        document) throw new error (     

      ("jquer"+"y req"+"uires"+" a wi"   

   +"ndow "+"with "+"a doc"+"ument") )   

; return t (e)}:t(e)}( ("undef"+"ined") 

!=typeof    window ?window:this,function(e,t){var 

這是jquery開始的一段代碼,可以看到,大部分操作符都允許中間插入任意多的空格或者換行,我們正是利用這一特性将js代碼解肢,然後拼接成任意形狀的圖檔。

核心代碼其實就是一個正則,我們用這個正則把js源碼解構成一個數組,然後後續根據每行需要的字元數,從這個數組裡不斷取片段出來拼接。

//分離代碼,以可分割機關拆分成數組。var lines = hold_code.replace(/([^a-za-z_0-9=!|&$])/g,"/n$1/n").split("/n"); 

//有了這個lines數組之後後面就簡單了,根據第一步裡生成的struct不斷周遊從lines抽取代碼填充到struct裡即可生成最終的代碼: 

while(lines.length>0){  

    //循環往struct裡填充代碼 

    struct.foreach(function(s){ 

        var chars_arr = s.replace(/ +/g," ");//一行有多組分離的***** 

        var r = s; 

        chars_arr.split(/ +/).foreach(function(chars){ 

            if(chars.length == 0){ 

                return; 

            } 

            var char_count = chars.length; 

            //從lines裡取出char_count數量的代碼來填充,不一定精準,要確定斷行正确 

            var l = pickfromlines(lines,char_count); 

            r = r.replace(chars,function(){ 

                return l; 

            }) 

        }) 

        result += r+"/n" 

    }) 

③ 保留不可分割的文法

注意:到了這一步,還很早,你分解出來的代碼是無法運作的,很多不能換行和加空格的代碼都被你分開了,自然會報錯,那如何處理這些情況呢?

這一步,我們做的工作就是:

在執行代碼分拆之前,提取出代碼裡所有不可分割的文法,将他們保留在一個對象中,并且在源代碼中用占位符替代這些文法,然後讓占位符參與上個步驟的分離,因為占位符是一個完整的連字元變量,是以不會被分割。在分割完成之後,我們再把這些占位符替換回來即可。

不過,在js中哪些文法必須是連接配接在一起才能正常運作的呢?

這裡總結下:

字元串不可分割 包括雙引号單引号内的内容。

正規表達式絕對不可分割 正則裡的轉義很難處理,這是這個算法裡的難點。

運算操作符 包括2字元的3字元的 例如 以下兩種

var double_operator = ["==", ">=", "<=", "+=", "-=", "*=", "/=", "%=", "++", "--", "&&", "||", ">>", "<<"]  

var three_operator = ['===', '!=='] 

一些固定文法,可以用正則表達,如下:

var reg_operator = [  

    { 

        start:"return", 

        reg:/^return[^a-za-z_0-1"'][a-za-z_0-1.]+/ 

        // return 0.1 或者 return function 或者return aaabb 

    }, 

        start:"return/"", 

        reg:/^return".*?"/ // return "d" 或者 return "" 

        start:"return/'", 

        reg:/^return'.*?'/  // return 'd' 或者 return '' 

        start:"throw", 

        reg:/^throw [a-za-z_0-1]+?/ //throw new 或者 throw obj 

    } 

小數點文法,例如 0.01 因為之前我們用點号來分割代碼的,但是這裡的點号不能作為分割符使用,需要保留前後數字跟點号在一行 其他文法,例如 value++ 之類的文法,變量和操作符之間不可分割。 那我們如何從源代碼中解析出這些文法,然後做處理呢?

核心代碼均在 utils/keep-line.js 中

核心算法,事實上是通過一個對字元串的周遊來完成的,然後在周遊每個字元的時候都會判斷是否進入某個邏輯來跳躍處理。

例如,判斷出目前在雙引号内,則進入字元串提取邏輯,一直到字元串結束的時候再繼續正常的周遊。

其他操作符和正規表達式的算法也是類似,不過裡面很多細節需要處理,例如轉義字元之類的。

有些比較特殊的,例如小數點文法的提取,在判斷到目前字元是點号之後,需要往前和向後循環查找數字,然後把整個文法找出來。

這裡不細講,在keep-line.js 這個檔案中又一大坨代碼做這個事情的。

④ 字元串解構

做到這一步的時候,其實效果已經很不錯了,也可以保證代碼的可運作,但是代碼裡有些字元串很長,他們總是會被被保留在一行裡,這樣就造成他會影響一些圖案的邊緣的準确性(代碼分離原則是越細越好,就是為這個考慮)。

我們如何處理呢,那就是将字元串解構,以5個為機關将字元串分離成小塊。

這裡有兩個比較重要的問題需要處理;

字元串内的轉義字元如何處理,還有一些特殊字元,例如0×01這樣的字元,這些字元不能被分離到不同的字元串裡,是以分離的時候要保留這些字元串的完整性。

字元串分離成小字元串,然後用+号拼接起來,不過要注意操作符優先級的問題,是以所有分離後的字元串,都要用括号包起來,讓這個+号的優先級永遠最高。

具體算法見 keep-line.js 中的 splitdoublequot (分離雙引号字元串)。

結語

至此,整個應用就完成了,可以順利完成從任意js和圖像生成圖形代碼了。

來源:51cto

繼續閱讀