程序介绍

介绍

有时候想要将纵向的图片展示到电脑桌面上当壁纸,但是又因为尺寸不合适,使用市面上的截图软件不能准确截取到想要的宽高。因此,打算使用HTML和JavaScript实现准确截取图片功能,HTML用于展示图片和截取结果,JavaScript用于实现截取图片功能

页面展示

功能菜单有:选择文件、截取图片

图片展示区

页面展示

效果展示

效果展示

操作流程

点击选择文件按钮,选择需要截取的源图片;滑动浅米色蒙版,选择要截取的部分;

image-20231002215155482

Html代码展示

<div id="main">
<!-- 功能菜单 -->
<div class="btn-group">
<input type="file" onchange="handleFileSelect(event)" accept=".jpg,.png">
<button id="crop-image">截取图片</button>
</div>
<!-- 图片展示 -->
<div id="img-group">
<img alt="" srcset="" id="my-img">
</div>
</div>

功能代码分析

选择图片

<input type="file" onchange="handleFileSelect(event)" accept=".jpg,.png">

input是一个HTML元素,当前类型为file文件类型,用于选择文件。当用户选择文件后,会触发一个名为handleFileSelect的事件处理函数。该输入元素只接受后缀名为.jpg.png的文件(accept属性)。

/**
* Description: 处理文件选择
* @param {Event} event - 触发事件对象
*/
function handleFileSelect(event) {
clearBox(); // 清除Box
var file = event.target.files[0]; // 所选择的文件对象
var reader = new FileReader();
reader.onload = function (e) {
// 文件读取器加载事件
var imgElement = document.getElementById("my-img");
imgElement.src = e.target.result;
imgElement.onload = function () {
let maxHeight = 0.8; // 最大高度
const viewportHeight = window.innerHeight; // 当前设备高度
const maxHeightPixels = viewportHeight * maxHeight; // 当前设备最大高度(像素值)

let height = imgElement.height; // 图像高度
let width = imgElement.width; // 图像宽度

console.log("原始图片大小:", width, height);

if (height > maxHeightPixels) {
ratio = maxHeightPixels / height;
imgElement.style.width = `${width * ratio}px`
imgElement.style.height = `${height * ratio}px`
}
init_box_line(); // 初始化裁剪图片蒙版
};
};
// 将文件转为DataURL
reader.readAsDataURL(file);
}

