300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > vue2 + canvas 实现简易画板

vue2 + canvas 实现简易画板

时间:2021-03-05 04:42:21

相关推荐

vue2 + canvas 实现简易画板

前言

这两天在做QQ聊天机器人,目前也实现了一些功能

准备往里面加一些小游戏的功能,思来想去就准备自己实现一个简易版的你画我猜,但是QQ肯定不支持我发个画板过来,于是便诞生了这个网站picture_board (gitee.io)。网站部署在gitee pages上,感兴趣可以去picture_board()下载源代码。

实现思路

整个页面比较简单,上下的两栏布局。上面是canvas的绘画区域,下面是功能选择区域。(请忽略计时框,这个是为了在你画我猜中,提醒绘画时间用的)

canvas准备

首先在template模板里填入canvas元素

<template><div id="bottom"><canvasid="canvas"@mousedown="down($event)"@mousemove="move($event)"@mouseup="up"@mouseenter="enter"@touchstart="start($event)"@touchmove="move2($event)"@touchend="up"></canvas></div></template>

本来我是在canvas便签里面添加了style来设置宽高来实现剩余区域的自动填充(flex:1),但是一番调试之后,页面上怎么搞没有画的痕迹,整整一个上午的时间,都没有任何进展,后来吃饭的时候,我突然意识到可能是宽高的设置方式不对,改过之后发现真的是这个原因,直接老泪纵横。。。

let that = this;that.canvas = document.getElementById("canvas");that.cxt = that.canvas.getContext("2d");canvas.width = that.width;canvas.height = that.height;

width和height也是根据页面比例计算的

this.width = document.documentElement.clientWidth;this.height = document.documentElement.clientHeight * 0.9;

pc端绘画

主要相关的是三个事件

点击鼠标移动鼠标松开鼠标

当鼠标点击时用一个变量记录下该坐标点(x1),鼠标开始滑动时记录滑动到的第一坐标点(x2),这样我们就有了2个坐标点,再通过canvas中的stroke()事件将第一个坐标点与第二个坐标点连接起来变成线。然后将(x2)坐标点赋值给(x1),继续滑动鼠标。这时(x1)的值为滑动到的第一个坐标点,(x2)为当前坐标点,继续连线。最后松开鼠标,触发我们人为设置的控制开关paint = false,循环终止,坐标轴停止赋值。一段线段便完成。

