一、基础概念
1.1 Context:Canvas 的上下文、绘制环境,是所有绘制操作 api 的入口
const canvas = document.getElementById('canvas'); //获得画布
const ctx = canvas.getContext('2d'); // ctx 即 context
canvas的getContext()方法,返回的就是CanvasRenderingContext2D对象。
1.2 清除矩形(clearRect)
// 清除某个矩形内绘制的内容,相当于橡皮擦
ctx.clearRect(x, y, width, hegiht);
二、代码实例
所以在Vue3 + TS中我们这么定义数据,如下
(1)绘画板一定会用到鼠标点击、移动等事件,所以先定义事件
@mousedown 对应的是 鼠标按下事件
@mousemove 对应的是 鼠标移动事件
@mouseup 对应的是 鼠标弹起事件
注意:canvas不能用css单独设置width和height,原因自己查阅资料
(2)定义的数据
(3)onMounted钩子函数中,获取 canvas 操作的接口
(4)选择画笔(在选择前需要清空之前的数据)
(5)定义获取鼠标当前位置的函数
(6)清空画布
示例1-- 画笔的功能实现(比如签字墙)
<template>
<div class="helloWorld">
<div>
<button @click="clearCanvas">清除</button>
<button @click="changeKey(0)">线条</button>
<button @click="changeKey(1)">矩形</button>
</div>
<canvas id="canvas" ref="canvas"
@mousedown="mousedown"
@mouseup ="mouseup"
@mousemove.stop.prevent ="mousemove"
width="1000" height="500">
</canvas>
</div>
</template>
<script setup lang = "ts">
import {ref,onMounted} from 'vue'
var canvas = ref<any>('')
let ctx : CanvasRenderingContext2D
let painting = false
var points = false;
let key = 1
let rectList :{startX:number;startY:number;endX:number;endY:number}[]=[]
onMounted(()=>{
// cavans.value = document.getElementById("canvas");
ctx = canvas.value.getContext("2d") as CanvasRenderingContext2D;
})
const changeKey = (id:number)=>{
if(key === id) return
//清除画板
clearCanvas()
//清除矩形对象数组
rectList = []
key = id
}
const getOffset = (event:MouseEvent) =>{
let offset :[number,number]
const {offsetX,offsetY} =event
offset = [offsetX,offsetY]
return offset
}
// 清除画布
const clearCanvas = ()=>{
ctx.clearRect(0,0,canvas.value.width,canvas.value.height)
}
// 画线段--自定义一个划线的函数
const paint = (startX: number, startY: number, endX: number, endY: number, ctx: CanvasRenderingContext2D) => {
ctx.beginPath()
ctx.globalAlpha = 1
ctx.lineWidth = 2
ctx.strokeStyle = '#000'
ctx.moveTo(startX, startY) // 画线起点
ctx.lineTo(endX, endY) // 画线终点
ctx.closePath()
ctx.stroke() // 描绘线条
}
// 定义鼠标初始点击的位置
let startX = 0
let startY = 0
// 鼠标按下
const mousedown = (event: MouseEvent) => {
;[startX, startY] = getOffset(event)
painting = true // 将绘画状态改成true
}
// 鼠标移动
const mousemove = (event: MouseEvent) => {
if (painting) {
// 在鼠标移动的期间,获取鼠标的位置
const [endX, endY] = getOffset(event)
paint(startX, startY, endX, endY, ctx)
// 下面两行代码是更新线条的起点
startX = endX
startY = endY
}
}
// 鼠标松开
const mouseup = (event: MouseEvent) => {
// 松开鼠标后 禁止画线条
if (painting) painting = false
//画板内容转为图片 转为的是base64位图片 需转换
let image = canvas.value.toDataURL()
console.log(image)
}
</script>
<style scoped lang="scss">
.helloWorld{
width: 100%;
height: 100%;
}
.helloWorld img{
width: 100%;
height: 100%;
display: block;
}
</style>
示例2--画矩形兼具画笔签字 -- 下载
<template>
<div class="helloWorld">
<!-- <img src="@/assets/icon3.jpg"> -->
<div>
<button @click="clearCanvas">清除</button>
<button @click="changeKey(0)">线条</button>
<button @click="changeKey(1)">矩形</button>
<button @click="linkxz()">下载</button>
</div>
<canvas id="canvas" ref="canvas"
@mousedown="mousedown"
@mouseup ="mouseup"
@mousemove.stop.prevent ="mousemove"
width="1000" height="500">
</canvas>
</div>
</template>
<script setup lang = "ts">
import {ref,onMounted} from 'vue'
// 画笔签字------------------------------------------------------
var canvas = ref<any>('')
let ctx : CanvasRenderingContext2D
let painting = false
var points:any = false;
let key = 1
let rectList :{startX:number;startY:number;endX:number;endY:number}[]=[]
onMounted(()=>{
// cavans.value = document.getElementById("canvas");
ctx = canvas.value.getContext("2d") as CanvasRenderingContext2D;
})
const changeKey = (id:number)=>{
if(key === id) return
//清除画板
clearCanvas()
//清除矩形对象数组
rectList = []
key = id
}
const getOffset = (event:MouseEvent) =>{
let offset :[number,number]
const {offsetX,offsetY} =event
offset = [offsetX,offsetY]
return offset
}
// 清除画布
const clearCanvas = ()=>{
ctx.clearRect(0,0,canvas.value.width,canvas.value.height)
}
// 画线段 自定义一个划线的函数
const paint = (startX: number, startY: number, endX: number, endY: number, ctx: CanvasRenderingContext2D) => {
ctx.beginPath()
ctx.globalAlpha = 1
ctx.lineWidth = 2
ctx.strokeStyle = '#000'
ctx.moveTo(startX, startY) // 画线起点
ctx.lineTo(endX, endY) // 画线终点
ctx.closePath()
ctx.stroke() // 描绘线条
}
// 定义鼠标初始点击的位置
// let startX = 0
// let startY = 0
// // 鼠标按下
// const mousedown = (event: MouseEvent) => {
// ;[startX, startY] = getOffset(event)
// painting = true // 将绘画状态改成true
// }
// // 鼠标移动
// const mousemove = (event: MouseEvent) => {
// if (painting) {
// // 在鼠标移动的期间,获取鼠标的位置
// const [endX, endY] = getOffset(event)
// paint(startX, startY, endX, endY, ctx)
// // 下面两行代码是更新线条的起点
// startX = endX
// startY = endY
// }
// }
// // 鼠标松开
// const mouseup = (event: MouseEvent) => {
// // 松开鼠标后 禁止画线条
// if (painting) painting = false
// }
// ------------------------------------------------------------------------
// 画矩形
// 画矩形
const drawRectangle = (startX: number, startY: number, endX: number, endY: number, ctx: CanvasRenderingContext2D) => {
ctx.beginPath()
ctx.globalAlpha = 0.3
ctx.lineWidth = 2
ctx.strokeStyle = '#000'
ctx.rect(startX, startY, endX - startX, endY - startY) // 查阅上述文档
ctx.stroke()
}
// 定义鼠标初始点击的位置
let startX = 0
let startY = 0
// 鼠标按下
const mousedown = (event: MouseEvent) => {
points = [] // 每一次画矩形的时候,都先将存下的位置清空
;[startX, startY] = getOffset(event)
painting = true
}
// 鼠标移动
const mousemove = (event: MouseEvent) => {
if (painting) {
// 在鼠标移动的期间,获取鼠标的位置
const [endX, endY] = getOffset(event)
if (key === 1) {
clearCanvas() // 因为是鼠标移动事件,所以每次移动都会产生一个矩形,首先清空在移动中产生的矩形
// 调用之前存储的已经画过的矩形,重新遍历渲染
rectList.forEach((item) => {
drawRectangle(item.startX, item.startY, item.endX, item.endY, ctx)
})
// 将移动获取到的坐标点,全部存入数组中
points.push({
x: endX,
y: endY,
})
// 渲染当前画的矩形
drawRectangle(startX, startY, points[points.length - 1].x, points[points.length - 1].y, ctx)
} else {
paint(startX, startY, endX, endY, ctx)
startX = endX
startY = endY
}
}
}
// 鼠标松开
const mouseup = (event: MouseEvent) => {
const [endX, endY] = getOffset(event)
if (key === 1) {
// 记录画的每一个矩形,且需用unshift方法
rectList.unshift({
startX,
startY,
endX,
endY,
})
}
if (painting) painting = false
}
// 点击下载
const linkxz = ()=>{
// 转换为图片并触发下载
const link = document.createElement('a');
//画板内容转为图片 转为的是base64位图片 需转换
let image = canvas.value.toDataURL('image/png')
console.log(image)
// 创建一个Blob对象
fetch(image)
.then(res => res.blob())
.then(blob => {
// 创建一个文件对象
const file = new File([blob], "canvas-image.png", {
type: "image/png",
});
// 创建一个URL
const fileURL = URL.createObjectURL(file);
console.log(fileURL);
// 设置下载链接的属性
link.href = fileURL;
link.download = "canvas-image.png";
// 触发下载
link.click();
// 清理URL对象
URL.revokeObjectURL(fileURL);
});
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
.helloWorld{
width: 100%;
height: 100%;
}
.helloWorld img{
width: 100%;
height: 100%;
display: block;
}
</style>