我们使用了自己写的nodejs库。 要实现这种效果,只需遵循以下第一章的方法。 当然,也可以在线压缩代码。
分为以下两章,说明名为js2image的库和名为js2image的库的使用原理。
使用js 2图像
js2image主要有两个比较特殊的特性。 将任意js源代码压缩为由代码堆积形成的最终代码。 圣诞树、圣诞老人、代码、照片等都可以定制。
压缩的js代码格式已被破坏,但仍可以执行。 这是一个重点!
使用方法很简单。 npm install js2image -g;
然后,在js所在的文件夹中运行。 js2image -s ./resource/jquery.js
或者,如果对某个目录下的所有js运行,则会深度遍历该目录中的所有js文件,并压缩带有. xmas.js后缀的结果文件。 js2image -s ./resource/
将生成相应的**.xmas.js文件。
如果要将js2image集成到gulp或其他nodes项目中,可以使用使用模块的格式。 varjs2image=require(js2image ); //得到结果
code js2 image.getcode ('./resource/jquery.js ','./resource/tree.png ',{}}.then ) ) function ) {
console.log (代码;
() )
有关详细信息,请参考github文档。
如果只是使用这个效果的话,看这里就可以了。 解释一下这个库的原理,可能也有绕圈子的地方。
js2image实现原理
js2image的实现从宏观上来说,大致只有三个点。 从图像生成文字的画。 这有现成的库。
将js代码拆分为较小的部分,尽量缩小,然后逐行填充并替换为上一步生成的文字图片。
js代码中有很多无法分离的语法。 分块时请将这些语法留在一个块内。 是这个库的难点,也是代码最多的地方。
我想有点想法的学生,看到这里大概就知道发生了什么,我会逐一说明这三个要点。
从图像生成2个文字画
这里使用了一个叫image-to-ascii的现成的npm包。 此库的作用是用指定的字符恢复图像。 使用此库可以生成分别由字符和空格表示黑色和白色的文字图片,并将文字图片的每一行分解为数组的元素,用于第二步。 这就是我们之间生成的结构。 代码请参见utils/image-to-struct.js
尽量将js源代码分成小块。
这是一个非常重要的步骤,但js代码具体能分解成多少小块?
请看下面的代码。 函数
(e,t
() ) ) (
“对象”
' t ' )==
typeof
模块(
“objec' 't”)
==typeof module
. exports? 模块。
exports=e.document? t(e
、 0 ) :函数(e ) {if (! e.
文档(throw new error (
(' jQuer' 'y req' 'uires' ' a wi )
' ndow ' 'with ' 'a doc' 'ument ' )
; returnt(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中就生成了最终的代码:
wile(lines.Length0) {
//循环地在struct中填充代码
struct.foreach(function(s ) )
varchars_arr=s.replace(/g,''); //一行分为多个组的*****
var r=s;
chars_ARR.split(/).forE
ach(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和图像生成图形代码了。