你的位置:首页 > Java教程

[Java教程]将图片转化为矢量并canvas化的简单工具(基于Node.js + HTML5 canvas)


一、前言

最近需要做一个图标的矢量化,但是没有数据,因此采用了node.js作为数据处理工具,canvas绘制图标;结果发现使用canvas绘制的图标比之前少了近10几k(原20K+, 现包含代码10k-);所以结果还是比较近人意的。代码已经放在github了,具体戳这:https://github.com/vczero/image-vector 欢迎大家改进,这只是个so simple tool...

二、设计

(1)做一个基本的矢量数据处理工具,方便后期数据生产较为自动化;

(2)选用node.js作为批量数据处理的脚本工具,暴露服务接口,前端调用;canvas按照图层要素渲染;

三、产出

(1)canvas绘制携程的图标(不是图片,是json数据),如下。可以访问:http://vczero.github.io/ctrip/index.html;

(2)简单的矢量化处理工具,如下图:

四、node.js服务端

node.js主要作为数据处理服务存在,具体的代码很简单,如下:

(0)服务接口如下:

 1 /* 2  * 路由服务列表模块  3  *  4  * */ 5 var routes = require('./index'); 6 var create = require('./create'); 7 var get = require('./get'); 8 var compile = require('./compile'); 9 var dir = require('./dir');10 var contact = require('./contact');11 var dist = require('./dist');12 13 14 module.exports = function(app){15   16   //index, 服务列表17   app.use('/', routes);18   19   //des:创建点文件20   //@x:横坐标21   //@y:纵坐标22   //@fileName:需要保存的文件名23   //url: domain/create?x=112&y=678&fileName=polygon_text24   app.get('/create', create);25   26   //des:获取目录下指定json文件的json对象27   //@dirName:28   //@fileName:29   //url: domain/get?dirName=json&fileName=polygon_text30   app.get('/get', get);31   32   //des:将点数据转化成可用坐标数据33   //@fileNames:当isMany传入参数为1的时候,fileNames=file1@file2@file2的形式;否则只为fileNames=fileName34   //[@isMany]:当需要处理多文件的时候传入35   //url:domain/compile?fileNames=xxx[&isMany=1]36   app.get('/compile', compile);37   38   //des:列出目录下面的文件39   //@dirName:目录名称40   //url:domain/dir?dirName=xxx41   app.get('/dir', dir);42   43   //des:数据合并44   //@fileNames:多文件,以@分割,例如fileNames=file1@file2@file245   app.get('/contact', contact);46   47   //des:数据压缩48   //@fileName需要压缩的文件名49   //url:domain/dist?fileName=xxx50   app.get('/dist', dist);51   52 };

(1)获取某个文件夹下的某个文件:

 1 /* 2  * 描述:读取json文件的json对象, 这里因为实时读取建议不使用require加载 3  * 时间:2015-04-14 4  * */ 5  6 var fs = require('fs'); 7  8 module.exports = function(req, res){ 9   var dirName = req.param('dirName');10   var fileName = req.param('fileName');11   var errStatus = {status: 0};12   13   if(!dirName || !fileName){14     return res.json(err);15   }16   17   var path = './' + dirName + '/' + fileName + '.json';18   19   fs.readFile(path, function(err, data){20     try{21       var reObj = null;22       var obj = JSON.parse(data.toString());23       reObj = {24         status: 1,25         data: obj26       }27     }catch(e){28       reObj = null;29     }30   31     if(!err && obj){32       return res.json(reObj);33     }else{34       return res.json(errStatus);35     }36   });  37 };

(2)创建点位数据

 1 /* 2  * 描述:向文件中追加坐标 3  * 时间:2015-04-14 4  * */ 5 var fs = require('fs'); 6  7 module.exports = function(req, res){ 8   var x = req.param('x'); 9   var y = req.param('y');10   var fileName = req.param('fileName');11   var str = x + '\t' + y + '\r\n';12   13   fs.appendFile('./data/' + fileName, str, function(err){14     var obj = {15       x: x,16       y: y17     }18     if(!err){19       obj.status = 1;20       res.json(obj);21     }else{22       obj.status = 0;23       res.send(obj);24     }25     console.log('saved: ', x + ' , ' + y);26   });27   28   29 }

(3)将数据转化为可用的json格式数据

 1 /* 2  * 描述:将数据转化为可用的json 3  * 时间:2015-04-14 4  * */ 5  6 var fs = require('fs'); 7 var parseData = function(data){ 8   var strs = (data.toString()).split('\r\n'); 9   var data = [];10   for(var i = 0; i < strs.length; i++){11     var xys = strs[i].split('\t');12     var xy = {13       x: xys[0],14       y: xys[1]15     };16     if((i + 1) !== strs.length)17       data.push(xy);18   }19   var obj = data;20   return obj;21 };22 23 module.exports = function(req, res){24   var fileNames = req.param('fileNames');25   var isMany = req.param('isMany');26   var pathData = '';27   var pathJSON = '';28   29   //单文件处理30   if(!isMany){31     pathData = './data/' + fileNames;32     pathJSON = './json/' + fileNames + '.json';33     fs.readFile(pathData, function(err, data){34       var obj = parseData(data);35       fs.writeFile(pathJSON, JSON.stringify(obj), function(err){36         if(!err)37           return res.json(obj);38         else39           return res.json({status: 0});40       });  41     });  42   }else{//多文件处理43     var names = fileNames.split('@');44     try{45       for(var i in names){46         var path_Data = './data/' + names[i];47         var path_JSON = './json/' + names[i] + '.json';48         var content = fs.readFileSync(path_Data);49 50         fs.writeFileSync(path_JSON, JSON.stringify(parseData(content)));51       }52       res.json({status: 1});53     }catch(e){54       res.json({status: 0});  55     }56   }57 }

(4)列目录

 1 /* 2  * 描述:列目录 3  * 时间:2015-04-14 4  * */ 5  6 var fs = require('fs'); 7  8 module.exports = function(req, res){ 9   var dirName = req.param('dirName');10   fs.readdir('./' + dirName, function(err, files){11     if(!err){12       return res.json({13         status: 1,14         files: files15       });16     }17     return res.json({status: 0});18     19   });20 };

(5)多个json文件合并

 1 /* 2  * 描述:数据合并 3  * 时间:2015-04-14 4  * */ 5  6 var fs = require('fs'); 7 module.exports = function(req, res){ 8   var fileNames = req.param('fileNames'); 9   var pathContact = './contact/';10   var names = fileNames.split('@');11   12   try{13     var content = [];14     for(var i in names){15       var pathData = './json/' + names[i];16       var data = fs.readFileSync(pathData);17       var name = names[i].split('.')[0];18       var obj = {};19       obj[name] = JSON.parse(data.toString());20       content.push(obj);21       22     }23     var files = fs.readdirSync(pathContact);24     var newName = 'contact_';25     if(files.length){26       var n = files.length;27       newName += (parseInt(n) + 1) + '.json';28     }else{29       newName += '1.json'; 30     }31     var str = JSON.stringify(content);32     fs.writeFile(pathContact + newName, str, function(err){33       if(!err)34         res.json({status: 1});  35     });36   }catch(e){37     res.json({status: 0});  38   }39   40 }

(6)去除冗余的数据

 1 var fs = require('fs'); 2  3 module.exports = function(req, res){ 4   var fileName = req.param('fileName'); 5   var errStatus = {status: 0}; 6    7   if(!fileName){ 8     return res.json(errStatus);   9   }10   11   var path = './contact/' + fileName + '.json';12   fs.readFile(path, function(err, data){13     if(!err){14       var arr = JSON.parse(data.toString());15       //TODO:Format && compare16       //第一层17       var result = {};18       for(var i in arr){19         var obj = arr[i];20         //第二层21         for(var n in obj){22           var dataXYs = obj[n];23           result[n] = [];24           //第三层25           for(var k in dataXYs){26             result[n].push(dataXYs[k].x);27             result[n].push(dataXYs[k].y);28           }29         }30       }31       //写入到压缩文件夹32       var files = fs.readdirSync('./dist/');33       var newFileName = 'dist_';34       if(files.length){35         var n = files.length;36         newFileName += (parseInt(n) + 1) + '.json';37       }else{38         newFileName += '1.json';39       }40       fs.writeFile('./dist/' + newFileName, JSON.stringify(result), function(err){41         if(!err){42           var sizeOld = fs.statSync(path).size;43           var sizeNew = fs.statSync('./dist/' + newFileName).size;44           var reObj = {45             status: 1,46             //压缩率计算47             rate: (sizeOld - sizeNew)/sizeOld,48             //新文件名49             filename: newFileName,50             //压缩结果51             data: result52           }53           return res.json(reObj);54         }55         return res.json(errStatus);56       });57     }else{58       return res.json(errStatus);59     }60   });61   62   63 }

五、canvas绘制

(1)封装了个简单的ajax工具类

 1 /* 2  * 描述:提供基本的AJAX、JSON、事件绑定 3  * 时间:2015-04-14 4  * */ 5 ;(function(exports){ 6    7   //JSON 8   function jsonParse(data){ 9     if(JSON){10       return JSON.parse(data);11     }else{12       return (new Function('return' + data))();13     }14   }15   16   //AJAX17   function ajax(options, callback){18     var method = options.method || 'GET';19     var url = options.url || '';20     var null;21     22     if(window.23       new 24     }25     26     if(window.ActiveXObject){27       new ActiveXObject("Microsoft.);28     }29     30     true);31     function(){32       if(){33         callback(jsonParse(34       }35     }36     37   }38   39   //事件绑定40   function addEvent(el, type, callback){41     if(!el){42       return;43     }44     if(el.addEventListener){45       el.addEventListener(type, callback);46     }else{47       el.attachEvent('on' + type, callback);48     }49     return callback;50   }51   52   //Tip53   var ID_STRING = '_____wlh_tip___0088';54   function tipShow(){55     var body = document.getElementsByTagName('body')[0];56     if(document.getElementById(ID_STRING)){57       document.getElementById(ID_STRING).style.display = 'block';58       body.style.overflow = 'hidden';59       return;60     }61     var el = document.createElement('div');62     el.style.zIndex = 1000;63     el.style.width = '300px';64     el.style.height = '150px';65     el.style.backgroundColor = '#FFF';66     el.style.position = 'fixed';67     el.style.left = '30%';68     el.style.top = '40%';69     el.style.opacity = 0.9;70     el.style.color = '#00B7FF';71     el.style.lineHeight = '150px';72     el.style.fontSize = '16px';73     el.style.paddingLeft = '20px';74     el.style.borderRadius = '3px';75     el.innerHTML = '数据正在处理, 请等待......';76     el.id = ID_STRING;77     78     body.style.overflow = 'hidden';79     body.appendChild(el);80     return;81   }82   83   function tipHide(){84     var body = document.getElementsByTagName('body')[0];85     if(document.getElementById(ID_STRING)){86       document.getElementById(ID_STRING).style.display = 'none';87       body.style.overflow = 'auto';88     }89   }90   91   exports.ajax = ajax;92   exports.addEvent = addEvent;93   exports.tipShow = tipShow;94   exports.tipHide = tipHide;95   96 })(window);

View Code

(2)canvas图层叠加绘制

 1 ;(function(exports, require){ 2   var canvas = document.getElementsByTagName('canvas')[0]; 3   var context = canvas.getContext('2d'); 4   var ajax = require.ajax; 5   var addEvent = require.addEvent; 6    7   //缩放比例 8   var SCALE_BIG = 1; 9   var SCALE_SML = 0.25; 10    11   //颜色对应表 12   var COLOR_LIST = { 13     BODY: '#63CEF6', 14     DUPI: '#FFF', 15     EYE: '#034E68', 16     ZUIBA: '#0089B5', 17     TOUQUAN: '#BDF9FB', 18     JIUWO: '#FFBFE3', 19     QIPAO: '#C9E8FB', 20     BISHANG: '#46BEEF', 21     BIXIA: '#54C5F2', 22     GO: '#B1DBF2' 23   }; 24    25   if(!context){ 26     return; 27   } 28    29   context.scale(SCALE_SML, SCALE_SML); 30    31   //simple package design pattern 32   function addDrawFunc(data, part, color, fun){ 33     var xys = data[part]; 34     context.beginPath(); 35     context.strokeStyle = color;  36     context.lineWidth = 1; 37     context.fillStyle = color; 38     fun(xys); 39     context.stroke(); 40     context.fill(); 41     context.closePath(); 42   } 43   //draw polygon 44   function drawPolygon(data, part, color){     45     addDrawFunc(data, part, color, function(xys){ 46       context.moveTo(xys[0], xys[1]); 47       for(var i = 2; i < xys.length; i++){ 48         if(i % 2 !== 0){ 49           context.lineTo(xys[i-1], xys[i]); 50         } 51       } 52     }); 53   } 54    55   //draw eye 56   function drawEye(data, part, color){ 57     addDrawFunc(data, part, color, function(xys){ 58       //变形 59        context.save(); 60       context.scale(1.5, 2); 61       context.arc(xys[0]/1.5, xys[1]/2, 6, 0, Math.PI * 2, false); 62       context.restore(); 63      }); 64   } 65    66   //drawCircle 67   function drawCircle(data, part, color, radius){ 68     addDrawFunc(data, part, color, function(xys){ 69       context.arc(xys[0], xys[1], radius, 0, Math.PI * 2, false); 70      }); 71   } 72    73    74    75   //draw line 76   function drawLine(data, part, color, width){ 77     var xys = data[part]; 78     context.beginPath(); 79     context.strokeStyle = color;  80     context.lineWidth = width; 81     context.moveTo(xys[0], xys[1]); 82     for(var i = 0; i < xys.length; i++){ 83       if(i % 2 !== 0){ 84         context.lineTo(xys[i-1], xys[i]); 85       } 86     } 87     context.stroke(); 88     context.closePath(); 89   } 90    91   //draw text 92   function drawText(x, y){ 93     context.beginPath(); 94     context.fillStyle = '#FFFFFF'; 95     context.font = '40px arial,sans-serif'; 96     context.fillText('go...', x, y) 97     context.fill(); 98     context.closePath(); 99   }100   101   102   //绘制103   function draw(fileName){104     var url = 'http://127.0.0.1:3000/get?dirName=dist&fileName=' + fileName;105     ajax({method: 'GET', url: url}, function(data){106       data = data.data;107       drawPolygon(data, 'polygon_body', COLOR_LIST.BODY);108       drawPolygon(data, 'polygon_dupi', COLOR_LIST.DUPI);109       drawPolygon(data, 'polygon_touquan', COLOR_LIST.TOUQUAN);110       drawPolygon(data, 'polygon_jiuwo1', COLOR_LIST.JIUWO);111       drawPolygon(data, 'polygon_jiuwo2', COLOR_LIST.JIUWO);112       drawPolygon(data, 'polygon_go', COLOR_LIST.GO);113       114       drawEye(data, 'circle_eye1', COLOR_LIST.EYE);115       drawEye(data, 'circle_eye2', COLOR_LIST.EYE);116       117       drawLine(data, 'line_zui', COLOR_LIST.ZUIBA, 4);118       drawLine(data, 'line_bishang', COLOR_LIST.BISHANG, 8);119       drawLine(data, 'line_bixia', COLOR_LIST.BIXIA, 5);120       121       drawCircle(data, 'circle_qipao1', COLOR_LIST.QIPAO, 30);122       drawCircle(data, 'circle_qipao2', COLOR_LIST.QIPAO, 25);123       drawCircle(data, 'circle_qipao3', COLOR_LIST.QIPAO, 15);124       drawCircle(data, 'circle_qipao4', COLOR_LIST.QIPAO, 20);125       drawCircle(data, 'circle_qipao5', COLOR_LIST.QIPAO, 15);126       drawCircle(data, 'circle_qipao6', COLOR_LIST.QIPAO, 10);127       drawCircle(data, 'circle_qipao7', COLOR_LIST.QIPAO, 16);128     129       drawText(320, 69);130     });131   }132   133   draw('dist_3');134   135   136 })(window, window);

六、代码 & 实例

代码: https://github.com/vczero/image-vector

实例:http://vczero.github.io/ctrip/index.html

 




去重庆旅游要多少钱去重庆旅游最佳路线去重庆旅游最佳时间重庆旅游线路报价重庆旅游攻略大全江南水乡六大古镇_简介_交通以及图片欣赏 十渡门票_北京十渡门票价格_十渡门票多少钱_十渡门票价格 承德避暑山庄在哪里_避暑山庄位于哪个省 798艺术区地址_北京798艺术区在哪里 韩国顺天活动周今日在世园会上演 李连贵熏肉大饼 世园会里珍贵小鸟成明星赢得游人喝彩 免税岛PK自由港_谁才是真正的购物天堂 富华游乐园有表演吗?潍坊富华游乐园表演节目有什么? 富华游乐园滑车多少钱?潍坊富华游乐园滑车好玩吗? 富华游乐园旋转木马好玩吗?潍坊富华游乐园旋转木马介绍? 浮盖山峡谷漂流全程多长时间?江山浮盖山峡谷漂流全程能漂多久? 2015深圳欢乐海岸圣诞节夜场活动门票预订价格?欢乐海岸圣诞夜场门票多少钱? 2015东部华侨城圣诞节夜场活动时间?深圳东部华侨城圣诞夜场几时开始? 2015东部华侨城圣诞节夜场活动门票预订价格?深圳东部华侨城圣诞夜场门票多少钱? 2015广州长隆大马戏圣诞节夜场活动时间?长隆大马戏圣诞夜场几时开始? 2SK4016(Q) Datasheet 2SK4016(Q) Datasheet FCP13N60N Datasheet FCP13N60N Datasheet STP13NK60Z Datasheet STP13NK60Z Datasheet 沙巴岛旅游多少钱 沙巴岛旅游多少钱 沙巴岛旅游多少钱 一代身份证能买火车票吗 一代身份证能买火车票吗 一代身份证能买火车票吗 退票扣多少钱 退票扣多少钱 退票扣多少钱