返回

基于Taro框架实现图片裁剪组件的详细指南

前端

  1. 参数说明

在开始构建组件之前,我们需要先定义组件的参数,以便在使用组件时能够灵活地配置不同的裁剪选项。

参数名 类型 默认值
src String - 待裁剪的图片地址
width Number 300 裁剪框的宽度
height Number 300 裁剪框的高度
aspectRatio Number 1 裁剪框的宽高比
rotatable Boolean false 是否允许旋转图片
zoomable Boolean false 是否允许缩放图片
dragable Boolean false 是否允许拖拽图片
outputType String 'base64' 输出图片的类型,可选值:'base64'、'blob'

2. 界面布局

接下来,我们需要设计组件的界面布局,以便用户能够直观地操作裁剪组件。

<template>
  <div class="image-cropper">
    <div class="image-container">
      <canvas id="canvas"></canvas>
    </div>
    <div class="controls">
      <button @click="rotateImage">旋转</button>
      <button @click="zoomIn">放大</button>
      <button @click="zoomOut">缩小</button>
      <button @click="exportImage">导出</button>
    </div>
  </div>
</template>

在这个布局中,我们定义了一个包含画布(canvas)的容器和一个包含控制按钮的容器。

3. 初始化

在组件的初始化函数中,我们需要进行以下操作:

  • 获取组件的参数
  • 创建一个新的Canvas对象
  • 将待裁剪的图片加载到Canvas中
  • 初始化裁剪框
  • 初始化控制按钮
export default {
  props: {
    src: String,
    width: {
      type: Number,
      default: 300
    },
    height: {
      type: Number,
      default: 300
    },
    aspectRatio: {
      type: Number,
      default: 1
    },
    rotatable: {
      type: Boolean,
      default: false
    },
    zoomable: {
      type: Boolean,
      default: false
    },
    dragable: {
      type: Boolean,
      default: false
    },
    outputType: {
      type: String,
      default: 'base64'
    }
  },
  data() {
    return {
      canvas: null,
      ctx: null,
      image: null,
      cropRect: {
        x: 0,
        y: 0,
        width: this.width,
        height: this.height
      },
      rotateAngle: 0
    }
  },
  mounted() {
    this.initCanvas()
    this.loadImage()
    this.initCropRect()
    this.initControls()
  },
  methods: {
    initCanvas() {
      this.canvas = this.$refs.canvas
      this.ctx = this.canvas.getContext('2d')
    },
    loadImage() {
      const image = new Image()
      image.onload = () => {
        this.image = image
        this.drawCanvas()
      }
      image.src = this.src
    },
    initCropRect() {
      const canvasWidth = this.canvas.width
      const canvasHeight = this.canvas.height
      const cropRectWidth = Math.min(canvasWidth, canvasHeight * this.aspectRatio)
      const cropRectHeight = Math.min(canvasHeight, canvasWidth / this.aspectRatio)
      this.cropRect.width = cropRectWidth
      this.cropRect.height = cropRectHeight
      this.cropRect.x = (canvasWidth - cropRectWidth) / 2
      this.cropRect.y = (canvasHeight - cropRectHeight) / 2
    },
    initControls() {
      const controls = this.$refs.controls
      const buttons = controls.querySelectorAll('button')
      buttons.forEach(button => {
        button.addEventListener('click', this[button.id])
      })
    },
    rotateImage() {
      this.rotateAngle += 90
      this.drawCanvas()
    },
    zoomIn() {
      this.cropRect.width *= 1.1
      this.cropRect.height *= 1.1
      this.drawCanvas()
    },
    zoomOut() {
      this.cropRect.width *= 0.9
      this.cropRect.height *= 0.9
      this.drawCanvas()
    },
    exportImage() {
      const imageData = this.ctx.getImageData(this.cropRect.x, this.cropRect.y, this.cropRect.width, this.cropRect.height)
      const canvas = document.createElement('canvas')
      canvas.width = this.cropRect.width
      canvas.height = this.cropRect.height
      const ctx = canvas.getContext('2d')
      ctx.putImageData(imageData, 0, 0)
      const dataURL = canvas.toDataURL(this.outputType)
      this.$emit('export', dataURL)
    }
  }
}

4. 图片的拖拽功能

为了允许用户拖拽图片,我们需要在组件的mounted函数中添加以下代码:

mounted() {
  // ...其他代码

  const imageContainer = this.$refs.imageContainer
  const image = imageContainer.querySelector('img')

  // 监听图片的mousedown事件
  image.addEventListener('mousedown', (e) => {
    // 计算鼠标相对于图片的偏移量
    const offsetX = e.clientX - image.offsetLeft
    const offsetY = e.clientY - image.offsetTop

    // 记录鼠标按下的位置
    this.dragStartX = offsetX
    this.dragStartY = offsetY

    // 设置图片的capture属性,以便在拖拽图片时仍然能够捕捉鼠标事件
    image.setCapture()

    // 监听鼠标的mousemove和mouseup事件
    document.addEventListener('mousemove', this.onDragMove)
    document.addEventListener('mouseup', this.onDragEnd)
  })
}

在onDragMove和onDragEnd函数中,我们需要更新图片的位置并重新绘制Canvas:

onDragMove(e) {
  // 计算鼠标移动的距离
  const dx = e.clientX - this.dragStartX
  const dy = e.clientY - this.dragStartY

  // 更新图片的位置
  this.image.style.left = `${this.image.offsetLeft + dx}px`
  this.image.style.top = `${this.image.offsetTop + dy}px`

  // 重新绘制Canvas
  this.drawCanvas()
}

onDragEnd() {
  // 释放鼠标的capture属性
  this.image.releaseCapture()

  // 移除鼠标的mousemove和mouseup事件监听器
  document.removeEventListener('mousemove', this.onDragMove)
  document.removeEventListener('mouseup', this.onDragEnd)
}

5. 图片的缩放功能

为了允许用户缩放图片,我们需要在组件的mounted函数中添加以下代码:

mounted() {
  // ...其他代码

  const imageContainer = this.$refs.imageContainer
  const image = imageContainer.querySelector('img')

  // 监听图片的wheel事件
  image.addEventListener('wheel', (e) => {
    // 计算鼠标相对于图片的偏移量
    const offsetX = e.clientX - image.offsetLeft
    const offsetY = e.clientY - image.offsetTop

    // 计算缩放的比例
    const scale = e.deltaY > 0 ? 1.1 : 0.9

    // 更新图片的宽高
    this.image.style.width = `${this.image.offsetWidth * scale}px`
    this.image.style.height = `${this.image.offsetHeight * scale}px`

    // 重新计算裁剪框的位置和宽高
    this.cropRect.width *= scale