cropperjs 裁剪/框选图片

news2024/11/24 16:24:04

1.效果

在这里插入图片描述

2.使用组件

<!-- 父级 -->
 <Cropper ref="cropperRef" :imgUrl="url" @searchImg="searchImg"></Cropper>

3.封装组件

<template>
  <el-dialog :title="title" :visible.sync="dialogVisible" width="1000px">
    <input ref="input" type="file" name="image" @change="setImage" />
    <div class="flex justify-around">
      <div class="w-480px h-270px flex justify-center items-center">
        <div
          v-show="!imgSrc"
          @click="showFileChooser"
          class="w-full h-full flex cursor-pointer justify-center items-center border-1px border-dashed border-gray-300 rounded-lg"
        >
          <i class="font-size-20px el-icon-plus avatar-uploader-icon"></i>
        </div>
        <!-- :aspect-ratio="16 / 16" -->
        <vue-cropper
          v-show="imgSrc"
          class="w-full h-full"
          ref="cropper"
          :src="imgSrc"
          alt="Source Image"
          @ready="ready"
          @cropstart="cropstart"
          @cropmove="cropmove"
          @cropend="cropend"
          @crop="crop"
          @zoom="zoom"
          preview=".preview"
          :autoCropArea="autoCropArea"
        >
        </vue-cropper>
      </div>
      <div class="w-420px">
        <div class="font-bold color-#666 ml-20px mb-10px">预览</div>
        <div v-show="!imgSrc" class="preview_empty ml-20px"></div>
        <div v-show="imgSrc" class="preview ml-20px"></div>
        <!-- <div>裁剪图片</div>
        <div class="cropped-image">
          <el-image class="h-180px" v-if="cropImg" :src="cropImg" alt="Cropped Image" />
          <div v-else class="crop-placeholder" />
        </div> -->
        <div class="actions mt-10px ml-10px">
          <el-button class="mb-10px ml-10px" type="primary" @click="zoom(0.2)" size="small">放大</el-button>
          <el-button class="mb-10px" type="primary" @click="zoom(-0.2)" size="small">缩小</el-button>
          <el-button class="mb-10px" type="primary" @click="move(-10, 0)" size="small">左移</el-button>
          <el-button class="mb-10px" type="primary" @click="move(10, 0)" size="small">右移</el-button>
          <el-button class="mb-10px" type="primary" @click="move(0, -10)" size="small">上移</el-button>
          <el-button class="mb-10px" type="primary" @click="move(0, 10)" size="small">下移</el-button>
          <el-button class="mb-10px" type="primary" @click="rotate(90)" size="small">旋转90°</el-button>
          <el-button class="mb-10px" type="primary" @click="rotate(-90)" size="small">旋转-90°</el-button>
          <!-- <el-button class="mb-10px" type="primary" @click="flipX" size="small">水平翻转</el-button>
          <el-button class="mb-10px" type="primary" @click="flipY" size="small">垂直翻转</el-button> -->
          <!-- <el-button class="mb-10px" type="success" @click="cropImage" size="small">搜索</el-button> -->
          <el-button class="mb-10px" type="primary" @click="reset" size="small" plain>重置</el-button>
          <el-button
            v-if="!isHideFileChooser"
            class="mb-10px"
            type="success"
            @click="showFileChooser"
            size="small"
            plain
            >更换图片</el-button
          >

          <!-- <el-button class="mb-10px" type="primary" @click="getCropBoxData" size="small">获取裁剪框数据</el-button>
          <el-button class="mb-10px" type="primary" @click="setCropBoxData" size="small">设置裁剪框数据</el-button>
          <el-button class="mb-10px" type="primary" @click="getData" size="small">获取裁剪数据</el-button>
          <el-button class="mb-10px" type="primary" @click="setData" size="small">设置裁剪数据</el-button> -->
        </div>
      </div>
    </div>

    <span slot="footer" class="dialog-footer">
      <el-button size="small" @click="dialogVisible = false">取 消</el-button>
      <el-button size="small" type="primary" @click="cropImage">搜索</el-button>
    </span>
  </el-dialog>
</template>

<script>
import VueCropper from 'vue-cropperjs'
import 'cropperjs/dist/cropper.css'

