当前位置: 首页 > news >正文

利用vue-capper封装一个可以函数式调用图片裁剪组件

1. 效果

	const cropData = await wqCrop({prop:{img,autoCrop: true, // 是否开启截图框maxImgSize: 600,autoCropWidth: 30,canMove: true, // 图片是否可移动canMoveBox: true, // 截图框是否可移动fixedBox: false, // 截图框是否固定}});console.log(cropData);

在这里插入图片描述
在这里插入图片描述

使用wqCrop会开启一个截图弹出框, 当截取完毕后会返回一个 Promise<{file: File, blob:Blob, data: string} | null>, 为截取后的图片

2. 代码

type.ts

export type CropProp = {// 裁剪图片的地址img?: string; // 默认为空,可选值为 url 地址、base64 或 blob// 生成图片的质量outputSize?: number; // 默认值为 1,范围 0.1 ~ 1// 生成图片的格式outputType?: 'jpeg' | 'png' | 'webp'; // 默认值为 'jpg'(需传入 'jpeg')// 是否显示裁剪框的大小信息info?: boolean; // 默认值为 true// 图片是否允许滚轮缩放canScale?: boolean; // 默认值为 true// 是否默认生成截图框autoCrop?: boolean; // 默认值为 false// 默认生成截图框宽度autoCropWidth?: number; // 默认值为容器的 80%,范围 0 ~ max// 默认生成截图框高度autoCropHeight?: number; // 默认值为容器的 80%,范围 0 ~ max// 是否开启截图框宽高固定比例fixed?: boolean; // 默认值为 false// 截图框的宽高比例,开启 `fixed` 生效fixedNumber?: [number, number]; // 默认值为 [1, 1]// 是否输出原图比例的截图full?: boolean; // 默认值为 false// 是否固定截图框大小fixedBox?: boolean; // 默认值为 false// 上传图片是否可以移动canMove?: boolean; // 默认值为 true// 截图框能否拖动canMoveBox?: boolean; // 默认值为 true// 上传图片是否按照原始比例渲染original?: boolean; // 默认值为 false// 截图框是否被限制在图片里面centerBox?: boolean; // 默认值为 false// 是否按照设备的 dpr 输出等比例图片high?: boolean; // 默认值为 true// 是否展示真实输出图片宽高infoTrue?: boolean; // 默认值为 false// 限制图片最大宽度和高度maxImgSize?: number; // 默认值为 2000,范围 0 ~ max// 图片根据截图框输出比例倍数enlarge?: number; // 默认值为 1,范围 0 ~ max(建议不要太大)// 图片默认渲染方式mode?: 'contain' | 'cover' | '100px' | '100%' | 'auto'; // 默认值为 'contain'// 裁剪框限制最小区域limitMinSize?: number | number[] | string; // 默认值为 10// 导出时背景颜色填充fillColor?: string; // 默认为空,可选值为 #ffffff、white
};export type CropEvents = {// 实时预览事件realTime?: (data?: { w: number; h: number }) => void;// 图片移动事件imgMoving?: () => void;// 截图框移动回调函数cropMoving?: () => void;// 图片加载的回调, 返回结果imgLoad?: () => void;
};export type CropOptions = {prop?: CropProp;events?: CropEvents;
};

defaultOptions.ts

// 默认值对象
import { CropOptions } from './type';export const defaultCropOptions: CropOptions = {prop: {img: '',outputSize: 1,outputType: 'jpeg',info: true,canScale: true,autoCrop: false,autoCropWidth: 0,autoCropHeight: 0,fixed: false,fixedNumber: [1, 1],full: false,fixedBox: false,canMove: true,canMoveBox: true,original: false,centerBox: false,high: true,infoTrue: false,maxImgSize: 2000,enlarge: 1,mode: 'contain',limitMinSize: 10,fillColor: '',},events: {// 实时预览事件realTime: () => {},// 图片移动事件imgMoving: () => {},// 截图框移动回调函数cropMoving: () => {},// 图片加载的回调, 返回结果imgLoad: () => true,},
};

crop-dialog.vue

<template><el-dialog v-model="dialogVisible" :before-close="dialogClose" class="crop-dialog"><div class="crop-dialog__container center"><vue-cropper v-bind="options.prop" ref="cropper" v-on="options.events" /></div><template #footer><el-space><el-button type="primary" @click="submitHandle">截取</el-button><el-button @click="dialogClose">取消</el-button></el-space></template></el-dialog>
</template><script setup lang="ts">
import { ref, defineProps, defineExpose, defineEmits } from 'vue';
import { VueCropper } from 'vue-cropper/next';
import 'vue-cropper/next/dist/index.css';
import { CropOptions } from '@/components/CropDialog/type';
import { defaultCropOptions } from '@/components/CropDialog/defaultOptions';type Prop = {options?: CropOptions;
};
const props = withDefaults(defineProps<Prop>(), {options: () => ({ ...defaultCropOptions }),
});
// 定义emits
const emit = defineEmits(['submit', 'closed']);const cropper = ref();const showForm = ref('login');
// 定义表单数据
let dialogVisible = ref(false);// 关闭弹窗
function dialogClose() {dialogVisible.value = false;emit('closed');
}const getCropBlob = (): Promise<Blob> => {return new Promise((resolve, reject) => {cropper.value.getCropBlob((blob: Blob) => {resolve(blob);});});
};
const getCropData = (): Promise<string> => {return new Promise((resolve, reject) => {cropper.value.getCropData((data: string) => {resolve(data);});});
};const getCropFile = () => {const outputType = props.options?.prop?.outputType || 'jpeg';return new Promise((resolve, reject) => {getCropBlob().then((blob) => {resolve(new File([blob], `cropped-image.${outputType}`, { type: `image/${outputType}` }));});});
};const submitHandle = async () => {const file = await getCropFile();const data = await getCropData();const blob = await getCropBlob();emit('submit', { file, data, blob });dialogClose();
};defineExpose({showDialog: () => {dialogVisible.value = true;},hideDialog: () => {dialogVisible.value = false;},
});
</script>
<style lang="scss" scoped>
.crop-dialog {min-width: 314px;background: #eeeeee;color: #999999;&__container {width: 600px;height: 600px;position: relative;display: flex;justify-content: center;}
}
</style>

wq-crop.ts

import { createApp, h, ref } from 'vue';
import CropDialog from './crop-dialog.vue';
import { CropOptions } from './type';
import { defaultCropOptions } from './defaultOptions';type SubmitData = {data: string;file: File;blob: Blob;
};export function wqCrop(options: CropOptions): Promise<SubmitData | null> {const propOptions: CropOptions = {prop: options.prop || defaultCropOptions.prop,events: options.events || defaultCropOptions.events,};// console.log(propOptions);return new Promise((resolve, reject) => {const wqCropRef = ref();const mountNode = document.createElement('div');// const appendTo = document.querySelector('body')document.body.appendChild(mountNode);// 创建节点const app = createApp({render() {return h(CropDialog, {ref: wqCropRef,options: propOptions,onSubmit: (data: SubmitData) => {resolve(data);},onClosed: () => {mountNode.remove();},});},});// 挂载容器,instance就是容器的实例const instance = app.mount(mountNode);wqCropRef.value.showDialog();});
}

3. 最后

使用该组件需要vue-cropper
可以使用 npm install vue-cropper
这里用到是 "vue-cropper": "^0.6.5" 版本

本组件用到了element-uidialog, 这里也可以使用其他的dialog组件

函数传入的参数, 参照type.ts 中的 type CropOptions

如果组件封装有不合理的地方, 或者哪里有问题, 欢迎评论与私信

http://www.lryc.cn/news/450170.html

相关文章:

  • 在系统开发中提升 Excel 数据导出一致性与可维护性的统一规范与最佳实践
  • SpringAOP学习
  • 智能网联汽车飞速发展,安全危机竟如影随形,如何破局?
  • Android常用C++特性之std::function
  • 人工智能与机器学习原理精解【27】
  • XXL-JOB在SpringBoot中的集成
  • 前端工程规范-3:CSS规范(Stylelint)
  • Qt系列-1.Qt安装
  • 《自控原理》最小相位系统
  • SpringBoot3脚手架
  • 【C语言软开面经】
  • YOLOv11训练自己的数据集(从代码下载到实例测试)
  • HTML粉色烟花秀
  • 从零开发操作系统
  • SigmaStudio中部分滤波器算法有效性频谱分析
  • ArcGIS与ArcGIS Pro去除在线地图服务名单
  • 滚雪球学MySQL[10.1讲]:常见问题与解决
  • 利用 Llama-3.1-Nemotron-51B 推进精度-效率前沿的发展
  • SpringBoot+Thymeleaf发票系统
  • Updates were rejected because the tip of your current branch is behind 的解决方法
  • Redis桌面工具:Tiny RDM
  • 【Java】酒店管理系统
  • 【数据库】Java 中 MongoDB 使用指南:步骤与方法介绍
  • MySQL之内置函数
  • JVM 基本组成
  • Ubuntu 离线安装 docker
  • 【C++】set详解
  • C++游戏开发:构建高性能、沉浸式游戏体验的关键
  • 【STM32开发笔记】移植AI框架TensorFlow到STM32单片机【上篇】
  • 第三方供应商不提供API接口?教你四步破解集成难题