介绍
本项目将指导你完成一个简单图像裁剪工具的创建过程。最终,你将拥有一个允许用户上传、显示和裁剪图像的交互式应用程序。
👀 预览

🎯 任务
在本项目中,你将学习:
- 如何为图像裁剪工具创建 HTML 结构
- 如何使用 CSS 对网页进行样式设计,使其具有视觉吸引力
- 如何使用 JavaScript 初始化变量和事件监听器,以处理用户交互
- 如何使用 JavaScript 中的 FileReader API 处理图像上传和显示
- 如何使用 JavaScript 中的 Canvas API 实现裁剪机制
- 如何保存裁剪后的图像并显示结果
🏆 成果
完成本项目后,你将能够:
- 理解 HTML 标签和结构
- 有效地应用 CSS 属性和选择器
- 利用 JavaScript 语法、变量和事件监听器
- 在 JavaScript 中利用 FileReader API 处理文件上传
- 使用 JavaScript 中的 Canvas API 实现图像操作
搭建 HTML 结构
要求:
- 了解 HTML 标签和结构。
功能:
- 设计一个界面,允许用户上传图像并触发裁剪过程。
将 HTML 代码嵌入到你的index.html中。
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>HTML5 Crop Image</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<input type="file" name="file" id="post_file" />
<button id="save_button">SAVE</button>
<div id="label">
<canvas id="get_image"></canvas>
<p>
<canvas id="cover_box"></canvas>
<canvas id="edit_pic"></canvas>
</p>
</div>
<p>
<span id="show_edit"></span>
<span id="show_pic"><img src="" /></span>
</p>
<script type="text/javascript" src="main.js"></script>
</body>
</html>
上述三个<canvas>标签用于处理与图像相关的内容。详细处理将在后续的 js(JavaScript)代码中提供。id 为show_edit和 id 为show_pic的元素用于预览图像和查看最终图像生成结果。
为网页添加样式
要求:
- 熟悉 CSS 属性和选择器。
功能:
- 对 HTML 元素进行样式设计,使界面用户友好且具有视觉吸引力。
将 CSS 代码嵌入到你的style.css中。
body {
background-color: #f6f6f6;
margin: 0;
padding: 20px;
text-align: center;
}
#label {
border: 1px solid #ccc;
background-color: #fff;
text-align: center;
height: 300px;
width: 300px;
margin: 20px auto;
position: relative;
}
#get_image {
position: absolute;
}
#edit_pic {
position: absolute;
display: none;
background: #000;
}
#cover_box {
position: absolute;
z-index: 9999;
display: none;
top: 0px;
left: 0px;
}
#show_edit {
margin: 0 auto;
display: inline-block;
}
#show_pic {
height: 100px;
width: 100px;
border: 2px solid #000;
overflow: hidden;
margin: 0 auto;
display: inline-block;
}
canvas {
position: absolute;
top: 0;
left: 0;
}
#save_button {
padding: 8px 16px;
background-color: #3498db;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.2s;
}
#save_button:hover {
background-color: #2980b9;
}
input[type="file"] {
margin-bottom: 20px;
}
初始化变量和事件监听器
要求:
- 对 JavaScript 语法、变量和事件监听器有基本的理解。
功能:
- 为裁剪工具初始化属性和配置。添加一个事件监听器来处理上传的图像。
在main.js中编写代码来初始化裁剪工具属性并设置事件监听器。
var postFile = {
init: function () {
var t = this;
t.regional = document.getElementById("label");
t.getImage = document.getElementById("get_image");
t.editPic = document.getElementById("edit_pic");
t.editBox = document.getElementById("cover_box");
t.px = 0; //背景图像 x
t.py = 0; //背景图像 y
t.sx = 15; //裁剪区域 x
t.sy = 15; //裁剪区域 y
t.sHeight = 150; //裁剪区域高度
t.sWidth = 150; //裁剪区域宽度
document
.getElementById("post_file")
.addEventListener("change", t.handleFiles, false);
}
};
我们所有的函数和变量都封装在postFile对象中。上面提到的init函数主要设置一些初始值。
t.px = 0;
t.py = 0;
t.sx = 15;
t.sy = 15;
t.sHeight = 100;
t.sWidth = 100;
变量t.px和t.py表示实时预览区域中背景图像的坐标;t.sx、t.sy、t.sHeight和t.sWidth分别表示图像的 x、y 坐标以及宽度、高度。
我们还通过document.getElementById获取了几个稍后要操作的元素。
document
.getElementById("post_file")
.addEventListener("change", t.handleFiles, false);
我们监听id为post_file的input表单的change事件,以处理用户上传的文件。这里,我们将其委托给handleFiles函数。所以,接下来我们将实现handleFiles函数。
处理图像上传与显示
要求:
- 具备 JavaScript 中 FileReader API 的基础知识。
功能:
- 确保用户上传图像时,它能被正确处理、读取并显示在屏幕上。
在main.js中扩展函数,用于处理和显示上传的图像。
- 实现
handleFiles函数
在这里,我们使用 HTML5 文件 API。首先,通过调用new FileReader(),我们实例化一个名为oFReader的 FileReader 对象。然后,我们调用它的readAsDataURL()方法来读取文件内容并将其转换为 base64 编码格式。
最后,当文件被完全读取并加载后,我们使用postFile.paintImage(oFREvent.target.result)来处理读取的图像。简单来说,我们将读取的图像数据重新绘制到浏览器上。
handleFiles: function () {
var fileList = this.files[0];
var oFReader = new FileReader();
oFReader.readAsDataURL(fileList);
oFReader.onload = function (oFREvent) {
postFile.paintImage(oFREvent.target.result);
};
},
- 实现
paintImage函数
这里最关键的步骤是使用 canvas 根据容器大小绘制图像。在前面的步骤中,通过文件 API 的 FileReader,我们已经获得了要上传图像的 URL(oFREvent.target.result的值)。下一步是使用 canvas 绘制这个图像。首先,我们使用getImage.getContext来获取<canvas id="get_image"></canvas>的 2d 内容,可以简单理解为图像内容。之后,我们使用new Image()创建一个<img>元素并设置其src属性值。
paintImage: function (url) {
var t = this;
var createCanvas = t.getImage.getContext("2d");
var img = new Image();
img.src = url;
img.onload = function () {
if (
img.width < t.regional.offsetWidth &&
img.height < t.regional.offsetHeight
) {
t.imgWidth = img.width;
t.imgHeight = img.height;
} else {
var pWidth = img.width / (img.height / t.regional.offsetHeight);
var pHeight = img.height / (img.width / t.regional.offsetWidth);
t.imgWidth = img.width > img.height? t.regional.offsetWidth : pWidth;
t.imgHeight =
img.height > img.width? t.regional.offsetHeight : pHeight;
}
t.px = (t.regional.offsetWidth - t.imgWidth) / 2 + "px";
t.py = (t.regional.offsetHeight - t.imgHeight) / 2 + "px";
t.getImage.height = t.imgHeight;
t.getImage.width = t.imgWidth;
t.getImage.style.left = t.px;
t.getImage.style.top = t.py;
createCanvas.drawImage(img, 0, 0, t.imgWidth, t.imgHeight);
t.imgUrl = t.getImage.toDataURL();
t.cutImage();
t.drag();
};
},
在img.onload函数中,我们的主要目标是按原始大小并保持比例重新绘制图像,这就是为什么我们有一个 if 条件。最终,我们使用createCanvas.drawImage(img,0,0,t.imgWidth,t.imgHeight);这行代码来真正渲染图像。
实现裁剪机制
要求:
- 熟悉 JavaScript 中的 Canvas API,用于绘图和图像操作。
功能:
- 在显示的图像上添加一个裁剪区域,并使该区域可拖动。这为用户提供了选择所需裁剪区域的灵活性。
在main.js中添加相关方法。
- 创建一个
cutImage方法
cutImage方法主要负责两项任务:一是创建一个遮罩层,二是使用 CSS 的background属性提供所选裁剪区域的实时预览。
cutImage: function () {
var t = this;
t.editBox.height = t.imgHeight;
t.editBox.width = t.imgWidth;
t.editBox.style.display = "block";
t.editBox.style.left = t.px;
t.editBox.style.top = t.py;
var cover = t.editBox.getContext("2d");
cover.fillStyle = "rgba(0, 0, 0, 0.5)";
cover.fillRect(0, 0, t.imgWidth, t.imgHeight);
cover.clearRect(t.sx, t.sy, t.sHeight, t.sWidth);
document.getElementById("show_edit").style.background =
"url(" + t.imgUrl + ")" + -t.sx + "px " + -t.sy + "px no-repeat";
document.getElementById("show_edit").style.height = t.sHeight + "px";
document.getElementById("show_edit").style.width = t.sWidth + "px";
},
- 创建一个
drag方法
drag: function () {
var t = this;
var draging = false;
var startX = 0;
var startY = 0;
document.getElementById("cover_box").onmousemove = function (e) {
var pageX = e.pageX - (t.regional.offsetLeft + this.offsetLeft);
var pageY = e.pageY - (t.regional.offsetTop + this.offsetTop);
if (
pageX > t.sx &&
pageX < t.sx + t.sWidth &&
pageY > t.sy &&
pageY < t.sy + t.sHeight
) {
this.style.cursor = "move";
this.onmousedown = function () {
draging = true;
t.ex = t.sx;
t.ey = t.sy;
startX = e.pageX - (t.regional.offsetLeft + this.offsetLeft);
startY = e.pageY - (t.regional.offsetTop + this.offsetTop);
};
window.onmouseup = function () {
draging = false;
};
if (draging) {
if (t.ex + (pageX - startX) < 0) {
t.sx = 0;
} else if (t.ex + (pageX - startX) + t.sWidth > t.imgWidth) {
t.sx = t.imgWidth - t.sWidth;
} else {
t.sx = t.ex + (pageX - startX);
}
if (t.ey + (pageY - startY) < 0) {
t.sy = 0;
} else if (t.ey + (pageY - startY) + t.sHeight > t.imgHeight) {
t.sy = t.imgHeight - t.sHeight;
} else {
t.sy = t.ey + (pageY - startY);
}
t.cutImage();
}
} else {
this.style.cursor = "auto";
}
};
},
要理解这个方法,你需要掌握以下关键点:
var pageX = e.pageX - (t.regional.offsetLeft + this.offsetLeft);
var pageY = e.pageY - (t.regional.offsetTop + this.offsetTop);
通过以上两行代码,我们获取了鼠标与背景图像之间的距离。e.pageX表示鼠标到浏览器左边缘的距离,而t.regional.offsetLeft + this.offsetLeft计算了图像到浏览器左边缘的距离。同样,顶部距离也可以推导出来。
if ( pageX > t.sx && pageX < t.sx + t.sWidth && pageY > t.sy && pageY < t.sy + t.sHeight )
理解了鼠标与背景图像之间的距离后,这应该很容易理解:它确定鼠标是否在图像区域内。
t.ex = t.sx;
t.ey = t.sy;
startX = e.pageX - (t.regional.offsetLeft + this.offsetLeft);
startY = e.pageY - (t.regional.offsetTop + this.offsetTop);
这两段代码值得注意。前两行记录了上一次截图时的坐标(如果没有上一次截图,则是初始坐标);后两行记录了鼠标按下时的坐标。你可以分别使用console.log()检查这些值。
if (draging) {
if (t.ex + (pageX - startX) < 0) {
t.sx = 0;
} else if (t.ex + (pageX - startX) + t.sWidth > t.imgWidth) {
t.sx = t.imgWidth - t.sWidth;
} else {
t.sx = t.ex + (pageX - startX);
}
if (t.ey + (pageY - startY) < 0) {
t.sy = 0;
} else if (t.ey + (pageY - startY) + t.sHeight > t.imgHeight) {
t.sy = t.imgHeight - t.sHeight;
} else {
t.sy = t.ey + (pageY - startY);
}
t.cutImage();
}
上述代码本质上是说:如果我们正在拖动,我们需要根据坐标变化实时更新t.sx和t.sy的值,并调用cutImage方法以提供实时预览。
移动过程中裁剪区域的坐标 = 上次记录的位置 + (当前鼠标位置 - 鼠标按下时的位置)
保存裁剪后的图像
要求:
- 了解如何使用 canvas 提取和显示图像数据。
功能:
- 在裁剪出所需的图像区域后,允许用户保存该裁剪区域,并在屏幕上显示结果。
增强main.js中的初始化函数,以处理裁剪图像的保存。
var postFile = {
init: function () {
//...
document.getElementById("save_button").onclick = function () {
t.editPic.height = t.sHeight;
t.editPic.width = t.sWidth;
var ctx = t.editPic.getContext("2d");
var images = new Image();
images.src = t.imgUrl;
images.onload = function () {
ctx.drawImage(
images,
t.sx,
t.sy,
t.sHeight,
t.sWidth,
0,
0,
t.sHeight,
t.sWidth
);
document.getElementById("show_pic").getElementsByTagName("img")[0].src =
t.editPic.toDataURL();
};
};
}
};
postFile.init();
与实现paintImage方法类似,我们首先监听保存按钮的点击事件。然后,我们使用drawImage方法来渲染所选的图像区域。最后,我们使用toDataURL方法将图像转换为 base64 编码格式。然后,这个值被赋给show_pic下img的src属性。这样,图像裁剪和保存就完成了。
测试工具
- 在网页浏览器中打开
index.html。
- 上传一张图像并测试裁剪功能。
- 页面效果如下:

总结
恭喜你!你已经使用 HTML5 和 JavaScript 构建了一个基本的图像裁剪工具。可以在此基础上添加更高级的功能或进行其他图像操作任务。通过改进这个工具或探索其他网页开发项目来进行练习吧!



