http://blog.csdn.net/zswang/article/details/7061560

将js/css脚本放到png图片中的实践。

2011-12-11 21:50 13788人阅读 评论(26) 收藏 举报
 分类:
前端技术 png canvas 压缩

版权声明:本文为博主原创文章,未经博主允许不得转载。

起因
高级浏览器支持data协议,如:
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC"/>
<script src="data:text/javascript;base64,YWxlcnQoJ2h0dHA6Ly93ZWliby5jb20venN3YW5nJyk7"></script>
不光是<img>标签,<script>、<style>、<iframe>也都支持data协议;
data协议可以表示图片、文本、音视频等各种二进制数据
另html5的canvas除了绘制图像,还能读写每一个像素值
一种把脚本存到图像中的思路就有了。
 
经过
最初的尝试就是把每个字符挨个放到像素的r、g、b、a里

需要处理的问题:

1、计算图片的尺寸;
     本来想用高度为1,宽度为字符串长度的尺寸,但考虑到压缩比和美观,所以决定生成正方形。
        var pixel = Math.ceil((text.length + 3) / 4);
        var size = Math.ceil(Math.sqrt(pixel));
2、每个字符unicode编码,可能会超出255;
     那就得将字符串转成ascii码
function encodeUTF8(str) {
    return String(str).replace(
        /[\u0080-\u07ff]/g,
        function(c) {
            var cc = c.charCodeAt(0);
            return String.fromCharCode(0xc0 | cc >> 6, 0x80 | cc & 0x3f);
        }
    ).replace(
        /[\u0800-\uffff]/g,
        function(c) {
            var cc = c.charCodeAt(0);
            return String.fromCharCode(0xe0 | cc >> 12, 0x80 | cc >> 6 & 0x3f, 0x80 | cc & 0x3f);
        }
    );
}
3、还得考虑异步的问题
    var img = document.createElement('img');
    img.onload = function(){
        //...;
    }
 
第一轮demo做完,结果测试不符合预期。跟踪发现:还原的数据和编码的数据相差较大。
开始在怀疑是不是img标签绘制到canvas变成了有损压缩,如果真是这样,那整个方案就是不可行;
坚持“不抛弃,不放弃”+定位问题最简化的原则,逐步缩小范围,最终锁定是由于alpha值影响还原。
解决方案就是:一个像素存放三个字符并将alpha固定为255。

第二轮demo测试符合预期。
 
总结
build流程:
1、字符串转换成ascii码;
2、创建足够存储空间的canvas;
3、将字符填入到像素中(忽略alpha值);
4、获取data url;
     canvas.toDataURL("image/png");

5、存为png图片。
代码示例

  1. <textarea id="base64"></textarea>
  2. <script>
  3. function encodeUTF8(str) {
  4. return String(str).replace(
  5. /[\u0080-\u07ff]/g,
  6. function(c) {
  7. var cc = c.charCodeAt(0);
  8. return String.fromCharCode(0xc0 | cc >> 6, 0x80 | cc & 0x3f);
  9. }
  10. ).replace(
  11. /[\u0800-\uffff]/g,
  12. function(c) {
  13. var cc = c.charCodeAt(0);
  14. return String.fromCharCode(0xe0 | cc >> 12, 0x80 | cc >> 6 & 0x3f, 0x80 | cc & 0x3f);
  15. }
  16. );
  17. }
  18. function request(url, loaded) {
  19. var xmlhttp = new XMLHttpRequest();
  20. xmlhttp.onreadystatechange = function() {
  21. if (xmlhttp.readyState == 4)
  22. if (xmlhttp.status == 200)
  23. loaded(xmlhttp);
  24. }
  25. xmlhttp.open("GET", url, true);
  26. xmlhttp.send();
  27. }
  28. void function(){
  29. var source = 'tangram-1.5.0.js';
  30. request(source, function(xmlhttp){
  31. var text = encodeUTF8(xmlhttp.responseText);
  32. var pixel = Math.ceil((text.length + 2) / 3); // 1一个像素存3个字节,
  33. var size = Math.ceil(Math.sqrt(pixel));
  34. //console.log([text.length, pixel, size, size * size * 3]);
  35. var canvas = document.createElement('canvas');
  36. canvas.width = canvas.height = size;
  37. var context = canvas.getContext("2d"),
  38. imageData = context.getImageData(0, 0, canvas.width, canvas.height),
  39. pixels = imageData.data;
  40. for(var i = 0, j = 0, l = pixels.length; i < l; i++){
  41. if (i % 4 == 3) { // alpha会影响png还原
  42. pixels[i] = 255;
  43. continue;
  44. }
  45. var code = text.charCodeAt(j++);
  46. if (isNaN(code)) break;
  47. pixels[i] = code;
  48. }
  49. context.putImageData(imageData, 0, 0);
  50. document.getElementById('base64').value = canvas.toDataURL("image/png");
  51. });
  52. }();
  53. </script>
