React版:React制作页面在线截图功能

项目背景

写了一个关于身份证、驾驶证、行驶证OCR识别的页面。但是用户上传照片的时候不是单独上传,把3个证件摆放在一起上传的。于是业务部门提出能不能制作一个在线截图、裁剪的功能,这样就不需要他们额外打开其他截图软件。

⚠️关键技术点: 用Canvas如何绘制出裁剪框。

<!--* @Descripttion: * @version: * @Author: dal* @Date: 2021-11-16 17:12:58* @LastEditors: dal* @LastEditTime: 2021-11-17 14:09:29
-->
<template><div><canvas width="100%" height="100%" class="canvas" ref="canvasNode" @mousedown="handleMouseDownEvent" @mousemove="handleMouseMoveEvent" @mouseup="handleMouseRemoveEvent"></canvas><div ref="buttonGroupNode" class="btns"><el-button @click="clearCanvas">取消</el-button><el-button type="primary" @click="getImageData">确定</el-button></div></div>
</template>
<script>import * as htmlToImage from 'html-to-image';export default {data() {return {canvasNode: "", // canvas DOM节点buttonGroupNode: "", //裁剪框下的按钮组 DOM节点originImg: null, //源图startCoordinate: [0, 0], // 开始坐标leftTopPoint: [0, 0], //左上角起始点坐标dragging: false, //裁剪中dragComplate: false, //裁剪框绘制完毕trimPositionMap: [0, 0], // 裁剪框坐标信息curPoisition: null, // 当前裁剪框坐标信息borderPixelPoisition: null //判断鼠标是否停留在裁剪框上4个点}},mounted() {this.canvasNode = this.$refs.canvasNodethis.buttonGroupNode = this.$refs.buttonGroupNodethis.canvasNode.width = window.innerWidththis.canvasNode.height = window.innerHeight},methods: {start() {var node = document.getElementsByTagName("html")[0]htmlToImage.toPng(node).then(dataUrl => {this.canvasNode.style.zIndex = 1009const image = new Image();image.src = dataUrlthis.originImg = image})},handleMouseDownEvent(e) {let { buttonGroupNode, borderPixelPoisition, dragComplate, curPoisition, leftTopPoint } = thisif (!borderPixelPoisition && dragComplate) {return}this.dragComplate = false //还没有完成裁剪this.dragging = true //正在裁剪中buttonGroupNode.style.display = "none"const { offsetX, offsetY } = e;if (!borderPixelPoisition) {this.startCoordinate = [offsetX, offsetY]} else {let ltX = leftTopPoint[0],ltY = leftTopPoint[1],w = Math.abs(curPoisition.w),h = Math.abs(curPoisition.h)switch (borderPixelPoisition) {case 'lefttop':this.startCoordinate = [ltX + w, ltY + h]break;case "leftdown":this.startCoordinate = [ltX + w, ltY]break;case "righttop":this.startCoordinate = [ltX, ltY + h]break;case 'rightdown':this.startCoordinate = leftTopPointbreak;default:break;}}},handleMouseMoveEvent(e) {let { dragging, canvasNode, startCoordinate, dragComplate, curPoisition, borderPixelPoisition, leftTopPoint } = this/** 是否绘制完成,判断鼠标有没有经过绘制的点上 */if (dragComplate) {const { offsetX, offsetY } = e;let w = Math.abs(curPoisition.w),h = Math.abs(curPoisition.h),startX = leftTopPoint[0],startY = leftTopPoint[1]if (offsetX - startX < 2.5 && offsetX - startX > -2.5 && offsetY - startY < 2.5 && offsetY - startY > -2.5) {//左上角canvasNode.style.cursor = "nw-resize"this.borderPixelPoisition = "lefttop"} else if (offsetX - w - startX < 2.5 && offsetX - w - startX > -2.5 && offsetY - startY < 2.5 && offsetY - startY > -2.5) {//右上角canvasNode.style.cursor = "sw-resize"this.borderPixelPoisition = "righttop"} else if (offsetX - startX < 2.5 && offsetX - startX > -2.5 && offsetY - startY - h < 2.5 && offsetY - startY - h > -2.5) {//左下角canvasNode.style.cursor = "sw-resize"this.borderPixelPoisition = "leftdown"} else if (offsetX - w - startX < 2.5 && offsetX - w - startX > -2.5 && offsetY - startY - h < 2.5 && offsetY - startY - h > -2.5) {//右下角canvasNode.style.cursor = "nw-resize"this.borderPixelPoisition = "rightdown"} else {canvasNode.style.cursor = "default"this.borderPixelPoisition = null}}/**不在裁剪中 */if (!dragging) {return}const ctx = canvasNode.getContext('2d');// 每一帧都需要清除画布(取最后一帧绘图状态, 否则状态会累加)ctx.clearRect(0, 0, canvasNode.clientWidth, canvasNode.clientHeight);let tempWidth = 0,tempHeight = 0const { offsetX, offsetY } = e;if (borderPixelPoisition && curPoisition) {this.dragComplate = false// 获取不同裁剪点下的高宽let { w, h } = this.getPixelTempWH(offsetX, offsetY)tempWidth = wtempHeight = h} else {// 计算临时裁剪框的宽高tempWidth = offsetX - startCoordinate[0];tempHeight = offsetY - startCoordinate[1];}// console.log(startCoordinate[0], startCoordinate[1])// 调用绘制裁剪框的方法this.drawTrim(startCoordinate[0], startCoordinate[1], tempWidth, tempHeight);},handleMouseRemoveEvent(e) {let { curPoisition, buttonGroupNode, borderPixelPoisition, dragComplate } = this//禁止已经绘画完截图之后,鼠标点击canvas又重新触发以下事件if (dragComplate) {return}// 结束裁剪this.dragging = false// 处理裁剪按钮样式if (curPoisition == null) {return;}if (buttonGroupNode == null) {return;}const { x, y } = e;let ltX = x,ltY = y,w = curPoisition.w,h = curPoisition.hswitch (borderPixelPoisition) {case "lefttop":break;case "righttop":ltX = x - wbreak;case "leftdown":ltY = y - hbreak;case "rightdown":ltX = x - wltY = y - hbreak;default:ltX = x - wltY = y - hbreak;}this.leftTopPoint = [ltX, ltY],this.dragComplate = truebuttonGroupNode.style.display = "block"buttonGroupNode.style.left = `${ltX}px`buttonGroupNode.style.top = `${ltY + curPoisition.h + 10}px`;},// 调用绘制裁剪框的方法drawTrim(x, y, w, h) {let { canvasNode, originImg } = thisconst ctx = canvasNode.getContext('2d');// 绘制蒙层ctx.save();ctx.fillStyle = 'rgba(0,0,0,0.4)'; // 蒙层颜色ctx.fillRect(0, 0, canvasNode.clientWidth, canvasNode.clientHeight);// 将蒙层凿开ctx.globalCompositeOperation = 'source-atop';// 裁剪选择框ctx.clearRect(x, y, w, h);// 绘制8个边框像素点ctx.globalCompositeOperation = 'source-over';this.drawBorderPixel(ctx, x, y, w, h);// 保存当前区域坐标信息this.curPoisition = {w: w,h: h,startX: x,startY: y,position: [[x, y],[x + w, y],[x, y + h], //左下角[x + w, y + h]]}ctx.restore();// 再次调用drawImage将图片绘制到蒙层下方ctx.save();ctx.globalCompositeOperation = 'destination-over';ctx.drawImage(originImg, 0, 0, canvasNode.clientWidth, canvasNode.clientHeight);ctx.restore();},// 绘制边框像素点的方法  drawBorderPixel(ctx, x, y, w, h) {ctx.fillStyle = '#f5222d';const size = 5; // 自定义像素点大小ctx.fillRect(x - size / 2, y - size / 2, size, size);// ...同理通过ctx.fillRect再画出其余像素点ctx.fillRect(x + w - size / 2, y - size / 2, size, size);ctx.fillRect(x - size / 2, y + h - size / 2, size, size);ctx.fillRect(x + w - size / 2, y + h - size / 2, size, size);},//获取不同裁剪点下的宽高/*** @param x 鼠标当前的x轴* @param y 鼠标当前的y轴*/getPixelTempWH(x, y) {let { borderPixelPoisition, leftTopPoint, startCoordinate } = thislet result = { w: 0, h: 0 }let ltX = leftTopPoint[0],ltY = leftTopPoint[1],startX = startCoordinate[0],startY = startCoordinate[1]switch (borderPixelPoisition) {case "lefttop":result.w = x - startXresult.h = y - startYbreak;case "leftdown":result.w = x - startXresult.h = y - ltYbreak;case "righttop":result.w = x - ltXresult.h = y - startYbreak;case "rightdown":result.w = x - ltXresult.h = y - ltYbreak;default:break;}return result},//清除画布clearCanvas() {let { canvasNode, buttonGroupNode } = thisconst ctx = canvasNode.getContext('2d');// 每一帧都需要清除画布(取最后一帧绘图状态, 否则状态会累加)ctx.clearRect(0, 0, canvasNode.clientWidth, canvasNode.clientHeight);canvasNode.style.zIndex = -1buttonGroupNode.style.display = "none"this.dragComplate = false},//获取裁剪图片输出getImageData() {let { canvasNode, curPoisition, buttonGroupNode } = thisconst trimCanvasNode = document.createElement('canvas')trimCanvasNode.width = curPoisition.w;trimCanvasNode.height = curPoisition.h;const trimCtx = trimCanvasNode.getContext('2d');trimCtx.clearRect(0, 0, trimCanvasNode.width, trimCanvasNode.height);let ctx = canvasNode.getContext('2d')let data = ctx.getImageData(curPoisition.startX, curPoisition.startY, curPoisition.w, curPoisition.h)trimCtx.putImageData(data, 0, 0);const trimData = trimCanvasNode.toDataURL();this.$emit("get", trimData)canvasNode.style.display = "none"buttonGroupNode.style.display = "none"}}}</script>
<style scoped>.canvas {position: fixed;top: 0;left: 0;z-index: -1;}.btns {position: fixed;z-index: 1010;display: none;}</style>
<!--* @Descripttion:* @version:* @Author: dal* @Date: 2021-11-16 15:49:29* @LastEditors: dal* @LastEditTime: 2021-11-17 14:08:31
-->
<template><div><el-button type="primary" @click="screenShot">截图</el-button><onlineScreenshot ref="screen" @get="getScreen" /></div>
</template>
<script>import onlineScreenshot from "@/components/OnlineScreenshot/index.vue"export default {components: {onlineScreenshot},data() {return {img: ''}},mounted() {},methods: {screenShot() {this.$refs.screen.start()},getScreen(e) {console.log(e)}}}</script>

Vue制作页面在线裁剪功能相关推荐

  1. vue 登录页面记住密码功能

    vue+iview/element 一般用来快速搭建后台管理系统,登录页的记住密码功能也是必不可少的. 记住密码快速登录功能(iview ui): 思路:首次登录,记住密码,将密码存储到cookie中 ...

  2. vue实现xml在线编辑功能

    先看效果 避免误会 这是一个在线编辑器 我们可以在这上面随意的编写xml代码格式 我们修改上面的内容之后 就可以在控制台输出内容 如果这正是您想要的东西 那就可以先创建一个vue项目 我们先引入依赖 ...

  3. vue 头像上传裁剪功能

    1,第一步安装vue-cropper cnpm install vue-cropper 2,在main.js引用 import VueCropper from 'vue-cropper'; Vue.u ...

  4. web页面在线编辑功能

    首先在web.config文件中添加 <system.web> <httpHandlers> <add verb="GET" path="F ...

  5. vue H5页面实现海报功能demo

    demo展示地址:(扫描图片二维码即可体验) 1.准备插件 qrcanvas和html2canvas(npm装好) 2.html代码(布局按个人喜好) <template><div& ...

  6. asp.net实现图片在线上传并在线裁剪

    1.说明 接上一篇文章uploadify实现多附件上传完成后,又突然用到头像上传并在线裁剪.在网上找个众多例子都没有符合要求的,有一篇文章写的不错,就是文旺老兄写的这篇Asp.Net平台下的图片在线裁 ...

  7. vue模拟加载更多功能(数据追加)

    使用vue制作加载更多功能,通过ajax获取的数据往data里面push经常不成功,原因是push是往数组中追加数据内容的,而不能用作数组之间的拼接,ajax获取的数据就是数组形式的,因此不成功,应该 ...

  8. 制作歌词录入系统php,AJAX_ajax技术制作得在线歌词搜索功能,最新制作完成的在线歌词搜索 - phpStudy...

    ajax技术制作得在线歌词搜索功能 最新制作完成的在线歌词搜索功能,利用ajax技术,无刷新显示歌词,只需要输入你要查找的歌曲名或歌词.界面还不是很好看,完善中...... 源码下载http://ww ...

  9. vue+element实现图片上传及裁剪功能(vue-cropper)

    需求背景: 上传图片或者头像时,能够将图片进行裁剪(等比例缩放裁剪或非等比例裁剪) 效果图: 安装使用 1.安装 npm install vue-cropper // npm 安装 yarn add ...

最新文章

  1. 因需设岗,竞争上岗的多Agent协作的协作模式
  2. oracle合并查询
  3. 通向架构师的道路(第十七天)IBM Websphere集群探秘-WASND
  4. cfiledialog指定位置和大小_GDamp;T | 位置度公差的理解过程
  5. delayqueue_在DelayQueue中更改延迟,从而更改顺序
  6. 【牛客 - 318M】被打脸的潇洒哥(几何问题,水题,结论,知识点)
  7. SLS机器学习介绍(02):时序聚类建模
  8. angular学习的一些Mark
  9. Html5游戏开发-145行代码完成一个RPG小Demo
  10. 基于编辑方法的文本生成(下)
  11. Luogu2216 [HAOI2007]理想的正方形
  12. [struts2]继承ActionSupport类
  13. 网易云音乐获取音频链接(爬虫)破解params参数
  14. 网站页面要怎么设计?
  15. CST STUDIO SUITE 2019 Linux download
  16. 如何采集与分析RocketMQ客户端日志
  17. 致远OA单点登陆到第三方系统(零代码实现)
  18. 渗透测试流程 - 渗透测试的9个步骤
  19. 保险行业防范网络犯罪新思路
  20. 数据结构和算法(二):摘要算法之SHA和MD5

热门文章

  1. 计算机表格做八折怎么辛,原来古人的书信常用语那么美
  2. springMVC 解决 “because it is included into a circular dependency(循环导入依赖)” 的问题
  3. Xcode9点击对象跳转
  4. CG-62 压电式雨量传感器 什么是压电式雨量传感器,压电雨量传感器和翻斗式雨量传感器优势对比
  5. 微型机器学习,会是下一代AI革命吗?
  6. 学mysql需要英语水平多高_大学英语专业挂科率高吗
  7. Django框架中No installed app with label问题
  8. 开发手机游戏的步骤是怎样的?
  9. element中的横线,element的tab,下划线不显示的问题
  10. 阿里云ECS使用指南