Vue3(setup)中使用vue-cropper图片上传裁剪插件,复制代码直接使用

news2024/11/15 21:22:26

最近在项目中用到上传裁剪,看了一下代码,觉得这插件可可以。梳理了一下代码分享给大家

前端UI组件element-plus

如果你也用到了 ,快速帮你解决了问题,别忘记点赞收藏

1.首先看效果图
在这里插入图片描述
在这里插入图片描述

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

  1. 因为版本vue-cropper 众多 ,虽然网上有各种写法 为了保证能运行 直接copy代码就能用
  2. 直接在package.json中添加依赖 "vue-cropper": "^1.0.9",
  3. 在这里插入图片描述
    然后安装依赖npm install

5 创建一个公用组件

imgcropper.vue

<template>

  <el-dialog
    v-model="isModel"
    title="裁剪图片"
    width="1000px"
    destroy-on-close
    center
    draggable
  >
    <div class="main">
      <div class="tip">
        <div class="tip-text">上传说明"vue-cropper": "^1.0.9"版本</div>
      </div>
      <el-row :gutter="20">
        <el-col span="14">
          <div class="mainbox">
            <vue-cropper
              ref="cropper"
              :img="option.img"
              :outputSize="option.outputSize"
              :outputType="option.outputType"
              :info="option.info"
              :canScale="option.canScale"
              :autoCrop="option.autoCrop"
              :autoCropWidth="option.autoCropWidth"
              :autoCropHeight="option.autoCropHeight"
              :fixed="option.fixed"
              :fixedNumber="option.fixedNumber"
              :full="option.full"
              :fixedBox="option.fixedBox"
              :canMove="option.canMove"
              :canMoveBox="option.canMoveBox"
              :original="option.original"
              :centerBox="option.centerBox"
              :height="option.height"
              :infoTrue="option.infoTrue"
              :maxImgSize="option.maxImgSize"
              :enlarge="option.enlarge"
              :mode="option.mode"
              @realTime="realTime"
             
            >
            </vue-cropper>
            <div class="main-btn">
              <el-button  size="sm" @click="rotateLeft"
                >↺ 向左旋转15°</el-button
              >
              <el-button  size="sm" @click="rotateRight"
                >↻ 向左旋转15°</el-button
              >
              <el-button  size="sm" @click="changeScale(1)"
                ><el-icon type="elui-icon-add-circle" color="#fff"></el-icon
                >+ 放大</el-button
              >
              <el-button  size="sm" @click="changeScale(-1)" >- 缩小</el-button >
              <el-button type="danger" size="sm" @click="changeReset"  >重置</el-button >
             
            </div>
          </div>
        </el-col>
        <el-col span="10">
          <div class="preview-model">
            <div class="preview" style="width: 200px; height: 200px" v-if="previews.url">
         
              <el-image
                lazy
                style="width: 100px; height: 100px"
                :src="previews.url"
                :zoom-rate="1.2"
                :max-scale="7"
                :min-scale="0.2"
                :preview-src-list="srcList"
                :initial-index="4"
                @load="lookImg"
            />
            </div>
            <div class="upload-btn">
              <el-upload v-model="file"  :on-change="selectImg">
                  <el-button type="primary" size="sm">选择图片</el-button>
              </el-upload>
              <el-button type="success" style="margin-left: 10px;" size="sm" @click="uploadFile">确认上传</el-button>
            </div>
          </div>
        </el-col>
      </el-row>
    </div>
  </el-dialog>

</template>
<script setup>
import { ref, reactive ,watch,defineExpose} from "vue";
import { VueCropper } from "vue-cropper";

import "vue-cropper/dist/index.css";
const isModel = ref(false);
const name = ref();
const file = ref();
const previews = ref({});
const previewImg = ref();
const cropper = ref(null);
const emit = defineEmits([""]);