编译结果
调用流程:
1、加载png;
2、将png原尺寸绘制到canvas中;
3、读取像素中的字符串;
4、生成相应协议的data url使用。
代码示例

  1. <script>
  2. void function(){
  3. var source = 'tangram-1.5.0.png';
  4. var img = document.createElement('img');
  5. img.onload = function(){
  6. var canvas = document.createElement('canvas');
  7. canvas.width = img.width;
  8. canvas.height = img.height;
  9. var context = canvas.getContext("2d");
  10. context.drawImage(img, 0, 0);
  11. var imageData = context.getImageData(0, 0, canvas.width, canvas.height),
  12. pixels = imageData.data;
  13. var script = document.createElement('script');
  14. var buffer = [];
  15. for (var i = 0, l = pixels.length; i < l; i++) {
  16. if (i % 4 == 3) continue; // alpha会影响png还原
  17. if (!pixels[i]) break;
  18. buffer.push(String.fromCharCode(pixels[i]));
  19. }
  20. script.src = 'data:text/javascript;charset=utf-8,' + encodeURIComponent(buffer.join(''));
  21. document.body.appendChild(script);
  22. script.onload = function(){
  23. alert(T.date.format(new Date, 'yyyy年M月d日'));
  24. }
  25. img = null;
  26. }
  27. img.src = source;
  28. }();
  29. </script>

优势

1、压缩率大(50%);
2、隐蔽性相对高;
     可以设计加密的脚本,用公钥解锁;
3、减少网络请求;
     可以将多个图片、脚本放到一个png里,囧;
4、一种二进制处理文本的思路。
 
劣势
1、解码会使用更多的cpu,导致加载缓慢;
2、不支持低端浏览器;
3、开发维护成本更高。
 
总结
1、不具实战性;
2、在数据加密传输方面可以近一步研究。
 
相关demo
 
 

