基于casperjs和resemble.js实现一个像素对比服务详解


Posted in Javascript onJanuary 10, 2018

前言

本次分享一个提供设计稿与前端页面进行像素对比的node服务,旨在为测试或者前端人员自己完成一个辅助性测试。相信我,在像素级别的对比下,网页对设计稿的还原程度一下子就会凸显出来。下面话不多说了,来一起看看详细的介绍吧。

效果预览

基于casperjs和resemble.js实现一个像素对比服务详解

前置知识

本次用到了以下两个库作为辅助工具:

  • casperjs:基于PhantomJS的编写。其内部提供了一个无界面浏览器,简单来说用它你可以以代码的形式来完成模拟人来操作浏览器的操作,其中涉及鼠标各种事件,等等非常多的功能,本次主要使用其附带的截图功能。
  • resemble.js:图片像素对比工具。调用方法简单理解为,传入两张图,返回一张合成图并附带对比参数如差别度等等。基本实现思路可以理解为通过将图片转为canvas后,获取其图像像素点,之后对每个像素点进行一次比对。

所以整个服务我们应该已经有了大题的思路即通过casperjs来进入某个网站截取某个页面,再将其与设计图进行比对得出结果。

整体思路

基于casperjs和resemble.js实现一个像素对比服务详解

通过上图我们应该能整理出一个大概的流程:

  • 从前端页面接收设计稿图片及需要截取的网站地址与节点信息
  • 将设计稿保存到images文件夹
  • 开启子进程,启动casperjs,完成对目标网站的截取
  • 截取后请求form.html将图片地址信息填入并重新传回服务器
  • 服务端获取图片信息通过resemblejs将截取图与设计稿进行比对
  • 结果传回前端页面

这其中有一个问题可能会有人注意到就是:为什么在casperjs中对目标网站截图了不能直接把信息传回服务器中,而是选择了再去打开一个表单页面通过表单的形式来提交信息?

答:首先我对casperjs和node了解都不那么深入,我理解的是首先casperjs不是一个node模块,它是跑在操作系统中的,我尚且没有发现怎么在casperjs中建立与node服务的通信,如果有方法一定要告诉我,因为我真的不太了解casper!其次由于无法建立通信,我只能退而求其次,通过casper快速打开一个我写好的表单页面并且填写好图片信息传回服务器,这么做是可以完成最初的诉求。所以就有了上面from.html那段的操作。

实现细节

实现一个简易静态服务器

因为涉及到index.html与form.html页面的返回,故需要实现一个超级简易的静态服务器。代码如下:

const MIME_TYPE = {
 "css": "text/css",
 "gif": "image/gif",
 "html": "text/html",
 "ico": "image/x-icon",
 "jpeg": "image/jpeg",
 "jpg": "image/jpg",
 "js": "text/javascript",
 "json": "application/json",
 "pdf": "application/pdf",
 "png": "image/png",
 "svg": "image/svg+xml",
 "swf": "application/x-shockwave-flash",
 "tiff": "image/tiff",
 "txt": "text/plain",
 "wav": "audio/x-wav",
 "wma": "audio/x-ms-wma",
 "wmv": "video/x-ms-wmv",
 "xml": "text/xml"
}
function sendFile(filePath, res) {
 fs.open(filePath, 'r+', function(err){ //根据路径打开文件
  if(err){
   send404(res)
  }else{
   let ext = path.extname(filePath)
   ext = ext ? ext.slice(1) : 'unknown'
   let contentType = MIME_TYPE[ext] || "text/plain" //匹配文件类型
   fs.readFile(filePath,function(err,data){
    if(err){
     send500(res)
    }else{
     res.writeHead(200,{'content-type':contentType})
     res.end(data)
    }
   })
  }
 })
}

解析表单并将图片存储到images文件夹

const multiparty = require('multiparty') //解析表单
let form = new multiparty.Form()
 form.parse(req, function (err, fields, files) {
  let filename = files['file'][0].originalFilename,
   targetPath = __dirname + '/images/' + filename,
  if(filename){
   fs.createReadStream(files['file'][0].path).pipe(fs.createWriteStream(targetPath))
   ...
  } 
 })

