jq+canvas: realize image uploading + cropping + saving and other functions

renderings

Before uploading the image:
Insert image description here
After uploading the image, click to enlarge/reduce the effect:
Insert image description here
After cropping:
Insert image description here

The code is implemented as follows:

1.htmlpart

<input type="file" id="fileInput" accept="image/png, image/gif, image/jpeg"/>
<br />
<div id="controls" style="display: none">
 <button id="zoomIn">放大</button>
 <button id="zoomOut">缩小</button>
 <br />
 <button id="crop">裁剪</button>
 <button id="save">保存</button>
 <br />
 裁剪框大小:
 <input
   type="number"
   id="cropWidth"
   placeholder="宽度"
   value="200"
   onchange="updateCropBoxSize()"
 />
 x
 <input
   type="number"
   id="cropHeight"
   placeholder="高度"
   value="200"
   onchange="updateCropBoxSize()"
 />
</div>
<div id="canvasContainer">
 <canvas id="canvas"></canvas>
 <div id="cropBox"></div>
</div>
<div id="cropBoxInfo"></div>

2.scriptpartial charge

<script src="./script/jquery-1.8.3.js" type="text/javascript"></script>
<script>
  // 创建一个img对象
  let img = new Image();
  // 获取canvas元素
  let canvas = document.getElementById('canvas');
  // 获取2d绘图上下文
  let ctx = canvas.getContext('2d');
  // 缩放比例
  let scale = 1;
  // 获取裁剪框元素
  let cropBox = document.getElementById('cropBox');
  // 裁剪框的偏移量
  let cropOffsetX, cropOffsetY;

  // 当文件输入框选择文件时执行
  document
    .getElementById('fileInput')
    .addEventListener('change', function (event) {
    
    
      // 获取选择的文件
      const file = event.target.files[0];
      const fileInput = document.getElementById('fileInput');
      const filePath = fileInput.value;
      const allowedExtensions = /(\.png|\.gif|\.jpe?g)$/i; // 正则表达式匹配指定扩展名
      if (!allowedExtensions.exec(filePath)) {
    
    
        alert('请选择 PNG、GIF 或 JPE 格式的图片文件!');
        fileInput.value = ''; // 清空文件选择器中的值,防止非法文件的上传
        return false;
      }
      // 验证文件类型为图像类型
      if (file.type && !file.type.startsWith('image/')) {
    
    
        alert('请选择图片文件!');
        return false;
      }

      // 创建一个文件读取器
      const reader = new FileReader();

      // 当读取完成后执行
      reader.onload = function (event) {
    
    
        // 当图片加载完成后执行
        img.onload = function () {
    
    
          // 显示控制器
          document.getElementById('controls').style.display = 'block';
          // 重新绘制
          redraw();
          // 显示裁剪框
          cropBox.style.display = 'block';
          // 调整裁剪框大小
          updateCropBoxSize();
          // 中心裁剪框
          centerCropBox();
        };

        // 设置图片源
        img.src = event.target.result;
      };

      // 读取文件为数据URL
      reader.readAsDataURL(file);
    });

  // 点击缩放增加按钮时执行
  document.getElementById('zoomIn').addEventListener('click', function () {
    
    
    // 如果缩放比例小于2,则增加0.1
    if (scale < 2) {
    
    
      scale = parseFloat((scale + 0.05).toFixed(2));
      // 重新绘制
      redraw();
      // 调整裁剪框位置
      adjustCropBoxPosition();
    }
  });

  // 点击缩放减少按钮时执行
  document.getElementById('zoomOut').addEventListener('click', function () {
    
    
    // 如果缩放比例大于0.2,则减少0.1
    if (scale > 0.1) {
    
    
      scale = parseFloat((scale - 0.05).toFixed(2));
      // 重新绘制
      redraw();
      // 调整裁剪框位置
      adjustCropBoxPosition();
    }
  });

  // 点击裁剪按钮时执行
  document.getElementById('crop').addEventListener('click', function () {
    
    
    // 获取裁剪框的宽度和高度
    const cropWidth = parseInt(document.getElementById('cropWidth').value);
    const cropHeight = parseInt(
      document.getElementById('cropHeight').value
    );

    // 计算裁剪框在图片上的位置和尺寸,根据缩放比例调整
    const cropX = parseInt(cropBox.style.left);
    const cropY = parseInt(cropBox.style.top);

    // 绘制图片(img是已加载的图片)
    ctx.drawImage(img, 0, 0);

    // 创建一个临时画布
    const tempCanvas = document.createElement('canvas');
    const tempCtx = tempCanvas.getContext('2d');

    // 在临时画布上进行裁剪
    tempCanvas.width = cropWidth;
    tempCanvas.height = cropHeight;
    tempCtx.drawImage(
      img,
      cropX * (1 / scale),
      cropY * (1 / scale),
      cropWidth * (1 / scale),
      cropHeight * (1 / scale),
      0,
      0,
      cropWidth,
      cropHeight
    );

    // 清空主画布,设置为缩小后的尺寸
    canvas.width = cropWidth;
    canvas.height = cropHeight;

    // 缩放并绘制到主画布
    ctx.drawImage(
      tempCanvas,
      0,
      0,
      cropWidth,
      cropHeight,
      0,
      0,
      cropWidth,
      cropHeight
    );

    // 中心裁剪框
    centerCropBox();

    // 获取裁剪后的图像数据(Base64 格式)
    const croppedImageData = canvas.toDataURL('image/png'); // 可根据需要修改格式

    // 将 Base64 数据转换为 Blob 对象
    const base64Data = croppedImageData.split(',')[1]; // 去除前缀信息
    const byteArray = atob(base64Data);
    const byteNumbers = new Array(byteArray.length);
    for (let i = 0; i < byteArray.length; i++) {
    
    
      byteNumbers[i] = byteArray.charCodeAt(i);
    }
    const file = new Blob([new Uint8Array(byteNumbers)], {
    
    
      type: 'image/png',
    }); // 根据图像类型修改 type

    // // 创建一个 FormData 对象并添加文件
    // const formData = new FormData();
    // formData.append('image', file, 'cropped_image.png'); // 这里的 'image' 是表单字段的名称,'cropped_image.png' 是文件名
    //
    // // 创建一个 XHR 对象并发送 FormData
    // const xhr = new XMLHttpRequest();
    // xhr.open('POST', 'your_upload_url', true);
    // xhr.onload = function() {
    
    
    //     // 处理上传完成后的逻辑
    // };
    // xhr.send(formData);
  });

  // 点击保存按钮时执行
  document.getElementById('save').addEventListener('click', function () {
    
    
    // 将canvas转为数据URL
    const croppedImage = canvas.toDataURL('image/png', 1.0);

    // 创建下载链接
    const downloadLink = document.createElement('a');
    downloadLink.setAttribute('download', 'cropped_image.png');
    downloadLink.setAttribute('href', croppedImage);
    downloadLink.click();
  });

  // 重新绘制函数
  function redraw() {
    
    
    // 设置canvas的宽度和高度为图片的缩放尺寸
    canvas.width = img.width * scale;
    canvas.height = img.height * scale;
    ctx.imageSmoothingEnabled = true;

    // 对图像进行了缩放和处理:图像质量
    createImageBitmap(img, {
    
    
      resizeWidth: img.width,
      resizeHeight: img.height,
      resizeQuality: 'high',
    }).then(function (bitmap) {
    
    
      // 在canvas上绘制图片
      ctx.drawImage(
        bitmap,
        0,
        0,
        img.width,
        img.height,
        0,
        0,
        canvas.width,
        canvas.height
      );
    });

    // 在canvas上绘制图片
    // ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);

    // 根据缩放比例调整裁剪框位置和大小
    // cropBox.style.width = `${parseInt(cropBox.style.width) * scale}px`;
    // cropBox.style.height = `${parseInt(cropBox.style.height) * scale}px`;
    cropBox.style.left = `${
      
      parseInt(cropBox.style.left) * scale}px`;
    cropBox.style.top = `${
      
      parseInt(cropBox.style.top) * scale}px`;
    // 中心裁剪框
    centerCropBox();
  }
  // 调整裁剪框位置函数
  function adjustCropBoxPosition() {
    
    
    const left = parseInt(cropBox.style.left);
    const top = parseInt(cropBox.style.top);

    // 设置裁剪框的左、上位置
    cropBox.style.left =
      Math.min(
        Math.max(left, 0),
        canvas.width - parseInt(cropBox.style.width)
      ) + 'px';
    cropBox.style.top =
      Math.min(
        Math.max(top, 0),
        canvas.height - parseInt(cropBox.style.height)
      ) + 'px';
    setCropBoxInfo();
  }

  // 更新裁剪框大小函数
  function updateCropBoxSize() {
    
    
    const cropWidth = parseInt(document.getElementById('cropWidth').value);
    const cropHeight = parseInt(
      document.getElementById('cropHeight').value
    );

    // 设置裁剪框的宽度和高度
    cropBox.style.width = cropWidth + 'px';
    cropBox.style.height = cropHeight + 'px';
  }

  // 中心裁剪框函数
  function centerCropBox() {
    
    
    // 设置裁剪框的左、上位置为居中
    cropBox.style.left =
      (canvas.width - parseInt(cropBox.style.width)) / 2 + 'px';
    cropBox.style.top =
      (canvas.height - parseInt(cropBox.style.height)) / 2 + 'px';
  }

  // 是否正在拖拽裁剪框的标志
  let isDragging = false;

  // 当裁剪框被拖动时执行
  cropBox.addEventListener('mousedown', function (e) {
    
    
    isDragging = true;
    // 记录鼠标点击位置与裁剪框左上角距离
    cropOffsetX = e.clientX - parseInt(cropBox.style.left);
    cropOffsetY = e.clientY - parseInt(cropBox.style.top);
  });

  // 当鼠标移动时执行
  document.addEventListener('mousemove', function (e) {
    
    
    if (isDragging) {
    
    
      // 计算裁剪框的新位置
      const left = e.clientX - cropOffsetX;
      const top = e.clientY - cropOffsetY;

      // 设置裁剪框的左、上位置
      cropBox.style.left = left + 'px';
      cropBox.style.top = top + 'px';

      // 调整裁剪框位置
      adjustCropBoxPosition();
    }
  });

  // 当鼠标释放时执行
  document.addEventListener('mouseup', function () {
    
    
    isDragging = false;
  });

  function setCropBoxInfo() {
    
    
    $('#cropBoxInfo').html(
      cropBox.style.left +
        ',' +
        cropBox.style.top +
        ',' +
        cropBox.style.width +
        ',' +
        cropBox.style.height +
        '<br>' +
        scale +
        ',' +
        canvas.width +
        ',' +
        canvas.height
    );
  }