const option = reactive({
  img: "",
  name: "",
  outputSize: 1, //裁剪生成图片的质量(可选0.1 - 1)
  outputType: "png", //裁剪生成图片的格式(jpeg || png || webp)
  info: true, //图片大小信息
  canScale: true, //图片是否允许滚轮缩放
  autoCrop: true, //是否默认生成截图框
  autoCropWidth: 200, //默认生成截图框宽度
  autoCropHeight: 200, //默认生成截图框高度
  fixed: false, //是否开启截图框宽高固定比例
  fixedNumber: [1.53, 1], //截图框的宽高比例
  full: false, //false按原比例裁切图片,不失真
  fixedBox: false, //固定截图框大小,不允许改变
  canMove: true, //上传图片是否可以移动
  canMoveBox: true, //截图框能否拖动
  original: false, //上传图片按照原始比例渲染
  centerBox: true, //截图框是否被限制在图片里面
  height: false, //是否按照设备的dpr 输出等比例图片
  infoTrue: false, //true为展示真实输出图片宽高,false展示看到的截图框宽高
  maxImgSize: 3000, //限制图片最大宽度和高度
  enlarge: 1, //图片根据截图框输出比例倍数
  mode: "100% 100%", //图片默认渲染方式
});

//预览
const srcList = ref([])


//当再次打开时 清空图片
watch(isModel, (val) => {
  if (!val) {
    previews.value = {};
    option.img = ''
    srcList.value = []
  }
});

//实时预览
const realTime = (data) => {
  previews.value = data;
  option.autoCropWidth = data.img.width;
  option.autoCropHeight = data.img.height;
};




//图片缩放
const changeScale = (num) => {
  num = num || 1;
  cropper.value.changeScale(num);
};

// 向左旋转
const rotateLeft = () => {
 cropper.value.rotate = cropper.value.rotate - 15 / 90;
 //直接旋转90度
  cropper.value.rotateLeft()
};

//向右旋转
const rotateRight = () => {
  cropper.value.rotate = cropper.value.rotate + 15 / 90;
  //直接旋转90度
  // cropper.value.rotateRight()
};

// 重置
const changeReset = () => {
  cropper.value.refresh();
};

//图片预览图片
const lookImg = () => {
  cropper.value.getCropBlob((data) => {
    file.value = data;
    let img = window.URL.createObjectURL(data);
    srcList.value.push(img)
  });
};


//选择图片
const selectImg = (e) => {
  //这里需要注意 每个UI组件 返回的数据格式不一样   需要什么取什么
  srcList.value = [];
  option.img = URL.createObjectURL(e.raw);
  option.name = e.raw.name;
 
};

/**
 * @function 确认上传
 *
 * */
const uploadFile = () => {
  cropper.value.getCropBlob((data) => {
    if (data) {
      data.fileName = option.name;
    }
    emit("uploadImgSuccess", data);
  });
};

/**
 * @function 开启弹窗
 * */
const open = () => {
  isModel.value = true;
};

/**
 * @function 关闭
 * */
const close = () => {
  isModel.value = false;
};

defineExpose({
  open,
  close,
});
</script>
<style lang="scss" scoped>
.main {
  box-sizing: border-box;
  min-height: 450px;
  padding: 20px;

  .tip {
    display: flex;
    align-items: center;
    margin-bottom: 20px;

    .tip-text {
      color: red;
    }
  }

  .mainbox {
    width: 100%;
    height: 300px;
  }

  .preview-model {
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    align-items: center;

    .preview {
      overflow: hidden;
    }

    .upload-btn {
      display: flex;
      width: 100%;
      justify-content: space-evenly;
      margin-top: 20px;
    }
  }

  .main-btn {
    display: flex;
    justify-content: center;
    margin-top: 20px;
  }
}

</style>

直接在页面中使用 例如index.vue

index.vue 


<template>
    <div class='content'>
          <el-button type="danger" @click="addimg">上传图片 </el-button>
          <img v-if="imgurl" :src="imgurl" alt="" width='200' height="100">
          <comCutimgcropper   ref="imgref"    @uploadImgSuccess="uploadImgSuccess"  ></comCutimgcropper>
    </div>
</template>
  
<script setup>
import comCutimgcropper from '../components/imgcropper.vue'
import { ref ,watch} from 'vue'
import {useRoute, useRouter} from 'vue-router'
const router = useRouter();
const imgref = ref(null);
const imgurl = ref("");

const addimg = () => {
  imgref.value.open();
}

const uploadImgSuccess = data => {
  //调用接口
  // uploadImage(data)
  
  //这里为了展示 直接 写死一张图片 关闭弹框
  imgurl.value = "https://img2.baidu.com/it/u=3354585195,1512541150&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1700845200&t=636a3f6d0f2d46b6753a203f48801a98"
  imgref.value.close()

}
const uploadImage = async data => {
  console.log(data)
  //这里就是跟后台约定好需要传递什么参数
  const params = {
    file: data,
    fileName: data.fileName, 
  }
  //上传接口
  const res = await uploads(params);
  if (res && res.code === 200) {
    //页面上显示图片 后端返回的图片地址
    imgurl.value = res.data.url;
    //关闭弹窗
    imgref.value.close()
  }
}