通过创建可读流读出文件内容,再通过pipe写入到制定路径下即可保存上传来的图片。

运行casperjs

const { spawn } = require('child_process')
spawn('casperjs', ['casper.js', filename, captureUrl, selector, id])
casperjs.stdout.on('data', (data) => {
 ...
})

通过spawn可以创建子进程来启动casperjs,同样也可以使用exec等。

截图并提交数据到form.html

const system = require('system')
const host = 'http://10.2.45.110:3033'
const casper = require('casper').create({
 // 浏览器窗口大小
 viewportSize: {
  width: 1920,
  height: 4080
 }
})
const fileName = decodeURIComponent(system.args[4])
const url = decodeURIComponent(system.args[5])
const selector = decodeURIComponent(system.args[6])
const id = decodeURIComponent(system.args[7])
const time = new Date().getTime()
casper.start(url)
casper.then(function() {
  console.log('正在截图请稍后')
  this.captureSelector('./images/casper'+ id + time +'.png', selector)
})
casper.then(function() {
 casper.start(host + '/form.html', function() {
  this.fill('form#contact-form', {
   'diff': './images/casper'+ id + time +'.png',
   'point': './images/' + fileName,
   'id': id
  }, true)
 })
})
casper.run()

代码还是比较简单的,主要过程就是打开一个页面,然后在then中传入你的操作,最后执行run。在这个过程里我不太知道如何与node服务通信,故选择了再开一个页面。。想深入研究的可以去看casperjs的官网非常详尽!

通过resemble.js进行像素比对并返回数据

function complete(data) {
  let imgName = 'diff'+ new Date().getTime() +'.png',
   imgUrl,
   analysisTime = data.analysisTime,
   misMatchPercentage = data.misMatchPercentage,
   resultUrl = './images/' + imgName
  fs.writeFileSync(resultUrl, data.getBuffer())
  imgObj = {
   ...
  }
  let resEnd = resObj[id] // 找回最开始的res返回给页面数据
  resEnd.writeHead(200, {'Content-type':'application/json'})
  resEnd.end(JSON.stringify(imgObj))
 }
let result = resemble(diff).compareTo(point).ignoreColors().onComplete(complete)

这其中涉及到了一个点,即我现在所得到的结果要返回给最初的请求里,而从一开始的请求到现在我已经中转了多次,导致我现在找不到我最初的返回体res了。想了很久只能暂时采用了设定全局对象,在接收最初的请求后将请求者的ip和时间戳设定为唯一id存为该对象的key,value为当前的res。同时整个中转流程中时刻传递id,最后通过调用resObj[id]来得到一开始的返回体,返回数据。这个方法我不认为是最优解,但是鉴于我现在想不出来好方法为了跑通整个服务不得已。。如果有新的思路请务必告知!!

部署

安装PhantomJS(osx)

官网下载: phantomjs-2.1.1-macosx.zip

解压路径:/User/xxx/phantomjs-2.1.1-macosx

添加环境变量:~/.bash_profile 文件中添加

export PATH="$PATH:/Users/xxx/phantomjs-2.1.1-macosx/bin"

terminal输入:phantomjs --version

能看到版本号即安装成功

安装casperjs

brew update && brew install casperjs

安装resemble.js

cnpm i resemblejs //已写进packjson可不用安装
brew install pkg-config cairo libpng jpeg giflib
cnpm i canvas //node内运行canvas

node服务

git clone https://github.com/Aaaaaaaty/gui-auto-test.git
cd gui-auto-test
cnpm i
cd pxdiff
nodemon server.js

打开http://localhost:3033/index.html

惯例po作者的博客,不定时更新中——