</script>

3.cssKanshikidaiko

<style>
  #canvasContainer {
    
    
    position: relative;
  }
  #cropBox {
    
    
    position: absolute;
    border: 1px dashed red;
    pointer-events: all;
    box-sizing: border-box;
  }
</style>

Finish! ! !

The key code for file upload is as follows:

// 当文件输入框选择文件时执行
document
  .getElementById('fileInput')
  .addEventListener('change', function (event) {
    
    
    // 获取选择的文件
    const file = event.target.files[0];
    const fileInput = document.getElementById('fileInput');
    const filePath = fileInput.value;
    const allowedExtensions = /(\.png|\.gif|\.jpe?g)$/i; // 正则表达式匹配指定扩展名
    if (!allowedExtensions.exec(filePath)) {
    
    
      alert('请选择 PNG、GIF 或 JPE 格式的图片文件!');
      fileInput.value = ''; // 清空文件选择器中的值,防止非法文件的上传
      return false;
    }
    // 验证文件类型为图像类型
    if (file.type && !file.type.startsWith('image/')) {
    
    
      alert('请选择图片文件!');
      return false;
    }
    // 创建一个文件读取器
    const reader = new FileReader();
    // 当读取完成后执行
    reader.onload = function (event) {
    
    
      // 当图片加载完成后执行
      img.onload = function () {
    
    
        // 显示控制器
        document.getElementById('controls').style.display = 'block';
        // 重新绘制
        redraw();
        // 显示裁剪框
        cropBox.style.display = 'block';
        // 调整裁剪框大小
        updateCropBoxSize();
        // 中心裁剪框
        centerCropBox();
      };
      // 设置图片源
      img.src = event.target.result;
    };
    // 读取文件为数据URL
    reader.readAsDataURL(file);
  });

Guess you like

Origin blog.csdn.net/yehaocheng520/article/details/134663696
Recommended