前端业务开发中使用原生js和elementui两种方式实现头像裁切上传的功能

news2024/10/5 21:14:14

日常业务开发中,无论是后台管理系统还是前台界面,都会遇到图片裁剪的业务需求,选择合适的尺寸或者图片的关键部分,满足我们的功能需求!!

效果预览

效果一:

请添加图片描述效果二:请添加图片描述

实现过程

1.原生js实现方式

引入cropperjs的库文件和样式

<script src="https://cdn.jsdelivr.net/npm/cropperjs/dist/cropper.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/cropperjs/dist/cropper.css" rel="external nofollow" >
*{
  padding: 0;
  margin: 0;
  box-sizing: border-box;
  font-family: "Poppins",sans-serif;
}
body{
  background-color: #025bee;
}
.wrapper{
  width: min(90%,800px);
  position: absolute;
  transform: translateX(-50%);
  left:50%;
  top:1em;
  background-color: #fff;
  padding: 2em 3em;
  border-radius: .5em;
}
.container{
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap:1em;
}
.container .image-container,.container .preview-container{
   width: 100%;
   /* background-color: aquamarine; */
}

input[type="file"]{
  display: none;
}

label{
  display: block;
  position: relative;
  background-color: #025bee;
  font-size: 16px;
  text-align: center;
  width: 250px;
  color:#fff;
  padding: 16px 0;
  margin: 16px auto;
  cursor: pointer;
  border-radius: 5px;
}

img{
  display: block;
  /**is key to cropper.js**/
  max-width: 100%;
}

.image-container{
  width: 60%;
  margin: 0 auto;
}

.options{
  display: flex;
  justify-content: center;
  gap:1em;
}
input[type="number"]{
  width: 100px;
  padding: 16px 5px;
  border-radius: .3em;
  border: 2px solid #000;
}

button{
  padding: 1em;
  border-radius: .3em;
  border: 2px solid #025bee;
  background-color: #fff;
  color: #025bee;
}

.btns{
  display: flex;
  justify-content: center;
  gap: 1em;
  margin-top: 1em;

}
.btns button{
  font-size: 1em;
}
.btns a {
  border: 2px solid #025bee;
  background-color: #025bee;
  color: #fff;
  text-decoration: none;
  padding: 1em;
  font-size: 1em;
  border-radius: .3em;
}
.hide{
  display: none;
}
<div class="wrapper">
      <div class="container">
        <div class="image-container">
          <img src="" alt="" id="image">
        </div>
        <div class="preview-container">
           <img src="" alt="" id="preview-image">
        </div>
      </div>
      <input type="file" name="" id="file" accept="image/*">
      <label for="file">选择一张图片</label>
      <div class="options hide">
        <input type="number" name="" id="height-input" placeholder="输入高度" max="780" min="0">
        <input type="number" name="" id="width-input" placeholder="输入宽度" max="780" min="0">
        <button class="aspect-ration-button">16:9</button>
        <button class="aspect-ration-button">4:3</button>
        <button class="aspect-ration-button">1:1</button>
        <button class="aspect-ration-button">2:3</button>
        <button class="aspect-ration-button">自由高度</button>
      </div>
      <div class="btns">
         <button id="preview" class="hide">
          预览
         </button>
         <a href="" id="download" class="hide">下载</a>
      </div>
   </div>

核心步骤:

<script>
     const oFile = document.querySelector('#file')
     const oImage = document.querySelector('#image')
     const oDownload = document.querySelector('#download')
     const oAspectRation = document.querySelectorAll('.aspect-ration-button')
     const oOptions = document.querySelector('.options')
     const oPreview = document.querySelector('#preview-image')
     const oPreviewBtn = document.querySelector('#preview')
     const heightInput = document.querySelector('#height-input')
     const widthInput = document.querySelector('#width-input')
     let cropper = '',filename = ''
     /**
      * 上传事件
      * 
      * /
      */
    oFile.onchange = function(e){
      oPreview.src=""
      heightInput.value = 0
      widthInput.value = 0
      oDownload.classList.add('hide')


      const reader = new FileReader()
      reader.readAsDataURL(e.target.files[0])
      reader.onload = function(e){
        oImage.setAttribute('src',e.target.result)
        if(cropper){
          cropper.destory()
        }
        cropper = new Cropper(oImage)
        oOptions.classList.remove('hide')
        oPreviewBtn.classList.remove('hide')
      }
      filename = oFile.files[0].name.split(".")[0]
      oAspectRation.forEach(ele => {
        ele.addEventListener('click', () => {
          if(ele.innerText === '自由高度'){
            cropper.setAspectRatio(NaN)
          }else{
            cropper.setAspectRatio(eval(ele.innerText.replace(":",'/')))
          }
        }, false)
      })

      heightInput.addEventListener('input', () => {
        const {height} = cropper.getImageData()
        if(parseInt(heightInput.value) > Math.round(height)){
          heightInput.value = Math.round(height)
        }
        let newHeight = parseInt(heightInput.value)
        cropper.setCropBoxData({
          height:newHeight
        })
      }, false)

      widthInput.addEventListener('input', () => {
        const {width} = cropper.getImageData()
        if(parseInt(widthInput.value) > Math.round(width)){
          widthInput.value = Math.round(width)
        }
        let newWidth = parseInt(widthInput.value)
        cropper.setCropBoxData({
          width:newWidth
        })
      }, false)

      oPreviewBtn.addEventListener('click', (e) => {
        e.preventDefault();
        oDownload.classList.remove('hide');
        let imgSrc = cropper.getCroppedCanvas({}).toDataURL();
        oPreview.src = imgSrc;
        oDownload.download = `cropped_${filename}.png`;
        oDownload.setAttribute('href',imgSrc)
      }, false)
    }
   </script>
  1. vue2+elementui实现

安装vue-cropper库

npm i vue-cropper

静态页面

<template>
  <g-container>
    <div>
      <span>点击头像,更换头像:</span>
      <el-avatar @click.native="handleShowCropper" :src="avatarUrl"></el-avatar>
    </div>
    <el-dialog
      title="更换头像"
      :visible.sync="dialogVisible"
      width="800px"
      :before-close="dialogBeforeClose"
      v-if="dialogVisible"
      append-to-body
    >
      <el-row :gutter="10">
        <el-col :span="12" :style="{ height: '350px' }">
          <vueCropper
            ref="cropper"
            :img="option.img"
            :info="true"
            :auto-crop="option.autoCrop"
            :outputSize="option.size"
            :outputType="option.outputType"
            @realTime="realTime"
            centerBox
          ></vueCropper>
        </el-col>
        <el-col :span="12" class="preview-container">
          <div class="avatar-upload-preview">
            <div :style="previews.div">
              <img :src="previews.url" :style="previews.img" />
            </div>
          </div>
        </el-col>
      </el-row>
      <el-row :gutter="10" style="margin-top:20px;text-align:center" justify="center">
        <el-col :span="4">
          <el-upload
            action
            name="file"
            accept="image/*"
            :before-upload="beforeUpload"
            :show-upload-list="false"
          >
            <el-button icon="upload">选择图片</el-button>
          </el-upload>
        </el-col>
        <el-col :span="3">
          <el-button type="primary" @click="changeScale(1)">放大</el-button>
        </el-col>
        <el-col :span="3">
          <el-button type="primary" @click="changeScale(-1)">缩小</el-button>
        </el-col>
        <el-col :span="3">
          <el-button type="primary" @click="rotateLeft">左旋转</el-button>
        </el-col>
        <el-col :span="3">
          <el-button type="primary" @click="rotateRight">右旋转</el-button>
        </el-col>
        <el-col :span="3">
          <el-button type="primary" @click="saveHandle('blob')">保存</el-button>
        </el-col>
      </el-row>
      <div slot="footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="dialogVisible = false">确 定</el-button>
      </div>
    </el-dialog>
  </g-container>
</template>

在这里插入图片描述
在这里插入图片描述
关键的js代码实现

export default {
  components: {
    VueCropper,
  },
  data() {
    return {
      option: { img: "", size: 1, outputType: "", autoCrop: true },
      dialogVisible: false,
      previews: {},
      avatarUrl:
        "https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png",
    };
  },
  methods: {
    handleShowCropper() {
      this.dialogVisible = true;
    },
    dialogBeforeClose() {
      this.dialogVisible = false;
    },
    changeScale(num) {
      this.$refs.cropper.changeScale(num);
    },
    // 左旋转
    rotateLeft() {
      this.$refs.cropper.rotateLeft();
    },
    // 右旋转
    rotateRight() {
      this.$refs.cropper.rotateRight();
    },
    beforeUpload(file) {
      console.log("🚀 ~ beforeUpload ~ file:", file);
      const reader = new FileReader();
      //转化为base64
      reader.readAsDataURL(file);
      reader.onload = () => {
        console.log(reader, "reader");
        // this.previews.url = reader.result;
        this.option.img = reader.result;
      };
    },
    // 裁剪之后的数据
    realTime(data) {
      console.log("🚀 ~ realTime ~ data:", data);
      this.previews = data;
    },
    // 上传图片(点击保存按钮)
    saveHandle(type) {
      this.$refs.cropper.getCropData((data) => {
        this.dialogVisible = false;
        console.log(data);
        // data为base64图片,供接口使用
        this.avatarUrl = data;
        this.$emit("save", data);
      });
    },
    beforeDestory() {
      this.previews = null;
      this.option = null;
    },
  },
};
</script>