参考文献

  • PhantomJS 安装
  • casperjs 文档
  • resemble.js 文档

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
UI Events 用户界面事件
Jun 27 Javascript
Bootstrap每天必学之栅格系统(布局)
Nov 25 Javascript
简单对比分析JavaScript中的apply,call与this的使用
Dec 04 Javascript
JavaScript计划任务后台运行的方法
Dec 18 Javascript
JavaScript深度复制(deep clone)的实现方法
Feb 19 Javascript
微信小程序之ES6与事项助手的功能实现
Nov 30 Javascript
利用NPM淘宝的node.js镜像加速nvm
Mar 27 Javascript
小程序使用watch监听数据变化的方法详解
Sep 20 Javascript
layui扩展上传组件模拟进度条的方法
Sep 23 Javascript
JS+CSS实现随机点名(实例代码)
Nov 04 Javascript
vue中js判断长时间不操作界面自动退出登录(推荐)
Jan 22 Javascript
JavaScript canvas绘制圆弧与圆形
Feb 18 Javascript
JavaScript实现快速排序的方法分析
Jan 10 #Javascript
jQuery第一次运行页面默认触发点击事件的实例
Jan 10 #jQuery
js推箱子小游戏步骤代码解析
Jan 10 #Javascript
vue select二级联动第二级默认选中第一个option值的实例
Jan 10 #Javascript
AngularJS使用ui-route实现多层嵌套路由的示例
Jan 10 #Javascript
Vue+jquery实现表格指定列的文字收缩的示例代码
Jan 09 #jQuery
基于Vue、Vuex、Vue-router实现的购物商城(原生切换动画)效果
Jan 09 #Javascript
You might like
全国FM电台频率大全 - 2 天津市
2020/03/11 无线电
虹吸式咖啡壶操作
2021/03/03 冲泡冲煮
从网上搜到的phpwind 0day的代码
2006/12/07 PHP
php动态生成JavaScript代码
2009/03/09 PHP
如何使用Linux的Crontab定时执行PHP脚本的方法
2011/12/19 PHP
PHP+Apache+Mysql环境搭建教程
2016/08/01 PHP
让Firefox支持event对象实现代码
2009/11/07 Javascript
Jquery Ajax学习实例5 向WebService发出请求,返回泛型集合数据的异步调用
2010/03/17 Javascript
javascript 精粹笔记
2010/05/09 Javascript
js ondocumentready onmouseover onclick onmouseout 样式
2010/07/22 Javascript
js获取url中"?"后面的字串方法
2014/05/15 Javascript
Jquery选择器中使用变量实现动态选择例子
2014/07/25 Javascript
瀑布流的实现方式(原生js+jquery+css3)
2020/06/28 Javascript
js判断所有表单项不为空则提交表单的实现方法
2016/09/09 Javascript
jQuery自定义插件详解及实例代码
2016/12/29 Javascript
vue 2.0路由之路由嵌套示例详解
2017/05/08 Javascript
js通过Date对象实现倒计时动画效果
2017/10/27 Javascript
vue2实现可复用的轮播图carousel组件详解
2017/11/27 Javascript
vue组件库的在线主题编辑器的实现思路
2020/04/03 Javascript
Python中使用dom模块生成XML文件示例
2015/04/05 Python
Python实现字符串的逆序 C++字符串逆序算法
2020/05/28 Python
python频繁写入文件时提速的方法
2019/06/26 Python
对tensorflow中的strides参数使用详解
2020/01/04 Python
Python3之乱码\xe6\x97\xa0\xe6\xb3\x95处理方式
2020/05/11 Python
世界上最大的在线旅行社新加坡网站:Expedia新加坡
2016/08/25 全球购物
数控专业应届生求职信
2013/11/27 职场文书
学前教育毕业生自荐信范文
2013/12/24 职场文书
保护环境建议书100字
2014/05/13 职场文书
六一儿童节活动总结
2014/08/27 职场文书
科学发展观演讲稿
2014/09/11 职场文书
2014领导班子正风肃纪思想汇报
2014/09/18 职场文书
个人工作作风整改措施思想汇报
2014/10/13 职场文书
2014年工会工作总结
2014/11/12 职场文书
锅炉工岗位职责
2015/02/13 职场文书
windows安装python超详细图文教程
2021/05/21 Python
JavaScript与JQuery框架基础入门教程
2021/07/15 Javascript