在选择图片前,会先清除先前生成的图片蒙版(clearBox);选择完图片并且渲染到界面上后,将初始化图片蒙版(init_box_line

input选中的文件存在于event.target.files数组中,在FileReader对象加载过程中加载图片标签,将选择到的图片地址转为DataURL赋值到img标签上,img就可以显示需要打开的图片文件。并且根据设置的最大高度,等比例显示图片。所设置的最大高度(0.8)为相对于需要显示在界面上的大小,即表示图片的高为界面的80%,其中需要将80%转为像素值。

清除图片蒙版

/**
* Description: 清除box
*/
function clearBox() {
var img = document.getElementById("my-img");
img.style.width = ""
img.style.height = ""
var box_line = document.getElementById("box_line");
if (box_line) {
let parent = box_line.parentNode;
parent.removeChild(box_line);
}
}

先将展示图片标签的img的宽高设为空,并且搜索是否存在图片蒙版,如果存在则通过图片蒙版的父节点删除该蒙版

初始化图片蒙版

生产图片蒙版

var box_w, box_h;  // 图片蒙版宽、高
var r = 344 / 193; // 当前显示屏的宽高比例(344mm x 193mm、15.6寸)
var img = document.getElementById("my-img");
var w = img.clientWidth // 图片当前宽度
var h = img.clientHeight // 图片当前高低

if (w < h) {
box_w = w;
box_h = w / r;

if (box_h > h) {
box_h = h
box_w = h / r
}
} else if (w >= h) {
box_w = h * r;
box_h = h;

if (box_w > w) {
box_w = w;
box_h = w / r;
}
}
console.log("展示的图片大小:", w, h);
console.log("蒙版框大小:", box_w, box_h);
// console.log(img, w, h, box_w, box_h);

var box_line = document.createElement("div");
box_line.setAttribute("id", "box_line");
box_line.style.width = `${box_w}px`;
box_line.style.height = `${box_h}px`;
document.getElementById("img-group").append(box_line);

如果图片宽度小于图片高度,则图片蒙版宽度为图片宽度,并且图片蒙版高度为根据比例计算图片蒙版宽度得到;如果图片蒙版高度高于图片高度,则图片蒙版的高度为当前图片高度,图片蒙版的宽度为根据比例计算当前图片高度得到

定义触发事件

// 定义变量来保存鼠标按下时的初始位置
var initialX;
var initialY;

// 监听鼠标按下事件
box_line.addEventListener("mousedown", function (event) {
// 记录鼠标按下时的初始位置
initialX = event.clientX;
initialY = event.clientY;

// 添加鼠标移动和鼠标释放事件的监听器
document.addEventListener("mousemove", dragElement);
document.addEventListener("mouseup", stopDragging);
});

// 定义鼠标移动事件的处理函数
function dragElement(event) {
// 计算鼠标移动的距离
var deltaX = event.clientX - initialX;
var deltaY = event.clientY - initialY;

if (deltaX > w) {
deltaX = w;
}

if (deltaY > h) {
deltaY = h;
}

// 更新div元素的位置

if (box_h == h) {
if (
box_line.offsetLeft + deltaX >= 0 &&
box_line.offsetLeft + deltaX + box_w <= w
) {
box_line.style.left = box_line.offsetLeft + deltaX + "px";
}
} else if (box_w == w) {
if (
box_line.offsetTop + deltaY >= 0 &&
box_line.offsetTop + deltaY + box_h <= h
) {
box_line.style.top = box_line.offsetTop + deltaY + "px";
}
}

// 更新初始位置
initialX = event.clientX;
initialY = event.clientY;
}

// 定义鼠标释放事件的处理函数
function stopDragging() {
// 移除鼠标移动和鼠标释放事件的监听器
document.removeEventListener("mousemove", dragElement);
document.removeEventListener("mouseup", stopDragging);
}

鼠标按下:当鼠标在图片蒙版上方,并且鼠标按下,会先记录当前状态的横纵坐标,并添加鼠标移动和鼠标抬起监听器;

鼠标移动:在鼠标移动过程中,根据当前事件记录的横纵坐标减去鼠标按下时记录的初始位置,得到鼠标移动的距离,即图片蒙版移动的距离。如果移动的横向距离/纵向距离大于图片宽度/高度,则移动的距离默认为图片的最大宽度/最大高度。如果图片蒙版高度为图片高度,并且图片蒙版相对于图片左侧边框的距离加上移动距离大于0(即图片蒙版在图片左侧边框之内,没有超出左侧边框,超出左侧边框会变成负数)和图片蒙版相对于图片左侧边框的距离加上移动距离和图片蒙版宽度小于图片宽度(即图片蒙版移动的距离和图片蒙版的宽度在图片右侧的边框之内),则才可以移动图片蒙版。最后重新更新初始化位置。

鼠标抬起:释放所有的鼠标事件

下载截取的图片

function captureImage() {
var divElement = box_line;
var imgElement = img;
var divRect = divElement.getBoundingClientRect();
var imgRect = imgElement.getBoundingClientRect();

var divLeft = divRect.left - imgRect.left;
var divTop = divRect.top - imgRect.top;
var divWidth = divRect.width;
var divHeight = divRect.height;

var canvas = document.createElement("canvas");
canvas.width = divWidth / ratio;
canvas.height = divHeight / ratio;
var context = canvas.getContext("2d");

context.drawImage(
imgElement, // 要绘制的图像元素
divLeft / ratio, // 源图像的左上角在目标画布中的 X 坐标
divTop / ratio, // 源图像的左上角在目标画布中的 Y 坐标。
divWidth / ratio, // 源图像在目标画布中要绘制的宽度
divHeight / ratio, // 源图像在目标画布中要绘制的高度
0, // 目标画布中绘制的图像的左上角的 X 坐标
0, // 目标画布中绘制的图像的左上角的 Y 坐标
divWidth / ratio, // 目标画布中绘制的图像的宽度
divHeight / ratio // 目标画布中绘制的图像的高度
);

var link = document.createElement("a");
link.href = canvas.toDataURL();
link.download = "crop-image.png";
link.click();
link.remove()
}

var crop_image = document.getElementById("crop-image");
crop_image.addEventListener("click", captureImage);

Canvas自带的drawImage函数需要提供9个参数,

  1. imgElement即为需要从上面截取图片的元素;
  2. divLeft / ratio为源图像的左上角在目标画布中的 X 坐标(为保持和原图片一样的像素分辨率,将所有的坐标参数转为源图片的大小,);
  3. divTop / ratio为源图像的左上角在目标画布中的 Y 坐标;
  4. divWidth / ratio为源图像在目标画布中要绘制的宽度;
  5. 0为目标画布中绘制的图像的左上角的 X 坐标
  6. 0为目标画布中绘制的图像的左上角的 Y 坐标
  7. divWidth / ratio为目标画布中绘制的图像的宽度
  8. divHeight / ratio为目标画布中绘制的图像的高度

最后将截取到的图片转为DataURL,并且触发下载

完整代码

HTML

<!DOCTYPE html>
<html>

<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title>图片裁剪</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<script src="./static/js/main.js"></script>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
flex-wrap: nowrap;
}

#main {
position: relative;
display: flex;
flex-direction: column;
}

#img-group {
position: relative;
}

#box_line {
position: absolute;
top: 0;
left: 0;
/* border: 10px solid #000; */
background-color: bisque;
opacity: 0.2;
}
</style>
</head>

<body>
<div id="main">
<!-- 功能菜单 -->
<div class="btn-group">
<input type="file" onchange="handleFileSelect(event)" accept=".jpg,.png">
<button id="crop-image">截取图片</button>
</div>
<!-- 图片展示 -->
<div id="img-group">
<img alt="" srcset="" id="my-img">
</div>
</div>
</body>

</html>

JavaScript

main.js

var ratio = 1  // 展示图片宽高与原始图像宽高比率

