Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

v-avatar-cropper

varjay85ISC1.0.0

Support cutting of avatars of PC and mobile phones.

avatar-crop, 头像裁剪, 微信头像裁剪, wechat, vue3, vue-component, image-cropper, avatar-editor, mobile-friendly, touch-gestures, responsive, 无依赖, 移动端, 轻量级

readme

V Avatar Cropper

演示动图

一个支持 PC 端和移动端的 Vue 3 头像裁剪组件,提供直观的拖拽、缩放和裁剪功能。

✨ 特性

  • 🖱️ 直观操作 - 支持鼠标拖拽和触摸手势
  • 🔍 灵活缩放 - 双指缩放和滑块缩放
  • 📱 响应式设计 - 自适应不同屏幕尺寸
  • 🎨 现代 UI - 简洁美观的用户界面
  • 📦 轻量级 - 无外部依赖,体积小巧
  • 🔧 易于集成 - 简单的 API 设计

📦 安装

npm install v-avatar-cropper --save

🚀 快速开始

全局注册

import {createApp} from 'vue'
import avatar from 'v-avatar-cropper'
import App from './App.vue'

const app = createApp(App)
app.use(avatar)
app.mount('#app')

局部引入

import VAvatarCropper from 'v-avatar-cropper'

export default {
  components: {
    VAvatarCropper,
  },
}

基础用法

<template>
  <div>
    <!-- 文件选择 -->
    <input type="file" @change="handleFileChange" accept="image/*" />

    <!-- 头像裁剪组件 -->
    <v-avatar-cropper v-if="selectedFile" :file="selectedFile" @cancel="handleCancel" @done="handleDone" />
  </div>
</template>

<script setup>
import {ref} from 'vue'

const selectedFile = ref(null)

function handleFileChange(event) {
  selectedFile.value = event.target.files[0]
}

function handleCancel() {
  selectedFile.value = null
}

function handleDone(result) {
  console.log('裁剪完成:', result)
  // result.base64 - base64格式图片
  // result.img - Blob格式图片
  // result.url - Blob URL地址

  selectedFile.value = null
}
</script>

📖 API 文档

Props

参数 类型 默认值 说明
file File | String null 要裁剪的图片文件或图片 URL
edit Boolean true 是否显示编辑界面(操作按钮和滑块)

Events

事件名 参数 说明
cancel - 用户点击取消按钮时触发
done result: Object 用户确认裁剪时触发
touch - 用户触摸图片时触发

Done 事件返回值

{
  base64: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ...', // base64格式图片
  img: Blob,                                                    // Blob格式图片对象
  url: 'blob:http://localhost:3000/xxx-xxx-xxx'               // Blob URL地址
}

🎮 使用示例

完整示例

<template>
  <div class="avatar-demo">
    <!-- 上传区域 -->
    <div v-if="!currentFile" class="upload-area">
      <label for="file-input" class="upload-button"> 选择头像图片 </label>
      <input id="file-input" type="file" @change="selectFile" accept="image/*" style="display: none;" />
    </div>

    <!-- 裁剪组件 -->
    <v-avatar-cropper v-if="currentFile" :file="currentFile" @cancel="cancelCrop" @done="completeCrop" @touch="onImageTouch" />

    <!-- 结果展示 -->
    <div v-if="croppedResult" class="result-area">
      <h3>裁剪结果</h3>
      <img :src="croppedResult.url" alt="裁剪后的头像" class="result-image" />
      <div class="result-actions">
        <button @click="downloadImage">下载图片</button>
        <button @click="resetDemo">重新选择</button>
      </div>
    </div>
  </div>
</template>

<script setup>
import {ref} from 'vue'

const currentFile = ref(null)
const croppedResult = ref(null)

// 选择文件
function selectFile(event) {
  const file = event.target.files[0]
  if (file) {
    currentFile.value = file
  }
}

// 取消裁剪
function cancelCrop() {
  currentFile.value = null
}