将js/css脚本放到png图片中的实践。的更多相关文章

  1. MVC 中如果js,css文件放到视图文件夹无法访问的解决办法

    在视图配置文件web.config 中添加如下节点,注意:此web.config 是视图文件夹中的,在你的views视图中可以找到<system.webServer>开头的<hand ...

  2. 自写脚本实现上线前本地批量压缩混淆 js , css 代码。

    最近做项目遇到一个要求,就是把本地的 js 和 css 进行压缩后再上线,由于之前项目并没有使用 webpack 之类的库,项目上也因为一些机密不能在线上压缩,这无疑给代码打包压缩带来了很大麻烦,于是 ...

  3. JS&CSS文件请求合并及压缩处理研究(五)

    接上篇.在我们最终调用 @Html.RenderResFile(ResourceType.Script) 或者 @Html.RenderResFile(ResourceType.StyleSheet) ...

  4. iOS之在webView中引入本地html,image,js,css文件的方法 - sky//////////////////////////////////////ZZZZZZZZZZZZZZZ

    iOS之在webView中引入本地html,image,js,css文件的方法   2014-12-08 20:00:16CSDN-sky_2016-点击数:10292     项目需求 最近开发的项 ...

  5. ASP.NET MVC 4 Optimization的JS/CSS文件动态合并及压缩

    JS/CSS文件的打包合并(Bundling)及压缩(Minification)是指将多个JS或CSS文件打包合并成一个文件,并在网站发布之后进行压缩,从而减少HTTP请求次数,提高网络加载速度和页面 ...

  6. JS&CSS文件请求合并及压缩处理研究(一)

    在我们日常的网站开发工作中,一个页面难免会引用到各种样式及脚本文件.了解Web开发的朋友们都知道,页面引用的每一个: <link href="style.css" rel=& ...

  7. ASP.NET MVC 4使用Bundle的打包压缩JS/CSS

    打包(Bundling)及压缩(Minification)指的是将多个js文件或css文件打包成单一文件并压缩的做法,如此可减少浏览器需下载多个文件案才能完成网页显示的延迟感,同时通过移除JS/CSS ...

  8. 实用js+css多级树形展开效果导航菜单

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  9. js+css实现带缓冲效果右键弹出菜单

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

随机推荐

  1. Vue CLI UI:Vue开发者必不可少的工具

    突然发现一个Vue cli 比较好用的工具,一个可视化图形界面方便你去创建.更新和管理Vue项目.这里分享2个作者写得比较好的文章 https://codeseeding.com/portal.php ...

  2. C++学习笔记(三)--数组、字符串

    1.数组,C++中不允许数组的下标值为变量,只能是常量或者常量表达式,必须先定义后使用.数组赋初值几种常见方式: int a[] = {1,2,3,4,5}: int a[4] = {2,1,3,4} ...

  3. stl应用(map)或字典树(有点东西)

    M - Violet Snow Gym - 101350M Every year, an elephant qualifies to the Arab Collegiate Programming C ...

  4. 进程池和multiprocess.Pool模块

    一.为什么要有进程池 首先,创建进程需要消耗时间,销毁进程也需要时间.其次,即使开启了成千上万的进程,操作系统也不能让它们同时执行,这样反而会影响程序的效率.因此我们不能无限制的根据任务开启或者结束进 ...

  5. 57-python基础-python3-集合-集合常用方法-添加元素-add()-update()

    添加元素-add()-update() 1-add() add()用于增加一个元素值,原值修改,无返回值. 2-update()用于添加一个可迭代的对象,原值修改,无返回值. 下面依次向集合添加可迭代 ...

  6. python学习第七天流程控制循环while和循环for区别

    流程控制循环是任何编程语言都有一种循环结构,在python while 和break continue 搭配使用,还一种while ....else ......,for循环有序列表和字符串 whil ...

  7. JS 数组 学习笔记

    什么是数组 数组(array)是按次序排列的一组值.每个值的位置都有编号(从0开始),整个数组用方括号表示.本质上数组属于一种特殊的,由Array构造出来的对象,typeof运算符返回数组的类型是 o ...

  8. vsftpd.service: Main process exited, code=exited, status=2/INVALIDARGUMENT和vsftpd:500 OOPS: vsftpd: refusing to run with writable root inside chroot ()错误的解决方法

    今天在配置VSFTPD过程中遇到两个错误 1是启动失败,通过 SERVICE VSFTPD STATUS 查看到报错 May 02 16:06:58 debian systemd[1]: Starti ...

  9. ltp-ddt realtime_cpu_load timeout

    # @name Worstcase Latency with cpu load by using cyclictest# @desc Measure latency under cpu load by ...

  10. 前端每日实战:69# 视频演示如何用纯 CSS 创作一个单元素抛盒子的 loader

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/qKwXbx 可交互视频 此视频是可 ...