/**
 * axiso 封装一般这么写 都是采用 FormData的形式  注意请求头 headers    'Content-Type': 'multipart/form-data'
 * 
 * export const uploads = params => {
  const data = new FormData()
  data.append('file', params.file, params.fileName)
  return request({
    headers: {
      'Content-Type': 'multipart/form-data'
    },
    url: '/,
    method: 'post',
    data,
  })
}
 */

</script>


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1243234.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端 计算机基础篇 ( 二 )

文章目录 websockt及原理ipv4和ipv6的区别线程和进程的区别cdn原理缓存所涉及的http状态码缓存的时候设置 no-store和no-cache和max-age0这几个有什么区别token一般存放在哪儿怎么设置强缓存和协商缓存强缓存&#xff1a;1. 使用 Cache-Control 头字段&#xff1a; 协商缓存&am…

unordered_map 与 unordered_set 的使用

unordered_map unordered_map 的介绍文档 unordered_map 的介绍文档&#xff1a;来自cpluscplus.com 的中文翻译 unordered_map是存储<key, value>键值对的关联式容器&#xff0c;其允许通过keys快速的索引到与 其对应的value。在unordered_map中&#xff0c;键值通常用…

码云 -- 本地代码上传到码云

1. 在码云上创建远程仓库 复制远程仓库地址 2. 在本地代码上创建 git 仓库 在本地代码文件夹上&#xff0c;打开git 命令窗口 输入初始化命令&#xff0c;创建 git 仓库 git init3. 给 git 仓库添加远程仓库 继续输入 git 命令 git remote add origin 远程仓库地址4. 按 git 的…

数字逻辑电路基础-时序逻辑电路之触发器

文章目录 一、D触发器二、verilog源码三、综合及仿真结果一、D触发器 本文介绍数字逻辑电路中常用的基础时序逻辑电路触发器。它有记忆和存储信息功能,触发器是边沿触发电路。 下图是触发器常用表示方式(时钟上升沿有效): 触发器由两个锁存器组成,前级是主锁存器,后级…

Git工作流和Commit规范

Git大家都非常熟悉了&#xff0c;就不做过多介绍&#xff0c;但是如何用好Git、如何进行合理的分支开发、Merge你是否有一个规范流程呢&#xff1f;&#x1f4a4; 不论是一个团队一起开发一个项目&#xff0c;还是自己独立开发一个项目&#xff0c;都少不了要和Git打交道&…

C++学习笔记——static对象

一、static对象——累计创建了多少对象和正在使用的对象数目 static更多的是作为一个介入类的一个第三方成员变量。 声明为static的类成员称为类的静态成员&#xff0c;用static修饰的成员变量&#xff0c;称之为静态成员变量&#xff1b; 用static修饰的成员函数&#xff0c;称…

JS逆向之wasm逆向(二)

本文仅供技术交流和技术学习 不做其他用途 接着上一篇继续讲&#xff1a; 上篇地址&#xff1a; JS逆向之wasm逆向(二进制) 网址&#xff1a; aHR0cHM6Ly93d3cuN3E2Y3lqLmNvbTo5MDAxL3JlZ2lzdGVyNDY5Njg/aV9jb2RlPTQ0Mjc5OTU1 这个网站我们后面可以继续讲他的debugger 和滑块…

Spring Boot 整合MyBatis-Plus 详解

MyBatis-Plus (opens new window)&#xff08;简称 MP&#xff09;是一个 MyBatis (opens new window)的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。 全新的 MyBatis-Plus 3.0 版本基于 JDK8&#xff0c;提供了 lambda 形…

【数据结构】树和二叉树详解

目录 1. 前言2. 树概念及结构2.1. 树的概念2.2. 与树有关的一些概念2.3. 树与非树2.4. 树的表示 3. 二叉树概念及结构3.1. 二叉树概念3.2. 特殊的二叉树3.3. 二叉树的性质3.4 二叉树的存储结构3.4.1 顺序存储3.4.2 链式存储 3.5 堆 1. 前言 在前面我们一起了解的数据结构有顺序…

振南技术干货集:制冷设备大型IoT监测项目研发纪实(6)