/**
* Description: 处理文件选择
* @param {Event} event - 触发事件对象
*/
function handleFileSelect(event) {
clearBox(); // 清除Box
var file = event.target.files[0]; // 所选择的文件对象
var reader = new FileReader();
reader.onload = function (e) {
// 文件读取器加载事件
var imgElement = document.getElementById("my-img");
imgElement.src = e.target.result;
imgElement.onload = function () {
let maxHeight = 0.8; // 最大高度
const viewportHeight = window.innerHeight; // 当前设备高度
const maxHeightPixels = viewportHeight * maxHeight; // 当前设备最大高度(像素值)

let height = imgElement.height; // 图像高度
let width = imgElement.width; // 图像宽度

console.log("原始图片大小:", width, height);

if (height > maxHeightPixels) {
ratio = maxHeightPixels / height;
imgElement.style.width = `${width * ratio}px`
imgElement.style.height = `${height * ratio}px`
}
init_box_line(); // 初始化裁剪图片蒙版
};
};
// 将文件转为DataURL
reader.readAsDataURL(file);
}

/**
* Description: 清除box
*/
function clearBox() {
var img = document.getElementById("my-img");
img.style.width = ""
img.style.height = ""
var box_line = document.getElementById("box_line");
if (box_line) {
let parent = box_line.parentNode;
parent.removeChild(box_line);
}
}

/**
* Description: 初始化box
*/
function init_box_line() {
var box_w, box_h;
var r = 344 / 193;
var img = document.getElementById("my-img");
var w = img.clientWidth
var h = img.clientHeight

if (w < h) {
box_w = w;
box_h = w / r;

if (box_h > h) {
box_h = h
box_w = h / r
}
} else if (w >= h) {
box_w = h * r;
box_h = h;

if (box_w > w) {
box_w = w;
box_h = w / r;
}
}
console.log("展示的图片大小:", w, h);
console.log("蒙版框大小:", box_w, box_h);
// console.log(img, w, h, box_w, box_h);

var box_line = document.createElement("div");
box_line.setAttribute("id", "box_line");
box_line.style.width = `${box_w}px`;
box_line.style.height = `${box_h}px`;
document.getElementById("img-group").append(box_line);

// 定义变量来保存鼠标按下时的初始位置
var initialX;
var initialY;

// 监听鼠标按下事件
box_line.addEventListener("mousedown", function (event) {
// 记录鼠标按下时的初始位置
initialX = event.clientX;
initialY = event.clientY;

// 添加鼠标移动和鼠标释放事件的监听器
document.addEventListener("mousemove", dragElement);
document.addEventListener("mouseup", stopDragging);
});

// 定义鼠标移动事件的处理函数
function dragElement(event) {
// 计算鼠标移动的距离
var deltaX = event.clientX - initialX;
var deltaY = event.clientY - initialY;

if (deltaX > w) {
deltaX = w;
}

if (deltaY > h) {
deltaY = h;
}

// 更新div元素的位置

if (box_h == h) {
if (
box_line.offsetLeft + deltaX >= 0 &&
box_line.offsetLeft + deltaX + box_w <= w
) {
box_line.style.left = box_line.offsetLeft + deltaX + "px";
}
} else if (box_w == w) {
if (
box_line.offsetTop + deltaY >= 0 &&
box_line.offsetTop + deltaY + box_h <= h
) {
box_line.style.top = box_line.offsetTop + deltaY + "px";
}
}

// 更新初始位置
initialX = event.clientX;
initialY = event.clientY;
}

// 定义鼠标释放事件的处理函数
function stopDragging() {
// 移除鼠标移动和鼠标释放事件的监听器
document.removeEventListener("mousemove", dragElement);
document.removeEventListener("mouseup", stopDragging);
}

function captureImage() {
var divElement = box_line;
var imgElement = img;
var divRect = divElement.getBoundingClientRect();
var imgRect = imgElement.getBoundingClientRect();

var divLeft = divRect.left - imgRect.left;
var divTop = divRect.top - imgRect.top;
var divWidth = divRect.width;
var divHeight = divRect.height;

var canvas = document.createElement("canvas");
canvas.width = divWidth / ratio;
canvas.height = divHeight / ratio;
var context = canvas.getContext("2d");

context.drawImage(
imgElement, // 要绘制的图像元素
divLeft / ratio, // 源图像的左上角在目标画布中的 X 坐标
divTop / ratio, // 源图像的左上角在目标画布中的 Y 坐标。
divWidth / ratio, // 源图像在目标画布中要绘制的宽度
divHeight / ratio, // 源图像在目标画布中要绘制的高度
0, // 目标画布中绘制的图像的左上角的 X 坐标
0, // 目标画布中绘制的图像的左上角的 Y 坐标
divWidth / ratio, // 目标画布中绘制的图像的宽度
divHeight / ratio // 目标画布中绘制的图像的高度
);

var link = document.createElement("a");
link.href = canvas.toDataURL();
link.download = "crop-image.png";
link.click();
link.remove()
}

var crop_image = document.getElementById("crop-image");
crop_image.addEventListener("click", captureImage);
}