// 完成裁剪
function completeCrop(result) {
  croppedResult.value = result
  currentFile.value = null

  // 可以在这里上传到服务器
  // uploadToServer(result.img)
}

// 图片触摸事件
function onImageTouch() {
  console.log('用户触摸了图片')
}

// 下载图片
function downloadImage() {
  if (croppedResult.value) {
    const link = document.createElement('a')
    link.download = 'avatar.jpg'
    link.href = croppedResult.value.url
    link.click()
  }
}

// 重置演示
function resetDemo() {
  croppedResult.value = null
  // 清空文件输入
  const input = document.getElementById('file-input')
  if (input) input.value = ''
}
</script>

<style scoped>
.avatar-demo {
  max-width: 500px;
  margin: 0 auto;
  padding: 20px;
}

.upload-area {
  text-align: center;
  padding: 40px;
  border: 2px dashed #ddd;
  border-radius: 8px;
}

.upload-button {
  display: inline-block;
  padding: 12px 24px;
  background: #007bff;
  color: white;
  border-radius: 6px;
  cursor: pointer;
  transition: background 0.3s;
}

.upload-button:hover {
  background: #0056b3;
}

.result-area {
  text-align: center;
  margin-top: 20px;
}

.result-image {
  width: 200px;
  height: 200px;
  border-radius: 50%;
  object-fit: cover;
  border: 3px solid #007bff;
}

.result-actions {
  margin-top: 15px;
}

.result-actions button {
  margin: 0 10px;
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
</style>

仅预览模式

<template>
  <v-avatar-cropper :file="imageUrl" :edit="false" />
</template>

<script setup>
const imageUrl = 'https://example.com/avatar.jpg'
</script>

🎯 操作指南

PC 端操作

  • 拖拽移动: 鼠标按住图片拖拽
  • 缩放调节: 使用底部滑块调节缩放比例
  • 确认裁剪: 点击"确认"按钮完成裁剪
  • 取消操作: 点击"取消"按钮退出裁剪

移动端操作

  • 单指拖拽: 单指触摸拖拽移动图片位置
  • 双指缩放: 双指捏合/展开调节图片大小
  • 滑块缩放: 使用底部滑块精确调节缩放
  • 触摸确认: 点击"确认"按钮完成裁剪

⚙️ 技术特性

核心功能

  • 智能适配: 自动适配图片尺寸和屏幕大小
  • 边界检测: 防止图片移出裁剪区域
  • 平滑动画: 流畅的交互动画效果
  • 内存管理: 自动清理 Blob URL,防止内存泄漏

兼容性

  • Vue 版本: Vue 3.x
  • 浏览器: 现代浏览器 (Chrome, Firefox, Safari, Edge)
  • 移动端: iOS Safari, Android Chrome
  • 触摸支持: 完整的触摸手势支持

🔧 开发指南

本地开发

# 克隆项目
git clone https://github.com/varjay/avatar-crop.git

# 安装依赖
cd avatar-crop
npm install

# 启动开发服务器
npm run dev

# 构建项目
npm run build

# 代码检查
npm run lint

项目结构

avatar-crop/
├── lib/                    # 组件源码
│   ├── index.js           # 入口文件
│   └── components/
│       └── avatar.vue     # 主组件
├── src/                   # 示例代码
│   ├── App.vue           # 示例应用
│   └── main.js           # 入口文件
├── public/               # 静态资源
└── package.json          # 项目配置

🤝 贡献指南

欢迎提交 Issue 和 Pull Request 来帮助改进这个项目!

提交 Issue

  • 详细描述问题和复现步骤
  • 提供浏览器和设备信息
  • 如果可能,请提供最小复现示例

提交 PR

  • Fork 项目并创建特性分支
  • 确保代码通过 ESLint 检查
  • 添加必要的测试用例
  • 更新相关文档

📄 许可证

ISC License

👨‍💻 作者

virjay - GitHub


如果这个项目对你有帮助,请给个 ⭐️ 支持一下!