组件的option配置项,大家可以去逐个测试下,体验下效果!!
在这里插入图片描述
这样,我们就实现了两种不同的图像裁剪上传!!

附上参考资料:Cropperjs官网

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

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

相关文章

全栈开发之路——前端篇(2)文件、组件与setup引入

全栈开发一条龙——前端篇 第一篇&#xff1a;框架确定、ide设置与项目创建 本文系该系列第二篇&#xff0c;主要将介绍各个文件的意义、组件结构与导入以及setup的引入。 目录 一、src外文件介绍.gitignore为git忽略文件env.d.ts用于识别其他文件index.htmljson文件vite.confi…

七、Google Protobuf

这里写自定义目录标题 一、编码和解码二、Netty本身的编码解码机制和存在的问三、Protobuf四、Protobuf示例1五、ProtoBuffer传输多种数据类型 一、编码和解码 二、Netty本身的编码解码机制和存在的问 netty提供的编码器 netty提供的解码器 存在的问题 无法跨语言序列化后…

【C语言】动态内存分配(一)

目录 1.为什么要有动态内存分配 2.malloc和free 2.1malloc 2.2free 1.为什么要有动态内存分配 我们已经掌握的内存开辟方式有: 但是上述的开辟空间的方式有两个特点: ⭐空间开辟大小是固定的。 ⭐数组在申明的时候&#xff0c;必须指定数组的长度&#xff0c;数组空间一旦…

大模型实体化:个人智能体为AI PC注入灵魂

从Alpha GO到Chat GPT&#xff0c;从DALL-E到Sora&#xff0c;AI时代的演进正行驶在快车道&#xff0c;人工智能正受到有史以来最高的关注。人们在生怕被快速变革的AI时代抛弃的同时&#xff0c;也不禁疑惑&#xff0c;AI浪潮的下一步在哪里&#xff1f; 大模型实体化&#xff…

java-函数式编程-函数对象

定义 什么是合格的函数&#xff1f;无论多少次执行函数&#xff0c;只要输入一样&#xff0c;输出就不会改变 对象方法的简写 其实在类中&#xff0c;我们很多参数中都有一个this&#xff0c;被隐藏传入了 函数也可以作为对象传递&#xff0c;lambda就是很好的例子 函数式接口中…

jenkins汉化不完全问题解决

jenkins安装完Localization:Chinese(Simplified)中文语言包后&#xff0c;发现是出现汉化不完全或者部分汉化的情况&#xff0c;如下图&#xff1a; 解决方法&#xff1a; 启动命令中指定语言 -Duser.languageen_US.UTF-8 或者 -Duser.languageC.UTF-8原因分析&#xff1a;安…

第十五届蓝桥杯Java软件开发大学B组自我经验小结

自我介绍 23届大一 双非 计院科班 软件工程 江苏人在吉林上大学 Java蒟蒻 在学校的宣传下 有幸参加了第十五届蓝桥杯Java大学b组省赛 蓝桥杯说明 就是一个算法比赛吧 考试时间9.00到1.00 四小时 带准考证和身份证和笔 草稿纸会发 赛制是IOC就是不会给任何反馈 就是你…

基于粒子滤波器的电池剩余使用寿命计算matlab仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 粒子滤波器基础 4.2 电池剩余使用寿命建模与预测 4.3 粒子滤波器在电池寿命预测中的应用 5.完整工程文件 1.课题概述 基于粒子滤波器的电池剩余使用寿命计算。根据已知的数据&#xff0c;预测未来…

Python绘制的好看统计图