export default {
  name: 'Cropper',
  components: { VueCropper },
  props: {
    title: {
      type: String,
      default: '图片框选'
    },
    imgUrl: {
      type: String,
      default: ''
    },
    autoCropArea: {
      type: Number,
      default: 0.6
    },
    isHideFileChooser: {
      type: Boolean,
      default: true
    }
  },
  data() {
    return {
      imgSrc: '',
      dialogVisible: false,
      cropImg: ''
    }
  },
  watch: {
    imgUrl(val) {
      if (val) {
        this.imgSrc = val
        console.log('🚀 ~ imgUrl ~ this.imgSrc:', this.imgSrc)
      }
    }
  },
  methods: {
    open() {
      if (!this.imgUrl) {
        this.imgSrc = ''
      }
      this.dialogVisible = true
    },
    handleClose() {
      this.$emit('close')
    },
    ready() {
      // console.log('🚀 ~ ready ~ this.$refs.cropper:', this.$refs.cropper)
    },
    cropImage() {
      // get image data for post processing, e.g. upload or setting image src
      this.cropImg = this.$refs.cropper.getCroppedCanvas().toDataURL()
      const base64Data = this.cropImg.split(',')[1]
      this.$emit('searchImg', base64Data)
      this.dialogVisible = false
    },
    cropstart() {
      // console.log('🚀 ~ cropstart ~')
    },
    cropmove() {
      // console.log('🚀 ~ cropmove ~')
    },
    cropend() {
      // console.log('🚀 ~ cropend ~')
    },
    crop(data) {
      // console.log('🚀 ~ crop ~ data:', data)
    },
    flipX() {
      const dom = this.$refs.flipX
      let scale = dom.getAttribute('data-scale')
      scale = scale ? -scale : -1
      this.$refs.cropper.scaleX(scale)
      dom.setAttribute('data-scale', scale)
    },
    flipY() {
      const dom = this.$refs.flipY
      let scale = dom.getAttribute('data-scale')
      scale = scale ? -scale : -1
      this.$refs.cropper.scaleY(scale)
      dom.setAttribute('data-scale', scale)
    },
    getCropBoxData() {
      this.data = JSON.stringify(this.$refs.cropper.getCropBoxData(), null, 4)
    },
    getData() {
      this.data = JSON.stringify(this.$refs.cropper.getData(), null, 4)
      console.log('🚀 ~ getData ~ this.data:', this.data)
    },
    move(offsetX, offsetY) {
      this.$refs.cropper.move(offsetX, offsetY)
    },
    reset() {
      this.$refs.cropper.reset()
    },
    rotate(deg) {
      this.$refs.cropper.rotate(deg)
    },
    setCropBoxData() {
      if (!this.data) return
      this.$refs.cropper.setCropBoxData(JSON.parse(this.data))
    },
    setData() {
      if (!this.data) return
      this.$refs.cropper.setData(JSON.parse(this.data))
    },
    setImage(e) {
      const file = e.target.files[0]
      if (file.type.indexOf('image/') === -1) {
        alert('Please select an image file')
        return
      }
      if (typeof FileReader === 'function') {
        const reader = new FileReader()
        reader.onload = (event) => {
          this.imgSrc = event.target.result
          // rebuild cropperjs with the updated source
          this.$refs.cropper.replace(event.target.result)
        }
        reader.readAsDataURL(file)
      } else {
        alert('Sorry, FileReader API not supported')
      }
    },
    showFileChooser() {
      this.$refs.input.click()
    },
    zoom(percent) {
      this.$refs.cropper.relativeZoom(percent)
    }
  },
  mounted() {
    this.imgSrc = this.imgUrl
  }
}
</script>

<style lang="scss" scoped>
input[type='file'] {
  display: none;
}

.preview-area {
  width: 100%;
}

.preview-area p {
  font-size: 1.25rem;
  margin: 0;
  margin-bottom: 1rem;
}

.preview-area p:last-of-type {
  margin-top: 1rem;
}

.preview {
  width: 270px;
  height: calc(270px * (9 / 16));
  overflow: hidden;
  background-color: #f5f5f5;
}

.preview_empty {
  width: 270px;
  height: calc(270px * (9 / 16));
  overflow: hidden;
  background-color: #f5f5f5;
}
.crop-placeholder {
  width: 100%;
  height: 200px;
  background: #ccc;
}

