Vue3 + TS中利用 canvas 创建绘画画板==>签名板、画笔、矩形 并转为base64图片下载

一、基础概念


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>

  • 10
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值