基于Vue+Canvas实现的画板绘画以及保存功能,解决保存没有背景问题

基于Vue+Canvas实现的画板绘画以及保存功能

本文内容设计到的画板的js部分内容来源于灵感来源引用地址,然后我在此基础上,根据自己的需求做了修改,增加了其他功能。
在这里插入图片描述

下面展示了完整的前后端代码

这里写目录标题

  • 基于Vue+Canvas实现的画板绘画以及保存功能
    • 1. board-js.js
    • 2. 前端的vue文件
    • 3. 后端Controller

1. board-js.js

这个代码,接收一个容器参数,创建了一个画板类,里面实现了画板会用到的基本方法,保存到单独的js文件,在vue文件中导入,创建一个画板对象。
Canvas直接使用canvas.toDataURL()保存的图片是没有背景的,因为默认的是png,所以需要开始绘画之前先填充背景,下面代码做出了修改,在init()函数中。
代码中用到的Canvas的原生API的作用,可以参考这里Canvas参考手册

export default class BoardCanvas {constructor(container) {// 容器this.container = container// canvas画布this.canvas = this.createCanvas(container)// 绘制工具this.ctx = this.canvas.getContext('2d')// 起始点位置this.startX = 0this.stateY = 0// 画布历史栈this.pathSegmentHistory = []this.index = 0// 初始化this.init()}// 创建画布createCanvas(container) {const canvas = document.createElement('canvas')canvas.width = container.clientWidthcanvas.height = container.clientHeightcanvas.style.display = 'block'canvas.style.backgroundColor = 'white'container.appendChild(canvas)return canvas}// 初始化init() {this.addPathSegment()this.setContext2DStyle()//下面两行,原文件是没有的,如果没有会导致保存的图片没有背景,只有绘画轨迹this.ctx.fillStyle = "#ffffff";this.ctx.fillRect(0, 0,this.canvas.width, this.canvas.height);this.canvas.addEventListener('contextmenu', e => e.preventDefault())this.canvas.addEventListener('mousedown', this.mousedownEvent.bind(this))window.document.addEventListener('keydown', this.keydownEvent.bind(this))}// 设置画笔样式setContext2DStyle() {this.ctx.strokeStyle = 'black'this.ctx.lineWidth = 3this.ctx.lineCap = 'round'this.ctx.lineJoin = 'round'}// 鼠标事件mousedownEvent(e) {const that = thisconst ctx = this.ctxctx.beginPath()ctx.moveTo(e.offsetX, e.offsetY)ctx.stroke()this.canvas.onmousemove = function (e) {ctx.lineTo(e.offsetX, e.offsetY)ctx.stroke()}this.canvas.onmouseup = this.canvas.onmouseout = function () {that.addPathSegment()this.onmousemove = nullthis.onmouseup = nullthis.onmouseout = null}}// 键盘事件keydownEvent(e) {if(!e.ctrlKey) returnswitch(e.keyCode) {case 90:this.undo()breakcase 89:this.redo()break}}// 添加路径片段addPathSegment() {const data = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height)// 删除当前索引后的路径片段,然后追加一个新的路径片段,更新索引this.pathSegmentHistory.splice(this.index + 1)this.pathSegmentHistory.push(data)this.index = this.pathSegmentHistory.length - 1}// 撤销undo() {if(this.index <= 0) returnthis.index--this.ctx.putImageData(this.pathSegmentHistory[this.index], 0, 0)}// 恢复redo() {if(this.index >= this.pathSegmentHistory.length - 1) returnthis.index++this.ctx.putImageData(this.pathSegmentHistory[this.index], 0, 0)}//获取画布内容getImage() {return this.canvas.toDataURL();}//清空画板cleanboard(){this.ctx.fillStyle = "#ffffff";this.ctx.fillRect(0, 0,this.canvas.width, this.canvas.height);}
}

2. 前端的vue文件

<template><el-container direction="vertical" style="height: 100%;width: 100%"><!--头顶布局,用户操作提示语--><el-header height="10%"><h2>在空白处进行绘画</h2></el-header><!--中间布局 --><div style=display:flex;justify-content:center;align-items:center;><!--中间画板--><div class="drawing-board"style="width:66%;height:600px;border: 1px black solid;margin-left: 10px"><div id="container" ref="container" style="width: 100%; height: 100%"></div></div></div><!--底部按钮--><el-footer style="height: 300px;margin-top: 25px"><el-button type="primary" round @click="savedrawing">保存</el-button></el-footer></el-container>
</template>//这里使用的vue的setup语法糖,所以data和method不需要封装,直接用
<script setup>
import { ref, onMounted } from 'vue'
import Board from '@/js/drawing-board.js'
import axios from "axios";const container = ref(null)
let drawboard = null;
onMounted(() => {// 新建一个画板drawboard=new Board(container.value)
})
function savedrawing() {const drawdata = drawboard.getImage();				let formdata = new FormData();const timestamp = (new Date()).valueOf();let filename = timestamp;formdata.append("drawPictureId",filename);formdata.append("drawPictureData",drawdata.substring(22));axios({method:"post",url:"/savedrawdata",baseURL:"http://localhost:9999",data:formdata,contentType:false,processData:false}).then(response=>{if(response.status===200){alert("保存成功!")}}).catch(error=>{console.log(error);})
}
</script>

3. 后端Controller

Controller文件中,前端使用FormData格式传递参数,就相当于是个map键值对,所以在参数这里,使用 @RequestParam() ,取出表单中的值,括号中的字符串,是在前端传入的,表示将该key对应的value,赋值给后面的String 参数。
其次,这里注意Canvas得到的图片是经过base64编码过的,所以先解码成字节数组