.cropped-image img {
  max-width: 100%;
}
</style>

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

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

相关文章

亚马逊云科技官方活动:一个月拿下助理架构师SAA+云从业者考试认证(送半价折扣券)

为了帮助大家考取AWS SAA和AWS云从业者认证&#xff0c;小李哥争取到了大量考试半价50%折扣券&#xff0c;使用折扣券考试最多可省75刀(545元人民币)。 领取折扣券需要加入云师兄必过班群&#xff0c;在群中免费领取。目前必过班群招募到了超过200名小伙伴&#xff0c;名额有限…

前端自动化

前端自动化的内容 自动化代码检查自动化测试自动化构建自动化部署自动化文档 前端自动化的最佳实践

(六)使用统计学方法进行变量有效性测试(43道选择题)

本文整理了使用统计学方法进行变量有效性测试相关的练习题&#xff0c;共43道&#xff0c;适用于想巩固理论基础的同学。来源&#xff1a;如荷学数据科学题库&#xff08;CDA二级-第7章&#xff09;。 1&#xff09; 2&#xff09; 3&#xff09; 4&#xff09; 5&#xff09;…

CS-流量通讯特征修改-端口store证书流量通讯规则

免责声明:本文仅做技术交流与学习... 目录 1.修改默认端口&#xff1a; 2.去除store证书特征&#xff1a; 查看证书指纹&#xff1a; 生成证书指纹&#xff1a; 应用证书指纹&#xff1a; 3.去除流量通讯特征&#xff1a; 规则资源 http流量特征修改: https流量特征修改:…

营销效果大揭秘:评估你的市场营销活动是否达标

在营销的世界里&#xff0c;每一次活动都是一场精心策划的表演&#xff0c;而评估活动的效果就是我们的幕后总结会。 作为营销人员&#xff0c;我们需要问自己&#xff1a;我们为什么要做营销效果评估&#xff1f; 答案很简单&#xff0c;我们评估是为了总结经验、为未来的营…

Python 全栈体系【四阶】(六十一)

第五章 深度学习 十三、自然语言处理&#xff08;NLP&#xff09; 5. NLP应用 5.2 文本情感分析 目标&#xff1a;利用训练数据集&#xff0c;对模型训练&#xff0c;从而实现对中文评论语句情感分析。情绪分为正面、负面两种 数据集&#xff1a;中文关于酒店的评论&#…

[20] Opencv_CUDA应用之 关键点检测器和描述符

Opencv_CUDA应用之 关键点检测器和描述符 本节中会介绍找到局部特征的各种方法&#xff0c;也被称为关键点检测器关键点(key-point)是表征图像的特征点&#xff0c;可用于准确定义对象 1. 加速段测试特征功能检测器 FAST算法用于检测角点作为图像的关键点&#xff0c;通过对…

【数据库编程】Derby数据库的部署【用 ij 工具创建数据库】

Java有一个内置的Derby数据库&#xff0c;是一个完全用Java语言编写的、功能强大的微型数据库&#xff0c;其基础引擎和内嵌的JDBC驱动总共大约2MB大小。Derby为用户提供了轻量的标准数据库引擎&#xff0c;它可以紧密地嵌入到任何基于Java的解决方案中。 Derby的特性令人惊奇&…

Ubuntu20.04离线安装dpkg

方法一&#xff1a;百度云盘下载离线安装包 链接&#xff1a;https://pan.baidu.com/s/1L7TaFwE35bMfOJbXmJcWwQ 提取码&#xff1a;mjsm --来自百度网盘超级会员V4的分享 方法二&#xff1a;找一台联网计算机&#xff0c;自行下载离线安装包。 1. 创建存放离线包文件夹 …

测试行业,你的未来路在何方?失业之外,暗藏的这个危机更可怕!

目前测试行业现状 近期飞书的大规模裁员&#xff0c;无疑为2024年伊始蒙上了一层阴影。再加上“共享员工”模式的兴起&#xff0c;对于身处互联网行业的从业者来说&#xff0c;无疑是雪上加霜。 此外&#xff0c;延续了2023年的情况&#xff0c;在求职平台如BOSS直聘上&#…

