云服务器免费试用

Canvas实现照片涂鸦效果(canvas在图片上绘制图形)

服务器知识 0 1019

canvas 是 HTML5 中的一个元素,它能用来绘制各种图形、文字,目前前端的很多图表库也会用到 canvas 来绘制。canvas 除了绘制图形外,也能用来处理照片,我用过的一个图片压缩库也是用 canvas 实现的。

我之前写过 使用 canvas 截取摄像头画面来实现拍照 和 使用 canvas 裁剪图片 ,这里就继续来写照片涂鸦。

我要实现的功能包括:

  • 打开和读取本地图片
  • 用鼠标在图片上涂鸦
  • 笔画粗细和颜色可以自定义
  • 可以把涂鸦后的图片导出为 PNG

访问 canvas照片涂鸦demo 可以在线测试和查看源码。

下面是最终实现的效果,我只是实现了功能,没有加样式:

20221102080006100

颜色调节使用的是 input 的颜色选择器。

HTML 元素

下面是会用到的 HTML 元素:

<div role="toolbar" id="toolbar">  <input type="file" id="file-input">  <button type="button" id="open-img-file">打开图片</button>  <label for="color-select">画笔颜色</label>  <input type="color" id="color-select">  <label for="thickness">笔画粗细</label>  <input type="number" id="thickness" placeholder="笔画的粗细单位为像素" value="5">  <button type="button" id="export-btn">导出图片</button></div><hr><canvas id="canvas"></canvas>

下面是用到的 HTML 元素说明:

  • id 为 file-input 的 input 是文件表单,用来选择本地图片
  • id 为 open-img-file 的按钮用来打开文件表单,文件表单为了美观,我把 display 设置为了 none
  • id 为 color-select 的 input 是颜色选择器,用来设置画笔的颜色
  • id 为 thickness 的 input 用来设置笔画粗细
  • id 为 export-btn 的按钮用来导出图片

下面在 JavaScript 中选择这些元素:

const openImgFile = document.querySelector('#open-img-file');  // 打开图片按钮const fileInput = document.querySelector('#file-input');  // 文件选择表单const colorSelect = document.querySelector('#color-select');  // 颜色选择器const thickness = document.querySelector('#thickness');  // 笔画粗细的输入表单const exportBtn = document.querySelector('#export-btn');  // 导出图片按钮const canvas = document.querySelector('#canvas');  // canvas 元素const ctx = canvas.getContext('2d');let press = false;  // 用来记录鼠标按下状态

ctx 存储的 canvas.getContext('2d') 就是 canvas 的 2D 上下文,canvas.getContext('2d') 的属性和方法后面会直接通过 ctx 调用。

press 用来记录鼠标按下的状态,true 就是鼠标按下,false 就是放开。

在 canvas 中显示本地图片

下面先实现打开本地图片和在 canvas 中显示:

// 打开图片按钮点击openImgFile.addEventListener('click', () => {  // 点击文件表单  fileInput.click();});// 文件表单改变fileInput.addEventListener('change', ev => {  // 如果没有选择文件就直接返回  if (ev.target.value === '') return false;  // 检测是否是 jpg 或 png 的图片,如果不是就返回  if (ev.target.files[0].type !== 'image/jpeg' && ev.target.files[0].type !== 'image/png') {    alert('目前只支持 jpg 和 png 格式的图片!');    return false;  }  // 创建一个 img 元素  const img = new Image();  // 创建一个对象 URL,把对象 URL 传给 img  img.src = URL.createObjectURL(ev.target.files[0]);  // img 图片加载完成  img.onload = () => {    // 把 canvas 的宽高设置为图片的真实宽高    canvas.width = img.naturalWidth;    canvas.height = img.naturalHeight;    // 在 canvas 中绘制图片    ctx.drawImage(img, 0, 0, img.naturalWidth, img.naturalHeight, 0, 0, img.naturalWidth, img.naturalHeight);  }});

点击 打开图片 按钮后,调用文件表单的 click 方法就能触发文件表单的点击事件,文件选择对话框就会弹出。

文件选择完成后,我创建了一个 img 元素,把图片文件转为对象 URL 传给 img ,这个 img 主要是用来获取图片的真实尺寸,可以不用插入到页面。

通过 img 的 naturalWidth 和 naturalHeight 获取真实尺寸后,把 canvas 的宽高设置为图片的真实尺寸。我这里设置的是图片真实尺寸,没有做过缩放之类的处理,如果图片的尺寸超出屏幕分辨率就会出现滚动条。

