likes
comments
collection
share

谈如何用canvas截取图片,新手教程...

作者站长头像
站长
· 阅读数 23

我的想法

今天刚好无事,突然想到在很多地方都会有图片截取的功能,所以想自己也实现一下前端如何截取图片。 通过大量的搜索之后,我发现很多都是用的canvas去做的,刚好对canvas的技术不太熟悉,也顺便学习一下canvas的使用技巧,这里我用的是vue3

思考过程

按照我的想法,需要实现一个截图功能,得需要三个步骤

  1. 上传图片,并展示
  2. 截图操作
  3. 展示截图区域

下面就看一看简单的实现过程吧

页面结构

  <div class="chat-canvas">
    <canvas class="chat-canvas-box" ref="canvas"></canvas>
    <img :src="imgUrl" alt="" ref="img" class="img" />
  </div>
  <div>
    <input type="file" @change="handleFileChange" />
  </div>
  <div>
    <img class="img2" :src="img2Url" alt="" />
  </div>

css的话可以自己先随意写一点,能够理解思路就ok了

第一步 图片上传

  1. 通过input框的上传事件获取到图片文件,并将文件内容转换成url,放在img标签
  const handleFileChange = (e) => {
    const fileReader = new FileReader()
    fileReader.readAsDataURL(e.target.files[0])
    fileReader.onload = (e) => {
      imgUrl.value = e.target.result
    }
  }

这里可以用FileReader转换成url链接,在本地展示

第二步 截取操作

  1. 先初始化一个大小和图片的canvas画框
  const canvasInit = () => {
    img.value.onload = () => {
        canvas.value.width = img.value.width
        canvas.value.height = img.value.height
    }
    canvas.value.addEventListener('mousedown', handleMouseDown, false)
   }

这里,我用onload事件是为了获取页面图片展示的宽高一致,如果是一个固定的截图区域,也可以写一个固定的值,canvas的宽高不会影响到后面比例的问题 2. 给canvas添加事件监听mousedown,mousemove,mouseup

  // 鼠标按下
  const handleMouseDown = (e) => {
    canvas.value.addEventListener('mousemove', handleMouseMove, false)
    canvas.value.addEventListener('mouseup', handleMouseUp, false)
  }
  // 鼠标移动
  const handleMouseMove = (e) => {
  }
  // 鼠标抬起
  const handleMouseUp = () => {
    canvas.value.removeEventListener('mousemove', handleMouseMove, false)
    canvas.value.removeEventListener('mouseup', handleMouseUp, false)
  }
  1. 考虑画一个canvas画框
    • 首先要确定画框的初始坐标,所以在鼠标按下的时候确定,定义全局变量 startX,startY
    • 鼠标移动的时候,获取移动的距离,即可获取到画框的长度和宽度
    • 鼠标抬起的时候,事件解绑
  const handleMouseDown = (e) => {
    startX = e.clientX
    startY = e.clientY
    canvas.value.addEventListener('mousemove', handleMouseMove, false)
    canvas.value.addEventListener('mouseup', handleMouseUp, false)
  }
   const handleMouseMove = (e) => {
    let rectWidth = e.clientX - startX
    let rectHeight = e.clientY - startY
    let ctx = canvas.value.getContext('2d')
    ctx.clearRect(0, 0, canvas.value.width, canvas.value.height)
    ctx.fillStyle = '#000'
    ctx.strokeRect(startX, startY, rectWidth, rectHeight)
  }

注意点: 在每次移动绘画之前需要把之前的画框清除掉

  1. 画框对应的图片位置,开始绘制,考虑一下,大概就是鼠标抬起之后,就将画框区域内的图片区域绘画出来,然后再渲染到页面上面去就可以了
   // 定义绘画函数
     const draw = () => {
    const canvas2 = document.createElement('canvas')
    canvas2.width = 300
    canvas2.height = 300

    const ctx = canvas2.getContext('2d')
    ctx.clearRect(0, 0, 300, 300)
    ctx.drawImage(
      img.value,
      startX,
      startY,
      rectWidth,
      rectHeight,
      0,
      0,
      300,
      300,
    )
    img2Url.value = canvas2.toDataURL()
  }

drawImage方法就可以将一张图片绘画在canvas中

注意点 img.value是获取的一个img标签,或者可以通过自己new Image()也可以,这里我也是犯过错的,所以标注了一下,希望大家都能注意一下。

  1. 出现问题,怎么好像我绘制出来的图片和原图区别很大?啊哦。