down(e) {// 鼠标按下事件const x = e.offsetX;const y = e.offsetY;this.startPoint = {x: x,y: y,};if (this.eraser) {// 启用橡皮擦this.cxt.clearRect(x, y, 10, 10);}this.paint = true;},move(e) {// 鼠标移动事件e.preventDefault();const x = e.offsetX;const y = e.offsetY;this.endPoint = {x: x,y: y,};if (this.paint) {if (this.eraser) {this.cxt.clearRect(x, y, 10, 10);} else {this.draw();}}this.startPoint.x = this.endPoint.x;this.startPoint.y = this.endPoint.y;},up() {// 鼠标松开事件// 绘画完成,推入历史记录let image = this.cxt.getImageData(0, 0, this.width, this.height);this.history.push(image);// history中有记录,撤回按钮可用bus.$emit("abled", "启用撤回");this.paint = false;},

同时,还要监听进入画布事件,否则会造成使用完橡皮擦,切换到画笔之后,鼠标移动进入画布直接开始绘画

enter() {// 修复pc端使用橡皮擦之后进入canvas立即开始画线this.paint = false;},

移动端绘画

在移动端中是没有鼠标的,因此与其相对应的有一个触摸事件touch。步骤与上面同理,但需要注意一点:移动设备是支持多点触摸的,因此这里的x、y轴需要从e.touches[0]数组第一个中取。

start(e) {//[0]表示touch第一个触碰点let x = e.touches[0].clientX;let y = e.touches[0].clientY;this.startPoint = {x: x,y: y,};if (this.eraser) {this.cxt.clearRect(x, y, 10, 10);}this.paint = true;},move2(e) {e.preventDefault();let x = e.touches[0].clientX;let y = e.touches[0].clientY;this.endPoint = {x: x,y: y,};if (this.paint) {if (this.eraser) {this.cxt.clearRect(x, y, 10, 10);} else {this.draw();}}this.startPoint.x = this.endPoint.x;this.startPoint.y = this.endPoint.y;},

绘制

主要牵扯的canvas的一些基础api,不懂得掘友可以参考我之前写的文章 canvas学习–线条操作 - 掘金 ()。

draw() {// 开始绘制this.cxt.beginPath();// 设置线条样式this.cxt.lineWidth = this.value;this.cxt.lineCap = "round";this.cxt.lineJoin = "round";this.cxt.strokeStyle = this.color;//起始位置this.cxt.moveTo(this.startPoint.x, this.startPoint.y);//停止位置this.cxt.lineTo(this.endPoint.x, this.endPoint.y);//描绘线路this.cxt.stroke();//结束绘制this.cxt.closePath();},

橡皮擦

通过eraser是否等于true开启橡皮擦功能。如果等于true,则用clearRect绘制一个空白矩形,重复步骤点击、滑动、松开即可达到擦除的效果。

if (this.eraser) {this.cxt.clearRect(x, y, 10, 10);}

清空

清空画布主要通过canvas的api HTML canvas clearRect() 方法 ()

remove() {// 清空画布this.cxt.fillStyle = "#ffebce";this.cxt.clearRect(0, 0, this.width, this.height);},

画笔颜色和宽度

这个实现起来也比较简单,主要是通过组件通信来传递数据,我这里用的事件总线。utils文件夹新建一个js文件。

import Vue from 'vue'; // 事件总线export default new Vue;

页面需要的话,直接导入

import bus from "../utils/eventBus";

同时,也可以选择挂载到Vue上,感兴趣的掘友可查看这篇文章 Vue事件总线(EventBus)使用详细介绍 - 知乎 ()

下载

下载的话,使用的file-saver的软件包,可以从npm下载 file-saver - npm ()

this.canvas.toBlob(function (blob) {const time = new Date().getTime();saveAs(blob, `${time}.png`);});

撤回

这里主要牵扯的是两个api的应用,HTML canvas getImageData() 方法 ()和HTML canvas putImageData() 方法 ()

开始绘制的时候存入history数组,撤回的时候从数组中删除,使用putImageData放入尾元素到画布。

cancel() {// 撤回上一步this.history.pop();if (this.history.length == 1) {this.cxt.putImageData(this.history[this.history.length - 1], 0, 0);bus.$emit("disabled", "禁用撤回");} else {this.cxt.putImageData(this.history[this.history.length - 1], 0, 0);}},

这里为了防止多次点击撤回,数组清空控制台报错,添加了撤回禁用功能,感兴趣的掘友可查看源码。

上传

上传的话,由于没有服务器的缘故,我这里使用的存储地是strapi(之前同学帮我部署过strapi) https://strapi.ioStrapi是一款免费开源的Nodejs无头CMS内容管理框架,很好用,对于不懂后端的前端开发人员很友好!!

upload() {this.$prompt("请输入答案", "提示", {confirmButtonText: "确定",cancelButtonText: "取消",center: true,}).then(({value }) => {let that = this;canvas.toBlob(function (blob) {const formData = new FormData();const time = new Date();formData.append("files.img", blob, `${time.getTime()}.png`);formData.append("data", JSON.stringify({description: value }));axios.post("你的url", formData).then((_res) => {//console.log(res);that.$message({type: "success",message: "图片上传成功",});},(_err) => {that.$message({type: "error",message: "图片上传失败",});});});}).catch(() => {this.$message({type: "info",message: "取消输入",});});},

最后

画板的实现思路参考自实现一个canvas画板_A-Tione的博客-CSDN博客_canvas画板,感谢大佬的技术分享。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。