    @RequestMapping("/savedrawdata")public ResponseEntity<?> savedrawdata(@RequestParam("drawPictureId")String drawPictureId,@RequestParam("drawPictureData")String drawPictureData){try {// 解码前端传过来的base64编码byte[] imageBytes = Base64.decodeBase64(drawPictureData);// 将字节流转为图片缓冲流BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(imageBytes));// 保存为pngFile output = new File("F:\\image\\" + drawPictureId + ".png");ImageIO.write(bufferedImage, "png", output);} catch (IOException e) {e.printStackTrace();}System.out.println(rawPictureId);System.out.println(drawPictureId);return ResponseEntity.ok(HttpStatus.OK);}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/633143.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Python】遥感数据趋势分析Sen+mk

方法介绍 1.Theil-Sen Median方法又被称为 Sen 斜率估计&#xff0c;是一种稳健的非参数统计的趋势计算方法。该方法计算效率高&#xff0c;对于测量误差和离群数据不敏感&#xff0c;常被用于长时间序列数据的趋势分析中。对于后续代码计算结果中的slope.tif解读,当slope大于…

ycsb压测mongodb

下载解压 https://github.com/brianfrankcooper/YCSB/releases/download/0.17.0/ycsb-mongodb-binding-0.17.0.tar.gz tar -zxvf ycsb-mongodb-binding-0.17.0.tar.gzycsb提前已经在workload文件夹下准备好了几个压测场景分别对应workload[a:f] workloads/workloada 样例 …

微电网优化MATLAB:火鹰优化算法(Fire Hawk Optimizer,FHO)求解微电网优化(提供MATLAB代码)

一、火鹰优化算法FHO 火鹰优化算法&#xff08;Fire Hawk Optimizer&#xff0c;FHO&#xff09;由Mahdi Azizi等人于2022年提出&#xff0c;该算法性能高效&#xff0c;思路新颖。 单目标优化&#xff1a;火鹰优化算法&#xff08;Fire Hawk Optimizer&#xff0c;FHO&#…

[Linux 进程(五)] 程序地址空间深度剖析

文章目录 1、前言2、什么是进程地址空间&#xff1f;3、进程地址空间的划分4、虚拟地址与物理地址的关系5、页表的作用扩展 6、为什么要有地址空间&#xff1f; 1、前言 Linux学习路线比较线性&#xff0c;也比较长&#xff0c;因此一个完整的知识点学习就会分布在两篇文章中&…

【Python程序开发系列】一文搞懂argparse模块的常见用法(案例+源码)

一、引言 argsparse是python的命令行解析的标准模块&#xff0c;内置于python&#xff0c;不需要安装。这个库可以让我们直接在命令行中就可以向程序中传入参数并让程序运行。 在运行深度学习程序时。往往会因为电脑配置不行导致程序运行慢卡&#xff0c;需要将程序在虚机上进行…

Dubbo使用详解

简介 Dubbo是一个高性能、轻量级的开源Java RPC框架&#xff0c;由阿里巴巴公司开发并开源。它提供了三大核心能力&#xff1a;面向接口的远程方法调用&#xff0c;智能容错和负载均衡&#xff0c;以及服务自动注册和发现。Dubbo使得应用可通过高性能的 RPC 实现服务的输出和输…

浅聊雷池社区版(WAF)的tengine

雷池社区版是一个开源的免费Web应用防火墙&#xff08;WAF&#xff09;&#xff0c;专为保护Web应用免受各种网络攻击而设计。基于强大的Tengine&#xff0c;雷池社区版提供了一系列先进的安全功能&#xff0c;适用于中小企业和个人用户。 Tengine的故事始于2011年&#xff0c;…

解析Transformer模型

原文地址&#xff1a;https://zhanghan.xyz/posts/17281/ 进入Transformer RNN很难处理冗长的文本序列&#xff0c;且很容易受到所谓梯度消失/爆炸的问题。RNN是按顺序处理单词的&#xff0c;所以很难并行化。 用一句话总结Transformer&#xff1a;当一个扩展性极佳的模型和一…

springcloud Client端cloud-consumer-order80

文章目录 简介建立module修改pom修改yml主启动类把公共代码写在一个mudule 里面测试 简介 这个是和之前的8001相互配合端口测试 这里的80的用户测试端口。 代码在&#xff1a;GitHub 上&#xff1a;https://github.com/13thm/study_springcloud/tree/main/days2 建立module …

完美解决idea一直indexing,无法操作的问题

今天主要分享一下在使用idea 2020.3版本开发maven项目的时候&#xff0c;一直出现有效件index&#xff0c; 有时候是scaning indexing, 有时候是update indexing, indexing的时候&#xff0c;idea基本上就没办法操作了&#xff0c;连跳入到类或方法里都跳不了。不厌其烦。 于是…

模型Model:字符串列表模型QStringListModel

一、QStringListModel &#xff08;1&#xff09;功能&#xff1a;处理字符串列表的数据模型&#xff0c;可作为QListView的数据模型&#xff0c;在界面上显示和编辑字符串列表。 二、QStringListModel 类中的函数 1)、 QStringListModel(QObject *parent Q_NULLPTR) //构造函…

工程监测仪器振弦采集仪的新技术研究与创新方面

工程监测仪器振弦采集仪的新技术研究与创新方面 工程监测仪器振弦采集仪是一种用于测量和监测工程结构振动特性的仪器。传统的振弦采集仪主要采用振弦传感器和数据采集设备&#xff0c;通过对结构振动信号的采集和分析&#xff0c;可以获得结构的动态特性&#xff0c;如固有频…

【01】mapbox js api加载arcgis切片服务

需求&#xff1a; 第三方的mapbox js api加载arcgis切片服务&#xff0c;同时叠加在天地图上&#xff0c;天地图坐标系web墨卡托。 效果图&#xff1a; 形如这种地址去加载http://zjq2022.gis.com:8080/demo/loadmapboxtdt.html 思路&#xff1a; 需要制作一个和天地图比例…

vscode配置web开发环境(WampServer)

这里直接去下载了集成的服务器组件wampserver&#xff0c;集成了php&#xff0c;MySQL&#xff0c;Apache 可能会出现安装问题&#xff0c;这里说只有图上这些VC包都安装了才能继续安装&#xff0c;进入报错里提供的链接 在页面内搜索相关信息 github上不去可以去镜像站 下载…

《Python数据分析技术栈》第01章 02 Jupyter入门(Getting started with Jupyter notebooks)

02 Jupyter入门&#xff08;Getting started with Jupyter notebooks&#xff09; 《Python数据分析技术栈》第01章 02 Jupyter入门&#xff08;Getting started with Jupyter notebooks&#xff09; Before we discuss the essentials of Jupyter notebooks, let us discuss…

C#,字符串匹配(模式搜索)RK(Rabin Karp)算法的源代码

M.O.Rabin Rabin-Karp算法&#xff0c;是由M.O.Rabin和R.A.Karp设计实现的一种基于移动散列值的字符串匹配算法。 通常基于散列值的字符串匹配方法&#xff1a;&#xff08;1&#xff09;首先计算模式字符串的散列函数&#xff1b;&#xff08;2&#xff09;然后利用相同的散…

【漏洞攻击之文件上传条件竞争】

漏洞攻击之文件上传条件竞争 wzsc_文件上传漏洞现象与分析思路编写攻击脚本和重放措施中国蚁剑拿flag wzsc_文件上传 漏洞现象与分析 只有一个upload前端标签元素&#xff0c;并且上传任意文件都会跳转到upload.php页面&#xff0c;判定是一个apache容器&#xff0c;开始扫描…

IntelliJ IDEA 中输出乱码解决

最近tomcat突然在控制台输出乱码&#xff0c;各种乱码问题&#xff0c;查阅大量的资料&#xff0c;最终得以解决. IDEA控制台输出乱码 问题一&#xff1a;idea中tomcat控制台输出乱码 运行本地的tomcat\bin\start.bat文件页面显示正常 在idea中显示乱码 解决&#xff1a; 根…

WebRTC视频会议/视频客服系统EasyRTC进入会议室密码验证的开发与实现

基于WebRTC技术的EasyRTC视频会议系统&#xff0c;建设目标是让用户随时随地、快捷方便地进行视频会议&#xff0c;并根据行业需求有针对性地提供多样化、个性化功能&#xff0c;该系统是覆盖全球的实时音视频开发平台&#xff0c;支持一对一、一对多等视频通话&#xff0c;极大…

梳理从MVP变换到光栅化的过程

1.梳理从MVP变换到光栅化的过程 相关博客&#xff1a; 1.MVP变换 2.Rasterization&#xff08;光栅化&#xff09; 1.1 View/Camera transformation 此例中相机初始位置为&#xff08;0,0,5&#xff09;【备注&#xff1a;详见主函数中输入的值】经过 M view M_{\text{view}}…