谈如何用canvas截取图片,新手教程...

  1. 找出原因,因为在截取的时候,我们对比的是图片展示的宽高比例,但是,实际上应该比较的是原始图片的宽高与画框的大小比例,即需要计算,图片原始宽高/画框宽高=大小比例,所以,我们需要知道图片原生宽高,naturalWidth,naturalHeight,以及得到的比例ratioX,ratioY
  // 图片原生宽度
  let naturalWidth = 0
  // 图片原生高度
  let naturalHeight = 0
  // 图片缩放比例
  let ratioX = 0
  let ratioY = 0
  naturalWidth = img.value?.naturalWidth
  naturalHeight = img.value?.naturalHeight
  ratioX = naturalWidth / img.value.width
  ratioY = naturalHeight / img.value.height

起点坐标和画框长宽都需要乘上这个比例

最后一步展示截图区域

img2Url.value = canvas2.toDataURL()

展示完整代码

<template>
  <div class="chat-canvas">
    <canvas class="chat-canvas-box" ref="canvas"></canvas>
    <img :src="imgUrl" alt="" ref="img" class="img" />
  </div>
  <div>
    <input type="file" @change="handleFileChange" />
  </div>
  <div>
    <img class="img2" :src="img2Url" alt="" />
  </div>
</template>

<script setup lang="ts">
  const imgUrl = ref('')
  const canvas = ref(null)
  const img = ref(null)
  const img2Url = ref('')

  onMounted(() => {
    canvasInit()
  })
  // 图片原生宽度
  let naturalWidth = 0
  // 图片原生高度
  let naturalHeight = 0
  // 图片缩放比例
  let ratioX = 0
  let ratioY = 0
  const canvasInit = () => {
    img.value.onload = () => {
      naturalWidth = img.value?.naturalWidth
      naturalHeight = img.value?.naturalHeight
      canvas.value.width = img.value.width
      canvas.value.height = img.value.height
      ratioX = naturalWidth / img.value.width
      ratioY = naturalHeight / img.value.height
    }
    canvas.value.addEventListener('mousedown', handleMouseDown, false)
  }
  // 画框开始坐标
  let startX = 0
  let startY = 0
  // 画框宽度,高度
  let rectWidth = 0
  let rectHeight = 0
  // 鼠标按下
  const handleMouseDown = (e) => {
    startX = e.clientX
    startY = e.clientY
    canvas.value.addEventListener('mousemove', handleMouseMove, false)
    canvas.value.addEventListener('mouseup', handleMouseUp, false)
  }
  // 鼠标移动
  const handleMouseMove = (e) => {
    rectWidth = e.clientX - startX
    rectHeight = e.clientY - startY
    let ctx = canvas.value.getContext('2d')
    ctx.clearRect(0, 0, canvas.value.width, canvas.value.height)
    ctx.fillStyle = '#000'
    ctx.strokeRect(startX, startY, rectWidth, rectHeight)
  }
  // 鼠标抬起
  const handleMouseUp = () => {
    canvas.value.removeEventListener('mousemove', handleMouseMove, false)
    canvas.value.removeEventListener('mouseup', handleMouseUp, false)
    draw()
  }
  // 绘画
  const draw = () => {
    const canvas2 = document.createElement('canvas')
    canvas2.width = 300
    canvas2.height = 300

    const ctx = canvas2.getContext('2d')
    ctx.clearRect(0, 0, 300, 300)
    ctx.drawImage(
      img.value,
      startX * ratioX,
      startY * ratioY,
      rectWidth * ratioX,
      rectHeight * ratioY,
      0,
      0,
      300,
      300,
    )
    img2Url.value = canvas2.toDataURL()
  }
  // 图片上传
  const handleFileChange = (e) => {
    const fileReader = new FileReader()
    fileReader.readAsDataURL(e.target.files[0])
    fileReader.onload = (e) => {
      imgUrl.value = e.target.result
    }
  }
</script>

<style lang="less">
  .chat-canvas {
    position: relative;
  }
  .img {
    width: 300px;
    height: 450px;
  }
  .chat-canvas-box {
    position: absolute;
    top: 0 !important;
    bottom: 0 !important;
    cursor: crosshair;
    // border: 1px solid red;
  }
  .img2 {
    border-radius: 999px;
  }
</style>

结语

看着别人实现总归觉得好简单,当自己一点点摸索出来之后,其实还是有很多小细节点可以探讨的,可能这只是在造轮子,但是,对我来说,也是一个独立思考的过程,至少是自己真正写过之后,才会有信心写这篇文章。加油,共勉!

转载自:https://juejin.cn/post/7397324806708805668
评论
请登录