使用HTML5构建图像裁剪工具

JavaScriptJavaScriptBeginner
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

简介

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

👀 预览

图像裁剪工具演示

🎯 任务

在本项目中,你将学习:

  • 如何为图像裁剪工具创建HTML结构
  • 如何使用CSS对网页进行样式设计,使其具有视觉吸引力
  • 如何使用JavaScript初始化变量和事件监听器,以处理用户交互
  • 如何使用JavaScript中的FileReader API处理图像上传和显示
  • 如何使用JavaScript中的Canvas API实现裁剪机制
  • 如何保存裁剪后的图像并显示结果

🏆 成果

完成本项目后,你将能够:

  • 理解HTML标签和结构
  • 有效地应用CSS属性和选择器
  • 利用JavaScript语法、变量和事件监听器
  • 在JavaScript中利用FileReader API处理文件上传
  • 使用JavaScript中的Canvas API实现图像操作

Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL javascript(("`JavaScript`")) -.-> javascript/BasicConceptsGroup(["`Basic Concepts`"]) javascript(("`JavaScript`")) -.-> javascript/DOMManipulationGroup(["`DOM Manipulation`"]) javascript/BasicConceptsGroup -.-> javascript/variables("`Variables`") javascript/DOMManipulationGroup -.-> javascript/dom_select("`DOM Selection`") javascript/DOMManipulationGroup -.-> javascript/dom_manip("`DOM Manipulation`") javascript/DOMManipulationGroup -.-> javascript/event_handle("`Event Handling`") subgraph Lab Skills javascript/variables -.-> lab-445698{{"`使用HTML5构建图像裁剪工具`"}} javascript/dom_select -.-> lab-445698{{"`使用HTML5构建图像裁剪工具`"}} javascript/dom_manip -.-> lab-445698{{"`使用HTML5构建图像裁剪工具`"}} javascript/event_handle -.-> lab-445698{{"`使用HTML5构建图像裁剪工具`"}} end

构建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.pxt.py表示实时预览区域中背景图像的坐标;t.sxt.syt.sHeightt.sWidth分别表示图像的x、y坐标以及宽度、高度。

我们还通过document.getElementById获取了几个稍后要操作的元素。

document
  .getElementById("post_file")
  .addEventListener("change", t.handleFiles, false);

我们监听idpost_fileinput表单的change事件,以处理用户上传的文件。这里,我们将其委托给handleFiles函数。所以,接下来我们将实现handleFiles函数。

✨ 查看解决方案并练习

处理图像上传与显示

要求

  • 具备JavaScript中FileReader API的基础知识。

功能

  • 确保用户上传图像时,它能被正确处理、读取并显示在屏幕上。

main.js中扩展函数,用于处理和显示上传的图像。

  1. 实现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);
        };
},
  1. 实现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中添加相关方法。

  1. 创建一个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";
},
  1. 创建一个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.sxt.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_picimgsrc属性。这样,图像裁剪和保存就完成了。

✨ 查看解决方案并练习

测试工具

  • 在网页浏览器中打开index.html
    open web
  • 上传一张图像并测试裁剪功能。
  • 页面效果如下:
    image cropping tool demo
✨ 查看解决方案并练习

总结

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

您可能感兴趣的其他 JavaScript 教程