注解目录 1.制冷设备的监测迫在眉睫 1.1 冷食的利润贡献 1.2 冷设监测系统的困难 &#xff08;制冷设备对于便利店为何如何重要&#xff1f;了解一下你所不知道的便利店和新零售行业。关于电力线载波通信的论战。&#xff09; 2、电路设计 2.1 防护电路 2.1.1 强电防护 …

初识JVM(简单易懂),解开JVM神秘的面纱

目录 一、什么是JVM&#xff08;Java虚拟机&#xff09;&#xff1f; 二、JVM的功能 三、JVM的功能-即时编译 四、常见的JVM 五、JVM的组成 五、JVM的工作流程 参考资料 一、什么是JVM&#xff08;Java虚拟机&#xff09;&#xff1f; 在Java的世界里&#xff0c;Java虚…

【实验】配置用户自动获取IPv6地址的案例

【赠送】IT技术视频教程&#xff0c;白拿不谢&#xff01;思科、华为、红帽、数据库、云计算等等​编辑https://xmws-it.blog.csdn.net/article/details/117297837?spm1001.2014.3001.5502https://xmws-it.blog.csdn.net/article/details/117297837?spm1001.2014.3001.5502【…

云贝教育 |【技术文章】PG的缓存管理器原理

PG的缓存管理器原理 缓冲区管理器管理共享内存和持久存储之间的数据传输 &#xff0c; 它会对 DBMS 的性能产生重大影响。 PostgreSQL 缓冲区管理器工作非常高效。 本章介绍 PostgreSQL 缓冲区管理器。第一部分提供概述 &#xff0c;后续部分描述以下主题&#xff1a; 缓冲…

OSG动画与声音-路径动画之导出与导入(2)

路径的导出示例 路径的导出示例的代码如程序清单10-2所示。 1. // 创建路径 2. osg::ref_ptr<osg::AnimationPath> createAnimationPath(osg::Vec3 ¢er, 3. float radius, float looptime) 4. { 5. // 创建一个Path对象 6. osg::ref_ptr<…

【史上最细教程】服务器MySQL数据库完成主从复制

文章目录 MySQL完成主从复制教程准备&#xff1a;原理&#xff1a;步骤&#xff1a; 推荐文章 MySQL完成主从复制教程 主从复制&#xff08;也称 AB 复制&#xff09;就是将一个服务器&#xff08;主服务器&#xff09;的数据复制到一个或多个MySQL数据库服务器&#xff08;从…

云安全之盾:ZStack 云主机安全防护解决方案全方位保护云环境

随着云计算的蓬勃发展&#xff0c;网络威胁愈发复杂&#xff0c;涵盖了从勒索病毒到APT攻击的各种威胁类型。在这一风云变幻的网络安全环境下&#xff0c;云主机安全不再仅仅是一个选项&#xff0c;它是信息系统安全的核心要素。云轴科技ZStack 云主机安全防护解决方案是为了满…

java项目之品牌银饰售卖平台(ssm+vue)

项目简介 主要功能包括首页、个人中心、用户管理、促销活动管理、饰品管理、我的收藏管理、系统管理、订单管理等。管理员模块: 管理员可以查询、编辑、管理每个用户的信息和系统管理员自己的信息&#xff0c;同时还可以编辑、修改、查询用户账户和密码&#xff0c;以及对系统…

项目管理套路:看这一篇绝对够用❤️

写论文必不可少的&#xff0c;就是创建代码并进行实验。好的项目管理可以让实验进行得更加顺利。本篇博客以一次项目实践为例&#xff0c;介绍项目管理的方法&#xff0c;以及可能遇到的问题&#xff0c;并提供一些可行的解决方案。 目录 项目管理工具开始第一步版本管理十分关…

微信小程序:This Mini Program cannot be opened as your Weixin version is out-of-date.

项目场景&#xff1a; 问题描述 升级基础库3.2.0&#xff0c;然后PC端整个小程序都打不开了&#xff0c;点击小程序提示”This Mini Program cannot be opened as your Weixin version is out-of-date. Update Weixin to the latest version.“&#xff0c;并且点击Update Wei…

成为AI产品经理——模型构建流程(下)

目录 1.模型训练 2.模型验证 3.模型融合 4.模型部署 上节课我们讲了模型设计、特征工程&#xff0c;这节课我们来讲模型构建剩下的三个部分&#xff1a;模型训练、模型验证和模型融合。 1.模型训练 模型训练就是要不断地训练、验证、调优直至让模型达到最优。 那么怎么达…