Harbor本地仓库搭建004_Harbor配置管理功能_分布式分发功能_仓库管理_用户管理_垃圾清理_审查服务_项目定额---分布式云原生部署架构搭建00

然后我们再看一下配置管理,这里主要有个认证模式 这里我们是数据库,其实就是我们安装的postgresql 可以看到还有LDAP对吧,这个其实就是自己公司如果有 LDAP服务器,那么可以对接过来,那么,这个时候 再登录harbor的时候,就可以直接使用公司的,LDAP来管理,所有的用户了,其实就是…

Pycharm 启动 Django项目 —— python篇

1、打开你的工程&#xff0c;在菜单栏里找到Run-->Edit Configurations 2、在打开的对话框里边选择Python&#xff0c;点击号 3.选择Python 4.出现了一个新的项Unnamed&#xff0c;你可以把它改名叫debug&#xff0c;好听一点 5.脚本选择你网站的manage.py&#xff0c;脚本参…

学习C++,这几个练手项目值得推荐

写项目是学习C的一种有效方式&#xff0c;有以下几个原因&#xff1a; 实践应用&#xff1a;通过写项目&#xff0c;你可以将所学的理论知识应用到实际中&#xff0c;加深对C语言和编程概念的理解。这有助于巩固你的知识&#xff0c;并提高你的编码能力。锻炼技能&#xff1a;…

LabVIEW与PLC通讯方式及比较

LabVIEW与PLC之间的通讯方式多样&#xff0c;包括使用MODBUS协议、OPC&#xff08;OLE for Process Control&#xff09;、Ethernet/IP以及串口通讯等。这些通讯方式各有特点&#xff0c;选择合适的通讯方式可以提高系统的效率和稳定性。以下将详细介绍每种通讯方式的特点、优点…

网络协议TCP/IP, HTTP/HTTPS介绍

TCP/IP协议 TCP/IP是一种基于连接的通信协议&#xff0c;它是互联网的基础协议。TCP代表传输控制协议&#xff0c;IP代表Internet协议。虽然这两个协议通常一起提及&#xff0c;但它们实际上是分开的&#xff1a;IP负责在网络中从一台计算机向另一台计算机发送数据包&#xff0…

STM32定时器入门篇——(基本定时器的使用)

一、基本定时器的功能介绍&#xff1a; STM32F103的基本定时器有&#xff1a;TIM6、TIM7。基本定时器TIM6和TIM7各包含一个16位递增自动装载计数器&#xff0c;最大计数到2^16也就是65536&#xff0c;计数值为0~65535&#xff0c;其拥有的功能有&#xff1a;定时中断、主模式触…

星戈瑞CY5-DBCO在纳米粒子载体标记与追踪中的应用

随着纳米技术的飞速发展&#xff0c;纳米粒子载体在生物医学领域的应用增多。这些载体能够递送药物、基因和其他生物活性分子到特定的细胞或组织。为了有效监测纳米粒子载体在体内的分布、行为以及与生物分子的相互作用&#xff0c;荧光标记技术成为了一个常见工具。其中&#…

HttpServletRequest・getContentLeng・getContentType区别

getContentLength()&#xff1a; 获取客户端发送到服务器的HTTP请求主体内容的字节数&#xff08;长度&#xff09; 如果请求没有正文内容&#xff08;如GET&#xff09;&#xff0c;或者请求头中没有包含Content-Length字段&#xff0c;则该方法返回 -1 getContentType()&am…

算法与数据结构面试宝典——迭代与递归详解与示例(C#,C++)

文章目录 一、迭代与递归简介迭代递归 二、迭代与递归的应用场景迭代递归 三、迭代与递归的优缺点迭代优缺点递归优缺点 四、迭代与递归的示例及面试策略示例1&#xff1a;斐波那契数列&#xff08;迭代实现&#xff09;示例2&#xff1a;快速排序&#xff08;递归实现&#xf…

vue3+ts:监听dom宽高变化函数

一、效果展示 二、代码 getSize.ts import { ref, Ref, watchEffect } from "vue";export const getWidth (domRef: Ref<HTMLElement | null>) > {const width ref<number>(0);const height ref<number>(0);const observer new ResizeObs…