jq+canvas:实现图片上传+裁剪+保存等功能

效果图

上传图片之前:
在这里插入图片描述
上传图片之后,点击放大/缩小后的效果:
在这里插入图片描述
裁剪之后的效果:
在这里插入图片描述

代码实现如下:

1.html部分

<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 />裁剪框大小:<inputtype="number"id="cropWidth"placeholder="宽度"value="200"onchange="updateCropBoxSize()"/>x<inputtype="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.script部分代码

<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;};// 读取文件为数据URLreader.readAsDataURL(file);});// 点击缩放增加按钮时执行document.getElementById('zoomIn').addEventListener('click', function () {// 如果缩放比例小于2,则增加0.1if (scale < 2) {scale = parseFloat((scale + 0.05).toFixed(2));// 重新绘制redraw();// 调整裁剪框位置adjustCropBoxPosition();}});// 点击缩放减少按钮时执行document.getElementById('zoomOut').addEventListener('click', function () {// 如果缩放比例大于0.2,则减少0.1if (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转为数据URLconst 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.css样式代码

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

完成!!!

文件上传的关键代码如下:

// 当文件输入框选择文件时执行
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;};// 读取文件为数据URLreader.readAsDataURL(file);});

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python提取PDF表格(基于AUTOSAR_SWS_CANDriver.pdf)

个人学习笔记&#xff0c;仅供参考。 需求&#xff1a;提取AUTOSAR SWS中所有的API接口信息&#xff0c;用于生成C代码。 此处以AUTOSAR_SWS_CANDriver.pdf为例&#xff0c;若需要提取多个SWS文件&#xff0c;遍历各个文件即可。 1.Python包 pdfplumber是一款完全用python开…

【FMC139】青翼科技基于VITA57.1标准的4路500MSPS/1GSPS/1.25GSPS采样率14位AD采集FMC子卡模块

板卡概述 FMC139是一款基于VITA57.1标准规范的JESD204B接口FMC子卡模块&#xff0c;该模块可以实现4路14-bit、500MSPS/1GSPS ADC采集功能。该板卡ADC器件采用ADI公司的AD9680芯片,全功率-3dB模拟输入带宽可达2GHz。该ADC与FPGA的主机接口通过8通道的高速串行GTX收发器进行互联…

SpringCloud-高级篇(五)

一&#xff1a;分布式事务理论基础 原子性&#xff08;Atomicity&#xff09; 原子性是指事务是一个不可分割的工作单位&#xff0c;事务中的操作要么都发生&#xff0c;要么都不发生。 一致性&#xff08;Consistency&#xff09; 事务前后数据的完整性必须保持一致。 隔离性&…

计算机服务器中了mallox勒索病毒如何处理,mallox勒索病毒解密文件恢复

科技技术的发展推动了企业的生产运营&#xff0c;网络技术的不断应用&#xff0c;极大地方便了企业日常生产生活&#xff0c;但网络毕竟是一把双刃剑&#xff0c;网络安全威胁一直存在&#xff0c;近期&#xff0c;云天数据恢复中心接到很多企业的求助&#xff0c;企业的计算机…

【离散数学】——期末刷题题库(命题逻辑)

&#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java基础&#xff1a;Java基础_IT闫的博客-CSDN博客 &#x1f40b;c语言&#xff1a;c语言_IT闫的博客-CSDN博客 &#x1f41f;MySQL&#xff1a…

使用opencv的matchTemplate进行银行卡卡号识别

![字体文件](https://img-blog.csdnimg.cn/3a16c87cf4d34aceb0778c4b20ddadb2.png#pic_center import cv2 import numpy as npdef show_img(img, name"temp"):img cv2.resize(img, (0, 0), fx3, fy3)cv2.imshow(name, img)cv2.waitKey(0)cv2.destroyAllWindows()de…

神奇的世界(高斯核是唯一可以产生多尺度空间的线性核研究总结,两个高斯公式的联系,和推导)

放大缩小其实在现实世界中不存在。 也就是说尺度是不存在的。 比如树的长大&#xff0c;人的长大&#xff0c;从来就不是放大能解释的。 但你发现&#xff0c;这种事情存在于人的眼睛当中&#xff0c;光线真是神奇的东西。 但现实的东西是不存在放大缩小的&#xff0c;只有…

DjiTello + YoloV5的无人机的抽烟检测

一、效果展示 注&#xff1a;此项目纯作者自己原创&#xff0c;创作不易&#xff0c;不经同意不给予搬运权限&#xff0c;转发前请联系我&#xff0c;源码较大需要者评论获取&#xff0c;谢谢配合&#xff01; 1、未启动飞行模型无人机的目标检测。 DjiTello YOLOV5抽烟检测 …

【深入剖析K8s】容器技术基础(三):深入理解容器镜像 文件角度

容器里的进程‘看到’’的文件系统 可能你立刻就能想到,这应该是一个关于MountNamespace的问题:容器里的应用进程理应‘看到”一套完全独立的文件系统这样它就可以在自己的容器目录&#xff08;比如 /tmp&#xff09;下进行操作’而完全不会受宿主机以及其他容器的影响。 容器…

94.STM32外部中断

目录 1.什么是 NVIC&#xff1f; 2.NVIC寄存器 3.中断优先级 4.NVIC的配置 设置中断分组​编辑 配置某一个中断的优先级 5.什么是EXTI 6.EXTI和NVIC之间的关系 7.SYSCFG 的介绍 1.什么是 NVIC&#xff1f; NVIC是一种中断控制器&#xff0c;主要用于处理 ARM Cort…

美国高防云服务器的优劣势分析(相比普通云服务器)

在当前数字化时代&#xff0c;云服务器已经成为企业和个人进行在线业务的重要基础设施。而在选择云服务器时&#xff0c;很多人会面临一个问题&#xff1a;是选择普通云服务器还是高防云服务器?本文将从多个方面来分析美国高防云服务器相比普通云服务器的优势和劣势。 我们来看…

【APUE】进程间通信

目录 一、管道 1.1 匿名管道 1.2 命名管道 二、XSI IPC 2.1 概述 2.2 消息队列 2.2.1 msgget 2.2.2 msgsnd 2.2.3 msgrcv 2.2.4 msgctl 2.2.5 代码示例 2.3 信号量数组 2.3.1 semget 2.3.2 semop 2.3.3 semctl 2.3.4 代码示例 2.3 共享内存 2.3.1 shmget…