效果图
上传图片之前:
上传图片之后,点击放大/缩小后的效果:
裁剪之后的效果:
代码实现如下:
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);});