效果图
上传图片之前:
上传图片之后,点击放大/缩小后的效果:
裁剪之后的效果:
代码实现如下:
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 />
裁剪框大小:
<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.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;
};
// 读取文件为数据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.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;
};
// 读取文件为数据URL
reader.readAsDataURL(file);
});