代码 sx [Accent, Accent_r, Blues, Blues_r, BrBG, BrBG_r, BuGn, BuGn_r, BuPu, BuPu_r, CMRmap, CMRmap_r, Dark2, Dark2_r, GnBu, GnBu_r, Greens, Greens_r, Greys, Greys_r, OrRd, OrRd_r, Oranges, Oranges_r, PRGn, PRGn_r, Paired, Paired_r, Pastel1, Pastel1_r, P…

数据结构(C):玩转顺序表

&#x1f37a;0.前言 言C之言&#xff0c;聊C之识&#xff0c;以C会友&#xff0c;共向远方。各位博友的各位你们好啊&#xff0c;这里是持续分享数据结构知识的小赵同学&#xff0c;今天要分享的数据结构知识是顺序表&#xff0c;在这一章&#xff0c;小赵将会向大家展开聊聊顺…

PotatoPie 4.0 实验教程(31) —— FPGA实现摄像头图像高斯滤波

什么是高斯滤波 高斯滤波是一种常见的图像处理技术&#xff0c;用于去除图像中的噪声和平滑图像。它的原理基于统计学中的高斯分布&#xff08;也称为正态分布&#xff09;。 在高斯滤波中&#xff0c;一个二维的高斯核函数被用来对图像中的每个像素进行加权平均。这个高斯核…

基于Java+SpringBoot+Mybaties-plus+Vue+elememt+hadoop + redis 医院就诊系统 设计与实现

一.项目介绍 前端&#xff1a;患者注册 、登录、查看首页、医生排班、药品信息、预约挂号、就诊记录、电子病历、处方开药、我的收藏 后端分为&#xff1a; 医生登录&#xff1a;查看当前排班信息、查看患者的挂号情况、设置患者就诊记录、电子病历、给患者开药和个人信息维护 …

LeetCode 11—— 盛最多水的容器

阅读目录 1. 题目2. 解题思路一3. 代码实现一4. 解题思路二5. 代码实现二 1. 题目 2. 解题思路一 暴力法&#xff0c;遍历所有可能的垂线对 ( i , j ) (i, j) (i,j)&#xff0c;求取最大面积&#xff1a; a r e a m i n ( h [ i ] , h [ j ] ) ∗ ( j − i ) area min(h[i]…

when to create a ViewRootImpl

when to create a ViewRootImpl when method setView is called: when method dispatchDetachedFromWindow is called:

2024年【起重机械安全管理】考试内容及起重机械安全管理操作证考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 起重机械安全管理考试内容根据新起重机械安全管理考试大纲要求&#xff0c;安全生产模拟考试一点通将起重机械安全管理模拟考试试题进行汇编&#xff0c;组成一套起重机械安全管理全真模拟考试试题&#xff0c;学员可…

ubuntu20.04安装nodejs并创建简单的node.js应用

一. 安装nodejs Node.js 为 Linux、Windows 和 macOS 提供了不同的软件包。您可以访问 Node.js 官方网站并选择与您的操作系统对应的下载链接。 第一步&#xff1a;打开官网下载源文件 中文网址:http://nodejs.cn/download/ 第二步&#xff1a;解压压缩包 tar -xvf node-v18.…

关于Spring Aop的通知类型

一、概述 1.1 通知类型 为了符合各种流程处理&#xff0c;通知类型提供了5种&#xff0c;可以对目标方法进行全方位处理&#xff0c;如下所示&#xff1a; 通知类型说明前置通知&#xff08;Before advice&#xff09;在某连接点之前执行的通知&#xff0c;但这个通知不能阻止…

【C语言】atoi和atof函数的使用

人生应该树立目标&#xff0c;否则你的精力会白白浪费。&#x1f493;&#x1f493;&#x1f493; 目录 •&#x1f319;知识回顾 &#x1f34b;知识点一&#xff1a;atoi函数的使用和实现 • &#x1f330;1.函数介绍 • &#x1f330;2.代码演示 • &#x1f330;3.atoi函数的…

Java | Spring框架 | 快速入门实战

一、Spring框架简介&#xff1a;为何选择Spring&#xff1f; Spring框架是一个开源的Java平台&#xff0c;它最初由Rod Johnson设计&#xff0c;并且首次发布于2003年。Spring使Java开发变得更加容易&#xff0c;它提供了一种更简洁、更强大、更易于测试的方式来构建Java应用。…

Debian操作系统的常用指令介绍

Debian是一个流行的Linux操作系统&#xff0c;以其稳定性和安全性而闻名。对于Debian用户来说&#xff0c;掌握一些基本的命令行指令是非常重要的&#xff0c;因为它们可以帮助你更高效地管理系统。在这篇博客中&#xff0c;我们将介绍一些在Debian系统中常用的指令及其功能。 …