使用 drawImage 可以从 img 截取图像到 vanvas 绘制,下面是 drawImage 的参数说明:

  • image: 截取的图像资源
  • sX: 截取图像的左侧起始位置
  • sY: 截取图像的顶部起始位置
  • sW: 截取图像的宽度
  • sH: 截取图像的高度
  • dX: canvas 绘制图像的左侧起始位置
  • dY: canvas 绘制图像的顶部起始位置
  • dW: canvas 绘制图像的宽度
  • dH: canvas 绘制图像的高度

成功显示图片后,下面就可以开始涂鸦了。

鼠标涂鸦

涂鸦包括鼠标按下、鼠标移动、鼠标松开三个部分:

  1. 鼠标按下时,调整画笔的基本参数,然后建立一个点作为起点
  2. 鼠标移动时,根据鼠标位置,使用点来标记出一条路线,然后使用线连接起来
  3. 鼠标松开时,停止绘制

调用 canvas 上下文的 fillRect 和 arc 都能实现画点,但是鼠标在移动时,不能不间断的触发移动事件,如果只是画点的话,笔画就是一个个的点,不能连接起来。我这里会用到 moveTo 、lineTo ,stroke 来配合实现画线。

下面是鼠标涂鸦的代码:

// 鼠标按下canvas.addEventListener('mousedown', ev => {  // 把鼠标按下状态设置为 true  press = true;  // 开始一个新的路径  ctx.beginPath();  // 标记起点  ctx.moveTo(ev.offsetX, ev.offsetY);  // 设置线的宽度  ctx.lineWidth = Number(thickness.value);  // 设置颜色,颜色从颜色选择器表单获取  ctx.strokeStyle = colorSelect.value;});// 鼠标移动canvas.addEventListener('mousemove', ev => {  // 如果鼠标没有按下就直接返回  if (!press) return false;  // 直线连接位置  ctx.lineTo(ev.offsetX, ev.offsetY);  ctx.stroke();});// 鼠标松开canvas.addEventListener('mouseup', () => {  // 把鼠标按下的状态设置为 false  press = false;  // 停止当前路劲  ctx.closePath();});

下面是详细的步骤说明:

鼠标按下

我在上面定义了一个 press 变量用来存储鼠标状态,鼠标按下时 press 就设置为 true 。

调用 beginPath 方法可以开始一个新的路劲,beginPath 可以让每条笔画都是一个新的路劲。如果不调用 beginPath ,中途如果改变了笔画颜色,之前画的线也会受到影响。

调用 moveTo 来标记点,参数 x 是水平位置 y 是垂直位置,moveTo 只是标记,不会绘制。

通过 lineWidth 属性可以设置线的宽度,我的宽度是从 input 获取的,通过 strokeStyle 属性可以设置画笔颜色和样式,我的颜色也是从 input 的颜色选择器获取的,颜色选择器获取的颜色就是一个 #FFFFFF 的十六进制颜色。

鼠标移动

鼠标移动时,首先要检测 press 鼠标状态是否是按下,如果没有按下就直接返回。

lineTo 方法可以用线把上一个点和指定位置连接起来,参数 x 是水平位置 y 是垂直位置,我的 x 和 y 都是当前的鼠标位置。lineTo 只是把点连接起来,不会绘制出内容。

上面已经使用 moveTo 和 lineTo 创建出了一条路劲,调用 stroke 就能绘制出内容。

鼠标松开

首先把 press 鼠标状态设置为 false ,然后调用 closePath 停止当前路劲。

上面就实现了在照片上涂鸦。

我这里只是编写了鼠标事件,没有编写 touch 触摸事件,在触屏上是无法使用的。要支持触屏可以加入 touchstart 手指接触到屏幕、touchmove 手指在屏幕上移动、touchend 手指离开屏幕三个事件,方法和鼠标事件是差不多的,触摸事件获取位置的时候会包含多个点,如果不需要多指操作的画,获取第 0 个点就可以。

导出为图片

代码:

// 导出图片按钮点击exportBtn.addEventListener('click', () => {  // 把 canvas 内容转换为 DataURL 数据  const imgData = canvas.toDataURL('image/png');  const linkEl = document.createElement('a');  linkEl.href = imgData;  linkEl.download = 'image.png';  linkEl.click();});

下面是导出步骤说明:

  1. 调用 canvas 的 toDataURL 方法把画布转换为 dataURL
  2. 创建一个链接,把链接的 href 设置为 dataURL
  3. 链接的 download 属性可以设置导出的文件名
  4. 调用链接的 click 方法来触发点击

canvas 的 toDataURL 的第一个参数是图片类型,例如 image/jpeg 或 image/png ,第二个参数是图片质量,取值从 0 到 1,默认为 0.92。

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942@qq.com核实处理,我们将尽快回复您,谢谢合作!
若转载请注明出处: Canvas实现照片涂鸦效果(canvas在图片上绘制图形)
本文地址: https://solustack.com/5723.html

相关推荐:

网友留言:

我要评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。