js怎么实现将截图,或者复制的图片用ctrl+v

JavaScript06

js怎么实现将截图,或者复制的图片用ctrl+v,第1张

原理分析

提取操作:复制=>粘贴=>上传

在这个操作过程中,我们需要做的就是:监听粘贴事件=>获取剪贴板里的内容=>发请求上传

为方便理解下文,需要先明白几点:

我们只能上传网页图(在网页上右键图片,然后复制)和截图(截图工具截的图片,eg:qq截图),不能粘贴上传系统里的图片(从桌面上、硬盘里复制),他们是存在完全不同的地方的。截图工具截的图与在网页点击右键复制的图是有些不同的,因此处理方式也不一样。知悉paste event这个事件:当进行粘贴(右键paste/ctrl+v)操作时,该动作将触发名为'paste'的剪贴板事件,这个事件的触发是在剪贴板里的数据插入到目标元素之前。如果目标元素(光标所在位置)是可编辑的元素(eg:设置了contenteditable属性的div。textarea并不行。),粘贴动作将把剪贴板里的数据,以最合适的格式,插入到目标元素里;如果目标元素不可编辑,则不会插入数据,但依然触发paste event。数据在粘贴的过程中是只读的。此段是翻译于w3.org_the-paste-action。可惜的是,经过试验,发现chrome(当前最新版)、firefox(当前最新版)、ie11对paste事件的实现并不是完全按照w3c来的,各自也有区别(w3c的paste标准也因此只是草案阶段)。

test代码如下:

chrome:

<textarea ></textarea> <div contenteditable style="width: 100pxheight: 100pxborder:1px solid"> </div> <script> document.addEventListener('paste', function (event) { console.log(event) }) </script>

event有clipboardData属性,且clipboardData有item属性,clipboardData.item中的元素(对象)有type和kind属性;无论在哪进行粘贴,均可触发paste事件;在div(未特殊声明时,本文div均指设置了contenteditable属性的div) 里粘贴截图,不显示图片。img.src为base64编码字符串;在div里粘贴网页图片,直接显示图片,img.src为图片地址。

firefox:

event有clipboardData属性,clipboardData没有item属性;只有在textarea里或者可编辑的div(里才粘贴才触发paste事件;在div里粘贴截图,直接显示图片,img.src为base64编码字符串;在div里粘贴网页图片,表现同chrome。

ie11:(不截图了,可自行试验,其他浏览器同理<( ̄▽ ̄)/,因为懒...)

event没有clipboardData属性;只在可编辑的div中粘贴才触发paste事件;在div里粘贴截图,直接显示图片,img.src为base64编码字符串;在div里粘贴网页图片,表现同chrome。

监听了paste事件,也知道了表现形式,接下来就是如何获取数据了:

chrome有特定的方法,利用clipboardData.items、getAsFile()、new FileReader()等api可以在paste回调函数里获取到剪贴板里图片的base64编码字符串(无论是截图粘贴的还是网页图片复制粘贴的),ie11,firefox没有这样的api,不过依然有办法可以获取,因为数据已经表现在img的src里了,对于截图粘贴的,直接取img的src属性值(base64),对于网页粘贴的,则把地址传给后台,然后根据地址down下来,存在自己的服务器,最后把新地址返回来交给前端展示就ok了。为了保持一致性便于管理,统一将所有情况(截图、网页)中的img的src属性替换为自己存储的地址。因此可以得到以下核心代码(注释很全哦~~):

html展示:

<head> <meta charset="UTF-8"> <title>Document</title> <style> body {display: -webkit-flexdisplay: flex -webkit-justify-content: center justify-content: center } #tar_box {width: 500px height: 500px border: 1px solid red } </style>

前端js处理逻辑:

document.addEventListener('paste', function (event) {console.log(event)var isChrome = false if ( event.clipboardData || event.originalEvent ) { //not for ie11 某些chrome版本使用的是event.originalEvent var clipboardData = (event.clipboardData || event.originalEvent.clipboardData) if ( clipboardData.items ) {// for chromevar items = clipboardData.items, len = items.length, blob = null isChrome = true //items.length比较有意思,初步判断是根据mime类型来的,即有几种mime类型,长度就是几(待验证)//如果粘贴纯文本,那么len=1,如果粘贴网页图片,len=2, items[0].type = 'text/plain', items[1].type = 'image/*'//如果使用截图工具粘贴图片,len=1, items[0].type = 'image/png'//如果粘贴纯文本+HTML,len=2, items[0].type = 'text/plain', items[1].type = 'text/html'// console.log('len:' + len) // console.log(items[0]) // console.log(items[1]) // console.log( 'items[0] kind:', items[0].kind ) // console.log( 'items[0] MIME type:', items[0].type ) // console.log( 'items[1] kind:', items[1].kind ) // console.log( 'items[1] MIME type:', items[1].type ) //阻止默认行为即不让剪贴板内容在div中显示出来event.preventDefault() //在items里找粘贴的image,据上面分析,需要循环 for (var i = 0i <leni++) { if (items[i].type.indexOf("image") !== -1) {// console.log(items[i]) // console.log( typeof (items[i])) //getAsFile() 此方法只是living standard firefox ie11 并不支持blob = items[i].getAsFile() }}if ( blob !== null ) { var reader = new FileReader() reader.onload = function (event) {// event.target.result 即为图片的Base64编码字符串var base64_str = event.target.result//可以在这里写上传逻辑 直接将base64编码的字符串上传(可以尝试传入blob对象,看看后台程序能否解析)uploadImgFromPaste(base64_str, 'paste', isChrome) } reader.readAsDataURL(blob)} } else {//for firefoxsetTimeout(function () { //设置setTimeout的原因是为了保证图片先插入到div里,然后去获取值 var imgList = document.querySelectorAll('#tar_box img'),len = imgList.length,src_str = '',i for ( i = 0i <leni ++ ) {if ( imgList[i].className !== 'my_img' ) { //如果是截图那么src_str就是base64 如果是复制的其他网页图片那么src_str就是此图片在别人服务器的地址 src_str = imgList[i].src } } uploadImgFromPaste(src_str, 'paste', isChrome) }, 1) }} else { //for ie11 setTimeout(function () {var imgList = document.querySelectorAll('#tar_box img'), len = imgList.length, src_str = '', i for ( i = 0i <leni ++ ) { if ( imgList[i].className !== 'my_img' ) {src_str = imgList[i].src }}uploadImgFromPaste(src_str, 'paste', isChrome) }, 1) } })function uploadImgFromPaste (file, type, isChrome) {var formData = new FormData() formData.append('image', file) formData.append('submission-type', type) var xhr = new XMLHttpRequest() xhr.open('POST', '/upload_image_by_paste') xhr.onload = function () { if ( xhr.readyState === 4 ) {if ( xhr.status === 200 ) { var data = JSON.parse( xhr.responseText ),tarBox = document.getElementById('tar_box') if ( isChrome ) {var img = document.createElement('img') img.className = 'my_img' img.src = data.store_path tarBox.appendChild(img) } else {var imgList = document.querySelectorAll('#tar_box img'), len = imgList.length, i for ( i = 0i <leni ++) { if ( imgList[i].className !== 'my_img' ) {imgList[i].className = 'my_img' imgList[i].src = data.store_path }} } } else { console.log( xhr.statusText ) } } } xhr.onerror = function (e) { console.log( xhr.statusText ) }xhr.send(formData) }

用express.js搭的简易后台的接收逻辑:

router.post('/', upload.array(), function (req, res, next) { //1.获取客户端传来的src_str字符串=>判断是base64还是普通地址=>获取图片类型后缀(jpg/png etc) //=>如果是base64替换掉"前缀"("data:image\/pngbase64," etc) //2.base64 转为 buffer对象 普通地址则先down下来 //3.写入硬盘(后续可以将地址存入数据库) //4.返回picture地址 var src_str = req.body.image,timestamp = new Date().getTime() if ( src_str.match(/^data:image\/pngbase64,|^data:image\/jpgbase64,|^data:image\/jpgbase64,|^data:image\/bmpbase64,/) ) {//处理截图 src_str为base64字符串var pic_suffix = src_str.split('',1)[0].split('/',2)[1], base64 = src_str.replace(/^data:image\/pngbase64,|^data:image\/jpgbase64,|^data:image\/jpgbase64,|^data:image\/bmpbase64,/, ''), buf = new Buffer(base64, 'base64'), store_path = 'public/images/test_' + timestamp + '.' + pic_suffix fs.writeFile(store_path, buf, function (err) { if (err) {throw err } else {res.json({'store_path': store_path}) }}) } else {// 处理非chrome的网页图片 src_str为图片地址var temp_array = src_str.split('.'), pic_suffix = temp_array[temp_array.length - 1], store_path = 'public/images/test_' + timestamp + '.' + pic_suffix, wstream = fs.createWriteStream(store_path) request(src_str).pipe(wstream) wstream.on('finish', function (err) { if( err ) {throw err } else {res.json({"store_path": store_path}) }}) } })

需要node环境:安装node=>npm intall=>node app.js)

可以做得到。注明:内容非原创。学习javascript的时候觉得好就保存下来的文章:

html2canvas是一个相当不错的JavaScript类库,它使用了html5和css3的一些新功能特性,实现了在客户端对网页进行截图的功

能。html2canvas通过获取页面的DOM和元素的样式信息,并将其渲染成canvas图片,从而实现给页面截图的功能。

它不需要来自服务器任何渲染,整张图片都是在客户端浏览器创建。当浏览器不支持Canvas时,将采用Flashcanvas或

ExplorerCanvas技术代替实现。以下浏览器能够很好的支持该脚本:Firefox 3.5+, Google Chrome,

Opera新的版本, IE9以上的浏览器。

因为每个浏览器渲染页面的方式都不尽相同,所以生成的图片也不太一样。虽然它目前还是处理开发阶段,但还是很值得期待的。

这个插件依赖于jQuery插件,建议使用最新版的。

兼容性

html2canvas能在以下的浏览器中完美的运行。

- Firefox 3.5+

- Chrome

- Opera

- IE9使用方法

1、引入文件

首先我们需要引入jQuery库和html2canvas插件文件,注意html2canvas插件文件一定要在jQuery的后面引入,不然会报错。

<script type="text/javascript"

src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>

<script type="text/javascript"

src="http://html2canvas.hertzen.com/build/html2canvas.js"></script>

2、调用插件

通过html2canvas方法调用插件,该方法接受两个参数,第一个为要生成截图的DOM元素,第二个参数为插件的配置

html2canvas(document.body, { allowTaint: true, taintTest: false,

onrendered: function(canvas) { canvas.id = "mycanvas"

//document.body.appendChild(canvas) //生成base64图片数据 var dataUrl =

canvas.toDataURL() var newImg = document.createElement("img")

newImg.src = dataUrl document.body.appendChild(newImg) } })

通过一个回调函数来处理获取到的截图。

图片跨域问题

在网页的图片中,如果有跨域图片,调用toDataURL的时候会出错

SecurityError: The operation is insecure.

解决方法是在跨域的服务器上设置header设置为允许跨域请求

access-control-allow-origin: *  access-control-allow-credentials: true

官网主页: http://html2canvas.hertzen.com/

本站文章除注明转载外,均为本站原创或翻译,欢迎任何形式的转载,但请务必注明出处,尊重他人劳动共创和谐网络环境。

转载请注明:文章转载自:问说 » html2canvas根据DOM元素样式实现网页截图

本文标题:html2canvas根据DOM元素样式实现网页截图

本文地址:http://www.uedsc.com/html2canvas.html

原文链接:http://www.uedsc.com/html2canvas.html

使用node-webshot进行网页截图

用到的npm模块有yargs和node-webshot,关于yargs的文章参考这里 从零开始打造个人专属命令行工具集——yargs完全指南 。

node-webshot是调用phantomjs来生成网页截图的,phantomjs是非常有名的npm项目,相当于一个脚本版的WebKit浏览器 ,通过phantomjs可以使用脚本 和网页进行交互,所以phantomjs经常用来进行网页自动化测试。

phantomjs会和普通的浏览器一样加载完整的网页内容,然后在内存里面进行渲染,虽然肉眼看不到它渲染的页面,但是通过生成图片就可以看到了,node-webshot使用的就是phantomjs的render接口来获取网页截图的。

node-webshot生成谷歌首页的示例代码:

var webshot = require('webshot') webshot('google.com', 'google.png', function(err) { // screenshot now saved to google.png})phantomjs生成谷歌首页的示例代码: var page = require('webpage').create()page.open('http://github.com/', function() { page.render('github.png')phantom.exit()})

那为什么不直接使用phantomjs呢?一个字懒!

另外node-webshot还对文件读写进行了简单的封装,相信任何熟悉node.js的开发人员,都能很简单的写出这样的接口,但是既然轮子好用就不要自己造了。

node-webshot流式调用的写法:

var webshot = require('webshot')var fs = require('fs') var renderStream = webshot('google.com')var file = fs.createWriteStream('google.png', {encoding: 'binary'}) renderStream.on('data', function(data) { file.write(data.toString('binary'), 'binary')})

node-webshit还支持生成移动版的网页截图:

var webshot = require('webshot') var options = { screenSize: { width: 320 , height: 480 }, shotSize: { width: 320 , height: 'all' }, userAgent: 'Mozilla/5.0 (iPhoneUCPU iPhone OS 3_2 like Mac OS Xen-us)' + ' AppleWebKit/531.21.20 (KHTML, like Gecko) Mobile/7B298g'} webshot('flickr.com', 'flickr.jpeg', options, function(err) { // screenshot now saved to flickr.jpeg})

最后再把yargs和node-webshot进行集成,可惜这部分工作人家也帮我们做好了,直接安装就行。

npm isntall -g webshot-cli

使用desktop-screenshot进行系统截图

desktop-screenshot是一个跨平台的系统截图项目,用法和node-webshot类似,只是少了一个url参数。var screenshot = require('desktop-screenshot') screenshot("screenshot.png", function(error, complete) { if(error)console.log("Screenshot failed", error) elseconsole.log("Screenshot succeeded")})