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

前言

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

效果预览

前置知识

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

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

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

整体思路

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

  • 从前端页面接收设计稿图片及需要截取的网站地址与节点信息
  • 将设计稿保存到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作者的博客,不定时更新中——

参考文献

总结

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

(0)

相关推荐

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

    前言 本次分享一个提供设计稿与前端页面进行像素对比的node服务,旨在为测试或者前端人员自己完成一个辅助性测试.相信我,在像素级别的对比下,网页对设计稿的还原程度一下子就会凸显出来.下面话不多说了,来一起看看详细的介绍吧. 效果预览 前置知识 本次用到了以下两个库作为辅助工具: casperjs:基于PhantomJS的编写.其内部提供了一个无界面浏览器,简单来说用它你可以以代码的形式来完成模拟人来操作浏览器的操作,其中涉及鼠标各种事件,等等非常多的功能,本次主要使用其附带的截图功能. rese

  • JS实现一个文件选择组件详解

    目录 前言 插件安装 插件使用 参数说明 前言 花了点时间利用广度与深度优先搜索算法实现了一个文件选择插件,支持无限层次的文件夹嵌套,已开源并打包上传到了npm. 本文将跟大家分享一下这个插件,欢迎各位感兴趣的开发者阅读本文. 插件安装 yarn add file-folder-selector # or npm install file-folder-selector --save 插件使用 在你需要使用此插件的业务代码中导入插件. <script setup lang="ts"

  • 基于JS脚本语言的基础语法详解

    JS脚本语言的基础语法:输出语法  alert("警告!");  confirm("确定吗?");   prompt("请输入密码");为弱类型语言: 开始时要嵌入JS代码:<script type="text/javascript"></script>: 关于写程序是需注意的基本语法: 1.所有的字符全都是英文半角的: 2.大部分情况下每条语句结束后要加分号: 3.每一块代码结束后加换行:4.程序前呼

  • 基于js对象,操作属性、方法详解

    一,概述 在Java语言中,我们可以定义自己的类,并根据这些类创建对象来使用,在Javascript中,我们也可以定义自己的类,例如定义User类.Hashtable类等等. 目前在Javascript中,已经存在一些标准的类,例如Date.Array.RegExp.String.Math.Number等等,这为我们编程提供了许多方便.但对于复杂的客户端程序而言,这些还远远不够. 与Java不同,Java2提供给我们的标准类很多,基本上满足了我们的编程需求,但是Javascript提供的标准类很

  • Svg.js实例教程及使用手册详解(一)

    什么是SVG? SVG 指可伸缩矢量图形 (Scalable Vector Graphics) SVG 用来定义用于网络的基于矢量的图形 SVG 使用 XML 格式定义图形 SVG 图像在放大或改变尺寸的情况下其图形质量不会有所损失 SVG 是万维网联盟的标准 SVG 与诸如 DOM 和 XSL 之类的 W3C 标准是一个整体 简介: SVG.js是一个轻量级的JavaScript库,允许你轻松操作SVG和定义动画. SVG(Scalable Vector Graphics,可缩放矢量图形)是基

  • Nuxt.js开启SSR渲染的教程详解

    第一节:nuxt.js相关概述 nuxt.js简单的说是Vue.js的通用框架,最常用的就是用来作SSR(服务器端渲染).Vue.js是开发SPA(单页应用)的,Nuxt.js这个框架,用Vue开发多页应用,并在服务端完成渲染,可以直接用命令把我们制作的vue项目生成为静态html. 1.那服务器端渲染到底有什么好处呢? 主要的原因时SPA(单页应用)不利于搜索引擎的SEO操作,Nuxt.js适合作新闻.博客.电影.咨询这样的需要搜索引擎提供流量的项目.如果你要作移动端的项目,就没必要使用这个框

  • Android Bitmap像素级操作详解

    一:什么是Bitmap像素级的操作 相信大家都知道一张jpg或png放大后会是一个个小格子,称为一个像素(px),而且一个小格子是一种颜色,也就是一张jpg或png图片就是很多颜色的合集,而这些合集信息都被封装到了Bitmap类中.你可以使用Bitmap获取任意像素点,并修改它,对与某像素点而言,颜色信息是其主要的部分.所以像素级操作就是对一个个点的颜色超过. 二:载入与像素读写 在Android SDK中,图像的像素读写能够通过getPixel与setPixel两个Bitmap的API实现.

  • Vue.js实现watch属性的示例详解

    目录 1.写在前面 2.watch的实现原理 3.立即执行的watch与回调执行时机 立即执行的回调函数 回调函数的执行时机 4.过期的副作用函数和cleanup 5.写在最后 1.写在前面 在上篇文章中,我们讨论了compted的实现原理,就是利用effect和options参数进行封装.同样的,watch也是基于此进行封装的,当然watch还可以传递第三参数去清理过期的副作用函数.不仅可以利用副作用函数的调度性,去实现回调函数的立即执行,也可以控制回调函数的执行时机. 2.watch的实现原

  • Node.js完整实现博客系统详解

    目录 一.项目功能说明 二.最终效果 三.文件目录结构说明 四.项目技术栈 五.核心技术 1. 使用Schema定义数据模型 2. mongoose 的操作 3. mogodb数据库的操作 4. 使用第三方插件 express-session:存取数据状态 5. 挂载路由 6. 使用md5对密码进行加密 六.遇到的问题 七.github链接 一.项目功能说明 登录.注册 新建博客 首页显示全部博客 查看博客详情页 查看博客评论区 修改.删除博客 二.最终效果 首页: 登录.注册: 详情页: 评论

  • Xterm.js入门官方文档示例详解

    目录 前言 xterm.js是什么? 安装 初始化 使用插件 API文档模块 类 Terminal 构造函数 constructor 接口 插件 attach插件 前后端示例 结语 前言 入职的新公司所在的事业部专注于K12的编程教育.公司项目里有使用xterm.js这个库, 并基于master分支做出了一定的修改.为了尽快的熟悉业务以及公司的代码, 所以这里打算学习xterm.js的文档(粗略的翻译, 方便自己查阅, 凡是保留原文的地方, 是我目前还没有明白具体使用场景和用法的地方) xter

随机推荐