<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>cropper</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/layui/2.6.8/layui.js" integrity="sha512-lH7rGfsFWwehkeyJYllBq73IsiR7RH2+wuOVjr06q8NKwHp5xVnkdSvUm8RNt31QCROqtPrjAAd1VuNH0ISxqQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/layui/2.6.8/css/layui.min.css" integrity="sha512-iQBJbsNHXUcgEIgWThd2dr8tOdKPvICwqjPEZYY81z3eMya44A5MiAqfWSCh+Ee1YzNYkdrI982Qhwgr8LEYOQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
  <link href="https://cdn.bootcdn.net/ajax/libs/cropperjs/1.5.9/cropper.css" rel="stylesheet">
  <script src="https://cdn.bootcdn.net/ajax/libs/cropperjs/1.5.9/cropper.js"></script>
  <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
  <div class="wrapper">
    <div class="box">
      <img class="avatar" src="https://img1.baidu.com/it/u=52681052,678098948&fm=253&fmt=auto&app=120&f=JPEG?w=1000&h=400" >
      <input type="file" class="upload" accept="image/*">
    </div>
    <div class="popup">
      <div class="image-wrapper">
        <img src="" class="image">
      </div>
    </div>
  </div>
</body>
</html>
*{
      margin: 0;
      padding: 0;
    }
    .wrapper{
      position: relative;
    }
    .box{
      width: 300px;
      height: 300px;
      margin: 0 auto;
    }
    .avatar{
      width: 100%;
      display: block;
    }
    .upload{
      display: none;
    }
    .popup{
      display: none;
      padding: 15px;
    }
    .popup .image-wrapper{
      width: 400px;
    }
    .image-wrapper .image {
      width: 100%;
    }
$(function() {
  class Upload {
    constructor() {
      this.$box = $('.box')
      this.$input = $('.box .upload')
      this.$image = $('.popup .image')
      this.$popup = ''
      this.cropper = null
      this.imgUrl = ''
      this.init()
    }
    // 初始化
    init() {
      this.bindFileInputClick()
      this.bindImageClick()
      this.bindUploadChange()
    }
    // 创建弹窗
    openLayer() {
      var that = this
      layui.use('layer', function() {
        var layer = layui.layer
        that.$popup = layer.open({
          offset: '100px',
          title: '裁剪图片',
          type: 1,
          btn: ['确定'],
          content: $('.popup'),
          // 弹窗打开的回调
          success: function() {
            that.cropper = new Cropper(that.$image[0], {
              aspectRatio: 1,
              viewMode: 3
            })
            console.log('new Cropper')
          },
          // 点击确定按钮的回调
          yes: function(index) {
            console.log('保存')
            that.saveCropped()
            that.cropper.destroy()
            that.cropper = null
            layer.close(index, () => { $('.popup').hide() })
            console.log('cropper destroy')
          },
          // 点击关闭按钮的回调
          cancel: function(index) {
            that.cropper.destroy()
            that.cropper = null
            layer.close(index, () => { $('.popup').hide() })
            console.log('cropper destroy')
          }
        })
      })
    }
    bindImageClick() {
      var that = this
      this.$box.on('click', function(e) {
        e.preventDefault()
        e.stopPropagation()
        that.$input.click()
      })
    }
    bindFileInputClick() {
      this.$input.on('click', function(e) {
        e.stopPropagation()
      })
    }
    initPopup(url) {
      this.$input.val('')
      this.$image.attr('src', url)
      this.openLayer()
    }
    bindUploadChange() {
      var that = this
      this.$input.on('change', function(e) {
        var files = e.target.files
        var reader
        var file
        var url
        if(files && files.length > 0) {
          file = files[0]
        }
        that.initPopup(URL.createObjectURL(file))
      })
    }
    saveCropped() {
      var that = this
      var saveAvatar = this.$box.find('.avatar').attr('src')
      if(this.cropper) {
        this.imgUrl = ''
        var canvas = this.cropper.getCroppedCanvas({
          width: 300,
          height: 300,
        })
        canvas.toBlob(function(blob) {
          var formData = new FormData()
          formData.append('avatar', blob, 'avatar.jpg')
          $.ajax({
            url: 'https://jsonplaceholder.typicode.com/posts', // mock api
            method: 'post',
            processData: false,
            contentType: false,
            success: function(res) {
              console.log('upload success')
              that.imgUrl = res.url // mock
              console.log(res)
              that.$box.find('.avatar').attr('src', canvas.toDataURL())
            },
            error: function() {
              that.$box.find('.avatar').attr('src', saveAvatar)
              console.log('upload error')
            }
          })
        })
      }
    }
  }

  class Page {
    constructor() {
      this.$upload = new Upload()
      this.consoleUrl()
    }
    consoleUrl() {
      var that = this
      $(document).on('click', function () {
        console.log(that.$upload.imgUrl)
      })
    }
  }

  new Page()
})
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.