当前位置: 首页 > article >正文

使用 HTML + JavaScript 实现图片裁剪上传功能

本文将详细介绍一个基于 HTML 和 JavaScript 实现的图片裁剪上传功能。该功能支持文件选择、拖放上传、图片预览、区域选择、裁剪操作以及图片下载等功能,适用于需要进行图片处理的 Web 应用场景。

效果演示

image-20250602112435691

image-20250602112543017

项目概述

本项目主要包含以下核心功能:

  • 文件选择与拖放上传
  • 裁剪框拖动与调整大小
  • 图片裁剪
  • 图片上传(模拟)与下载

页面结构

上传区域

实现友好的文件选择体验,并支持拖放上传。

<div class="upload-section"><input type="file" id="fileInput" accept="image/*"><label for="fileInput" class="file-label">选择图片</label><p>或拖放图片到此处</p>
</div>
预览区域

分为两个部分,左侧显示原始图片和裁剪框,右侧展示裁剪后的结果。

<div class="preview-section"><div class="image-container"><div><h3>原始图片</h3><img id="originalImage" style="max-width: 100%; display: none;"><div class="cropper-container" id="cropperContainer" style="display: none;"><canvas id="sourceCanvas"></canvas><div class="selection-box" id="selectionBox"><div class="resize-handle"></div></div></div><p class="instruction">拖动选择框可移动位置,拖动右下角可调整大小</p></div><div><h3>裁剪结果</h3><canvas id="croppedCanvas" style="display: none;"></canvas><p id="noCropMessage">请先选择图片并设置裁剪区域</p></div></div>
</div>
操作按钮

提供“裁剪”、“上传”、“下载”和“重置”按钮,方便用户进行各种操作。

<div class="controls"><button id="cropBtn" disabled>裁剪图片</button><button id="uploadBtn" disabled>上传图片</button><button id="downloadBtn" disabled>下载图片</button><button id="resetBtn">重置</button>
</div>

核心功能实现

定义基础变量

获取DOM元素

const fileInput = document.getElementById('fileInput');
const originalImage = document.getElementById('originalImage');
const sourceCanvas = document.getElementById('sourceCanvas');
const croppedCanvas = document.getElementById('croppedCanvas');
const cropperContainer = document.getElementById('cropperContainer');
const selectionBox = document.getElementById('selectionBox');
const cropBtn = document.getElementById('cropBtn');
const uploadBtn = document.getElementById('uploadBtn');
const resetBtn = document.getElementById('resetBtn');
const noCropMessage = document.getElementById('noCropMessage');
const downloadBtn = document.getElementById('downloadBtn');

定义全局变量

let isDragging = false;
let isResizing = false;
let startX, startY;
let selection = {x: 0,y: 0,width: 0,height: 0,startX: 0,startY: 0,startWidth: 0,startHeight: 0
};
let imageRatio = 1;
文件选择

使用 FileReader API 将选中的图片读取为 Data URL 并显示在页面上。

function handleFileSelect(event) {const file = event.target.files[0];if (!file || !file.type.match('image.*')) {alert('请选择有效的图片文件');return;}const reader = new FileReader();reader.onload = function(e) {originalImage.src = e.target.result;originalImage.onload = function() {initCropper();};};reader.readAsDataURL(file);
}
拖放上传

通过监听 dragover、dragleave 和 drop 事件实现拖放上传功能。

const uploadSection = document.querySelector('.upload-section');
uploadSection.addEventListener('dragover', (e) => {e.preventDefault();uploadSection.style.borderColor = '#4CAF50';
});uploadSection.addEventListener('dragleave', () => {uploadSection.style.borderColor = '#ccc';
});uploadSection.addEventListener('drop', (e) => {e.preventDefault();uploadSection.style.borderColor = '#ccc';if (e.dataTransfer.files.length) {fileInput.files = e.dataTransfer.files;handleFileSelect({ target: fileInput });}
});
图片预览与裁剪框初始化

在图片加载完成后,绘制到 canvas 上,并根据图片尺寸调整画布大小。初始化一个固定比例的裁剪框,居中显示在画布上。

function initCropper() {// 显示原始图片和裁剪区域originalImage.style.display = 'none';cropperContainer.style.display = 'inline-block';// 设置canvas尺寸const maxWidth = 500;imageRatio = originalImage.naturalWidth / originalImage.naturalHeight;let canvasWidth, canvasHeight;if (originalImage.naturalWidth > maxWidth) {canvasWidth = maxWidth;canvasHeight = maxWidth / imageRatio;} else {canvasWidth = originalImage.naturalWidth;canvasHeight = originalImage.naturalHeight;}sourceCanvas.width = canvasWidth;sourceCanvas.height = canvasHeight;// 绘制图片到canvasconst ctx = sourceCanvas.getContext('2d');ctx.drawImage(originalImage, 0, 0, canvasWidth, canvasHeight);// 初始化选择框 (1:1比例)const boxSize = Math.min(canvasWidth, canvasHeight) * 0.6;selection = {x: Math.max(0, Math.min((canvasWidth - boxSize) / 2, canvasWidth - boxSize)), // 确保初始位置在画布范围内y: Math.max(0, Math.min((canvasHeight - boxSize) / 2, canvasHeight - boxSize)), // 确保初始位置在画布范围内width: boxSize,height: boxSize,startX: 0,startY: 0,startWidth: 0,startHeight: 0};updateSelectionBox();cropBtn.disabled = false;
}
裁剪框拖动与调整大小

通过监听鼠标事件(mousedown、mousemove、mouseup)实现裁剪框的拖动和调整大小功能。确保裁剪框始终位于画布范围内,并保持指定的比例。

selectionBox.addEventListener('mousedown', startDrag);
document.addEventListener('mousemove', handleDrag);
document.addEventListener('mouseup', endDrag);
const resizeHandle = document.querySelector('.resize-handle');
resizeHandle.addEventListener('mousedown', (e) => {e.stopPropagation();startResize(e);
});
function startDrag(e) {if (e.target.classList.contains('resize-handle')) {return; // 忽略调整大小手柄的点击}isDragging = true;startX = e.clientX;startY = e.clientY;// 存储初始位置selection.startX = selection.x;selection.startY = selection.y;e.preventDefault();
}
function startResize(e) {isResizing = true;startX = e.clientX;startY = e.clientY;// 存储初始尺寸和位置selection.startX = selection.x;selection.startY = selection.y;selection.startWidth = selection.width;selection.startHeight = selection.height;e.preventDefault();
}
function handleDrag(e) {if (!isDragging && !isResizing) return;const dx = e.clientX - startX;const dy = e.clientY - startY;if (isDragging) {// 处理移动选择框let newX = selection.startX + dx;let newY = selection.startY + dy;// 限制在canvas范围内newX = Math.max(0, Math.min(newX, sourceCanvas.width - selection.width));newY = Math.max(0, Math.min(newY, sourceCanvas.height - selection.height));selection.x = newX;selection.y = newY;} else if (isResizing) {// 处理调整大小 (保持1:1比例)let newSize = Math.max(10, Math.min(selection.startWidth + (dx + dy) / 2, // 取dx和dy的平均值使调整更平滑Math.min(sourceCanvas.width - selection.startX,sourceCanvas.height - selection.startY)));// 应用新尺寸 (保持正方形)selection.width = newSize;selection.height = newSize;// 确保裁剪框不会超出画布范围if (selection.x + selection.width > sourceCanvas.width) {selection.x = sourceCanvas.width - selection.width;}if (selection.y + selection.height > sourceCanvas.height) {selection.y = sourceCanvas.height - selection.height;}}updateSelectionBox();
}
function endDrag() {isDragging = false;isResizing = false;
}
图片裁剪与结果展示

使用 drawImage 方法从源画布中裁剪出指定区域,并将其绘制到目标画布上。

function cropImage() {const ctx = croppedCanvas.getContext('2d');// 设置裁剪后canvas的尺寸 (1:1)croppedCanvas.width = selection.width;croppedCanvas.height = selection.height;// 执行裁剪ctx.drawImage(sourceCanvas,selection.x, selection.y, selection.width, selection.height, // 源图像裁剪区域0, 0, selection.width, selection.height                     // 目标canvas绘制区域);// 显示裁剪结果croppedCanvas.style.display = 'block';noCropMessage.style.display = 'none';uploadBtn.disabled = false;downloadBtn.disabled = false;
}
图片上传与下载

提供模拟的上传功能,使用 toBlob 方法获取裁剪后的图片数据。支持将裁剪后的图片下载为 JPEG 格式的文件。

function uploadImage() {// 在实际应用中,这里应该将图片数据发送到服务器croppedCanvas.toBlob((blob) => {// 创建FormData对象并添加图片const formData = new FormData();formData.append('croppedImage', blob, 'cropped-image.jpg');// 模拟上传延迟setTimeout(() => {alert('图片上传成功!(模拟)');console.log('上传的图片数据:', blob);// 在实际应用中,你可能需要处理服务器响应}, 1000);}, 'image/jpeg', 0.9);
}
function downloadImage() {if (!croppedCanvas.width || !croppedCanvas.height) {alert('请先裁剪图片');return;}// 创建一个临时的a标签用于触发下载const link = document.createElement('a');link.href = croppedCanvas.toDataURL('image/jpeg', 0.9);link.download = 'cropped-image.jpg'; // 设置下载文件名link.click();
}

扩展建议

  • 支持多种裁剪比例:可以扩展代码以支持不同的裁剪比例(如 4:3、16:9),并通过 UI 控件让用户选择。
  • 图像缩放功能:添加对图片缩放的支持,允许用户放大或缩小图片以便更精确地选择裁剪区域。
  • 服务器端集成:实际应用中,应将裁剪后的图片发送到服务器进行存储和处理,可以通过请求实现。

完整代码

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>图片裁剪上传</title><style>body {max-width: 1200px;margin: 0 auto;padding: 20px;}.container {display: flex;flex-direction: column;gap: 20px;}h1 {text-align: center;}.upload-section {padding: 20px;text-align: center;border-radius: 5px;background: #f8f9fa;border: 2px dashed #dee2e6;transition: all 0.3s ease;cursor: pointer;}.upload-section:hover {border-color: #4CAF50;background: rgba(76, 175, 80, 0.05);}.file-label {background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%);color: white;border-radius: 25px;padding: 12px 24px;transition: transform 0.2s;}.file-label:hover {transform: translateY(-2px);box-shadow: 0 4px 15px rgba(76, 175, 80, 0.3);}.preview-section {display: flex;flex-direction: column;gap: 20px;background: #ffffff;border-radius: 12px;padding: 20px;box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);}canvas {max-width: 100%;border: 1px solid #eee;display: block;}.cropper-container {position: relative;display: inline-block;}.selection-box {position: absolute;border: 2px dashed #000;background: rgba(255, 255, 255, 0.3);cursor: move;box-sizing: border-box;}.resize-handle {position: absolute;width: 10px;height: 10px;background: #fff;border: 2px solid #000;border-radius: 50%;bottom: -5px;right: -5px;cursor: se-resize;}button {padding: 10px 15px;background-color: #4CAF50;color: white;border: none;border-radius: 4px;cursor: pointer;font-size: 16px;}button:hover {background-color: #45a049;}button {background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%);border-radius: 25px;padding: 12px 24px;font-weight: 500;box-shadow: 0 4px 6px rgba(76, 175, 80, 0.1);transition: all 0.3s ease;}button:hover {transform: translateY(-2px);box-shadow: 0 6px 12px rgba(76, 175, 80, 0.2);}button:disabled {opacity: 0.6;cursor: not-allowed;}input[type="file"] {display: none;}.file-label {display: inline-block;padding: 10px 15px;background-color: #f0f0f0;border-radius: 4px;cursor: pointer;margin-bottom: 10px;}.controls {display: flex;gap: 10px;margin-top: 10px;justify-content: center;}.instruction {font-size: 14px;color: #666;margin-top: 10px;}.image-container {display: flex;gap: 20px;}.image-container > div {width: 500px;}</style>
</head>
<body>
<div class="container"><h1>图片裁剪上传</h1><div class="upload-section"><input type="file" id="fileInput" accept="image/*"><label for="fileInput" class="file-label">选择图片</label><p>或拖放图片到此处</p></div><div class="preview-section"><div class="image-container"><div><h3>原始图片</h3><img id="originalImage" style="max-width: 100%; display: none;"><div class="cropper-container" id="cropperContainer" style="display: none;"><canvas id="sourceCanvas"></canvas><div class="selection-box" id="selectionBox"><div class="resize-handle"></div></div></div><p class="instruction">拖动选择框可移动位置,拖动右下角可调整大小</p></div><div><h3>裁剪结果</h3><canvas id="croppedCanvas" style="display: none;"></canvas><p id="noCropMessage">请先选择图片并设置裁剪区域</p></div></div></div><div class="controls"><button id="cropBtn" disabled>裁剪图片</button><button id="uploadBtn" disabled>上传图片</button><button id="downloadBtn" disabled>下载图片</button><button id="resetBtn">重置</button></div>
</div><script>// 获取DOM元素const fileInput = document.getElementById('fileInput');const originalImage = document.getElementById('originalImage');const sourceCanvas = document.getElementById('sourceCanvas');const croppedCanvas = document.getElementById('croppedCanvas');const cropperContainer = document.getElementById('cropperContainer');const selectionBox = document.getElementById('selectionBox');const cropBtn = document.getElementById('cropBtn');const uploadBtn = document.getElementById('uploadBtn');const resetBtn = document.getElementById('resetBtn');const noCropMessage = document.getElementById('noCropMessage');const downloadBtn = document.getElementById('downloadBtn');// 全局变量let isDragging = false;let isResizing = false;let startX, startY;let selection = {x: 0,y: 0,width: 0,height: 0,startX: 0,startY: 0,startWidth: 0,startHeight: 0};let imageRatio = 1;// 监听文件选择fileInput.addEventListener('change', handleFileSelect);// 拖放功能const uploadSection = document.querySelector('.upload-section');uploadSection.addEventListener('dragover', (e) => {e.preventDefault();uploadSection.style.borderColor = '#4CAF50';});uploadSection.addEventListener('dragleave', () => {uploadSection.style.borderColor = '#ccc';});uploadSection.addEventListener('drop', (e) => {e.preventDefault();uploadSection.style.borderColor = '#ccc';if (e.dataTransfer.files.length) {fileInput.files = e.dataTransfer.files;handleFileSelect({ target: fileInput });}});// 选择框鼠标事件selectionBox.addEventListener('mousedown', startDrag);document.addEventListener('mousemove', handleDrag);document.addEventListener('mouseup', endDrag);// 调整大小手柄事件const resizeHandle = document.querySelector('.resize-handle');resizeHandle.addEventListener('mousedown', (e) => {e.stopPropagation();startResize(e);});// 下载图片function downloadImage() {if (!croppedCanvas.width || !croppedCanvas.height) {alert('请先裁剪图片');return;}// 创建一个临时的a标签用于触发下载const link = document.createElement('a');link.href = croppedCanvas.toDataURL('image/jpeg', 0.9);link.download = 'cropped-image.jpg'; // 设置下载文件名link.click();}// 按钮事件cropBtn.addEventListener('click', cropImage);uploadBtn.addEventListener('click', uploadImage);resetBtn.addEventListener('click', resetAll);downloadBtn.addEventListener('click', downloadImage);// 处理文件选择function handleFileSelect(event) {const file = event.target.files[0];if (!file || !file.type.match('image.*')) {alert('请选择有效的图片文件');return;}const reader = new FileReader();reader.onload = function(e) {originalImage.src = e.target.result;originalImage.onload = function() {initCropper();};};reader.readAsDataURL(file);}// 初始化裁剪器function initCropper() {// 显示原始图片和裁剪区域originalImage.style.display = 'none';cropperContainer.style.display = 'inline-block';// 设置canvas尺寸const maxWidth = 500;imageRatio = originalImage.naturalWidth / originalImage.naturalHeight;let canvasWidth, canvasHeight;if (originalImage.naturalWidth > maxWidth) {canvasWidth = maxWidth;canvasHeight = maxWidth / imageRatio;} else {canvasWidth = originalImage.naturalWidth;canvasHeight = originalImage.naturalHeight;}sourceCanvas.width = canvasWidth;sourceCanvas.height = canvasHeight;// 绘制图片到canvasconst ctx = sourceCanvas.getContext('2d');ctx.drawImage(originalImage, 0, 0, canvasWidth, canvasHeight);// 初始化选择框 (1:1比例)const boxSize = Math.min(canvasWidth, canvasHeight) * 0.6;selection = {x: Math.max(0, Math.min((canvasWidth - boxSize) / 2, canvasWidth - boxSize)), // 确保初始位置在画布范围内y: Math.max(0, Math.min((canvasHeight - boxSize) / 2, canvasHeight - boxSize)), // 确保初始位置在画布范围内width: boxSize,height: boxSize,startX: 0,startY: 0,startWidth: 0,startHeight: 0};updateSelectionBox();cropBtn.disabled = false;}// 更新选择框位置和尺寸function updateSelectionBox() {selectionBox.style.left = `${selection.x}px`;selectionBox.style.top = `${selection.y}px`;selectionBox.style.width = `${selection.width}px`;selectionBox.style.height = `${selection.height}px`;}// 开始拖动function startDrag(e) {if (e.target.classList.contains('resize-handle')) {return; // 忽略调整大小手柄的点击}isDragging = true;startX = e.clientX;startY = e.clientY;// 存储初始位置selection.startX = selection.x;selection.startY = selection.y;e.preventDefault();}// 处理拖动function handleDrag(e) {if (!isDragging && !isResizing) return;const dx = e.clientX - startX;const dy = e.clientY - startY;if (isDragging) {// 处理移动选择框let newX = selection.startX + dx;let newY = selection.startY + dy;// 限制在canvas范围内newX = Math.max(0, Math.min(newX, sourceCanvas.width - selection.width));newY = Math.max(0, Math.min(newY, sourceCanvas.height - selection.height));selection.x = newX;selection.y = newY;} else if (isResizing) {// 处理调整大小 (保持1:1比例)let newSize = Math.max(10, Math.min(selection.startWidth + (dx + dy) / 2, // 取dx和dy的平均值使调整更平滑Math.min(sourceCanvas.width - selection.startX,sourceCanvas.height - selection.startY)));// 应用新尺寸 (保持正方形)selection.width = newSize;selection.height = newSize;// 确保裁剪框不会超出画布范围if (selection.x + selection.width > sourceCanvas.width) {selection.x = sourceCanvas.width - selection.width;}if (selection.y + selection.height > sourceCanvas.height) {selection.y = sourceCanvas.height - selection.height;}}updateSelectionBox();}// 结束拖动或调整大小function endDrag() {isDragging = false;isResizing = false;}// 开始调整大小function startResize(e) {isResizing = true;startX = e.clientX;startY = e.clientY;// 存储初始尺寸和位置selection.startX = selection.x;selection.startY = selection.y;selection.startWidth = selection.width;selection.startHeight = selection.height;e.preventDefault();}// 裁剪图片function cropImage() {const ctx = croppedCanvas.getContext('2d');// 设置裁剪后canvas的尺寸 (1:1)croppedCanvas.width = selection.width;croppedCanvas.height = selection.height;// 执行裁剪ctx.drawImage(sourceCanvas,selection.x, selection.y, selection.width, selection.height, // 源图像裁剪区域0, 0, selection.width, selection.height                     // 目标canvas绘制区域);// 显示裁剪结果croppedCanvas.style.display = 'block';noCropMessage.style.display = 'none';uploadBtn.disabled = false;downloadBtn.disabled = false;}// 上传图片function uploadImage() {// 在实际应用中,这里应该将图片数据发送到服务器croppedCanvas.toBlob((blob) => {// 创建FormData对象并添加图片const formData = new FormData();formData.append('croppedImage', blob, 'cropped-image.jpg');// 模拟上传延迟setTimeout(() => {alert('图片上传成功!(模拟)');console.log('上传的图片数据:', blob);// 在实际应用中,你可能需要处理服务器响应}, 1000);}, 'image/jpeg', 0.9);}// 重置所有function resetAll() {// 隐藏元素cropperContainer.style.display = 'none';croppedCanvas.style.display = 'none';noCropMessage.style.display = 'block';originalImage.style.display = 'none';// 重置按钮状态cropBtn.disabled = true;uploadBtn.disabled = true;downloadBtn.disabled = true;// 清除文件输入fileInput.value = '';// 清除画布const ctx = sourceCanvas.getContext('2d');ctx.clearRect(0, 0, sourceCanvas.width, sourceCanvas.height);const croppedCtx = croppedCanvas.getContext('2d');croppedCtx.clearRect(0, 0, croppedCanvas.width, croppedCanvas.height);}
</script>
</body>
</html>

相关文章:

使用 HTML + JavaScript 实现图片裁剪上传功能

本文将详细介绍一个基于 HTML 和 JavaScript 实现的图片裁剪上传功能。该功能支持文件选择、拖放上传、图片预览、区域选择、裁剪操作以及图片下载等功能&#xff0c;适用于需要进行图片处理的 Web 应用场景。 效果演示 项目概述 本项目主要包含以下核心功能&#xff1a; 文…...

Redis 缓存粒度如何控制?缓存整个对象还是部分字段?

控制 Redis 缓存粒度&#xff0c;即决定是缓存整个对象还是对象的部分字段&#xff0c;是一个需要在性能、内存使用、数据一致性、更新复杂性和开发成本之间进行权衡的决策。没有绝对的“最佳”方案&#xff0c;需要根据具体业务场景来选择。 以下是两种主要策略及其优缺点&am…...

【灵动Mini-F5265-OB】vscode+gcc工程创建、下载、调试

【前言】 【灵动Mini-F5265-OB】在官方的例程中提供了mdk、IAR的开发环境&#xff0c;使用起来非常方便。有位大佬也提供了一个gcc的示例&#xff0c;但是我使用vscode的keil插件进行工程创建&#xff0c;但是提示pack是对不上的。所以我决定重新创建我的vscode来创建开发环境。…...

程序设计实践期末考试模拟题(1)

1、排列论文 #include<bits/stdc.h> using namespace std; const int N105; vector<int>g[N]; int a[N]; int n,m; int flag; int topSort(){queue<int>q;for(int i1;i<n;i){if(a[i]0){q.push(i);}}int cnt0;flag1;while(!q.empty()){int tq.front();q.p…...

现代语言模型中的分词算法全解:从基础到高级

基础分词&#xff08;Naive Tokenization&#xff09; 最简单的分词方式是基于空格将文本拆分为单词。这是许多自然语言处理&#xff08;NLP&#xff09;任务中常用的一种分词方法。 text "Hello, world! This is a test." tokens text.split() print(f"Tok…...

HttpServletResponse 对象用来做什么?

HttpServletResponse 对象是由 Servlet 容器创建并传递给 Servlet 的 service() 方法&#xff08;以及间接传递给 doGet(), doPost() 等方法&#xff09;的。它的核心作用是让 Servlet 能够向客户端&#xff08;通常是浏览器&#xff09;发送 HTTP 响应。 通过 HttpServletRes…...

第十三章 Java基础-特殊处理

文章目录 1.包和final2.权限修饰符和代码块3.抽象类1.包和final 2.权限修饰符和代码块 3.抽象类...

MTK的Download agent是什么下载程序?

MTK(MediaTek)的Download Agent(DA)是一种与MTK设备进行通信的协议代理程序,在MTK设备的固件下载与烧录过程中起着关键作用,以下为你展开介绍: 下载原理 在MTK平台的固件下载过程中,DA会被加载到MTK设备的内部RAM中运行。它负责配置Flash及RAM的时序,从而建立起PC端…...

ArcGIS Pro 3.4 二次开发 - 地图创作 2

环境:ArcGIS Pro SDK 3.4 + .NET 8 文章目录 ArcGIS Pro 3.4 二次开发 - 地图创作 224 注记24.1 创建标注构造工具24.2 通过属性更新注释文本。注意:TEXTSTRING 注释属性必须存在24.3 旋转或移动标注24.4 获取注释文本图形24.5 获取注记的轮廓几何24.6 获取标注的掩膜几何25 …...

【操作系统原理08】文件管理

文章目录 零.大纲一.文件管理0.大纲1.文件管理1.1 **文件属性**1.2 文件内部数据组织1.3 文件之间的组织1.4操作系统提供功能1.5 文件在外存存放 二.文件的逻辑结构0.大纲1.无结构文件2.有结构文件 三.文件目录0.大纲1.文件控制块2.目录结构3.索引节点(FCB改进) 四.文件共享0.大…...

图论学习笔记 5 - 最小树形图

我们不废话&#xff0c;直接进入正题&#xff1a;最小树形图&#xff0c;一个名字看起来很高级的东西。 声明&#xff1a;为了便于理解&#xff0c;可能图片数量会有亿点点多。图片尺寸可能有的较大。 概念 最小树形图的英文是 Directed Minimum Spanning Tree。 相信懂英文…...

VueUse:组合式API实用函数全集

VueUse 完全学习指南&#xff1a;组合式API实用函数集合 &#x1f3af; 什么是 VueUse&#xff1f; VueUse 是基于 组合式API&#xff08;Composition API&#xff09; 的实用函数集合&#xff0c;为Vue 3开发者提供了丰富的可复用逻辑功能。它通过提供大量预构建的组合函数&…...

《自动驾驶轨迹规划实战:Lattice Planner实现避障路径生成(附可运行Python代码)》—— 零基础实现基于离散优化的避障路径规划

《自动驾驶轨迹规划实战&#xff1a;Lattice Planner实现避障路径生成&#xff08;附可运行Python代码&#xff09;》 —— 零基础实现基于离散优化的避障路径规划 一、为什么Lattice Planner成为自动驾驶的核心算法&#xff1f; 在自动驾驶的路径规划领域&#xff0c;Lattice…...

嵌入式笔试题+面试题

一、嵌入式笔试题 1) int a; 2) int *a; 3) int **a; 4) int a[10]; 5) int *a[10]; 6) int (*a)[10]; 7) int (*a)(int); 8) int (*a[10])(int); (1) 一个整型数 (2) 一个指向整型数的指针 (3) 一个指向指针的的指针&#xff0c;它指向的指针是指向一个整型数 (4) 一个有10个…...

【Go语言生态】

在Go语言生态中&#xff0c;以下工具和方法可以实现类似Laravel的dump()或Symfony的VarDumper的结构体美化打印和调试功能&#xff1a; 使用spew库 spew是Go社区广泛使用的结构化输出库&#xff0c;提供深度嵌套结构的可读性展示&#xff1a; import "github.com/davec…...

PyTorch——卷积操作(2)

二维矩阵 [[ ]] 这里面conv2d(N,C,H,W)里面的四个是 N就是batch size也就是输入图片的数量&#xff0c;C就是通道数这只是一个二维张量所以通道为1&#xff0c;H就是高&#xff0c;W就是宽&#xff0c;所以是1 1 5 5 卷积核 reshape 第一个参数是batch size样本数量 第二个参数…...

【JavaWeb】SpringBoot原理

1 配置优先级 在前面&#xff0c;已经学习了SpringBoot项目当中支持的三类配置文件&#xff1a; application.properties application.yml application.yaml 在SpringBoot项目当中&#xff0c;我们要想配置一个属性&#xff0c;通过这三种方式当中的任意一种来配置都可以&a…...

BSRR对比BRR对比ODR

✅ 三种操作方式的本质区别 寄存器功能原子操作特点BSRR同时支持置位(1)和复位(0)✔️ 是单指令完成任意位操作&#xff0c;无竞争风险ODR直接读写输出状态❌ 否需"读-改-写"&#xff0c;多线程/中断中需关中断保护BRR只能复位(0)✔️ 是仅清零功能&#xff0c;无置…...

ubuntu22.04安装taskfile

sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -dsudo mv ./bin/task /usr/local/bin/测试 task --version...

记录被mybatis一级缓存坑的问题

背景 我之前有个方法需要多次调用数据库拿数据&#xff0c;由于每次查询数据比较少&#xff0c;所以我前期都是直接查数据库拿的&#xff0c;准备后面再改缓存 // 查询代码 假设在A方法中 List<LeftOrderType> leftOrderTypes orderTypeMapper.selectList(wrapper); …...

遥感影像建筑物变化检测

文章目录 效果1、环境安装2、项目下载3、数据集下载4、模型训练5、模型推理6、推理结果7、批量推理效果 1、环境安装 参考文章 搭建Pytorch的GPU环境超详细 win10安装3DGS环境(GPU)超详细 测试GPU环境可用 2、项目下载 https://gitcode.com/gh_mirrors/ch/change_detectio…...

【数据库】《DBA实战手记》- 读书笔记

《DBA实战手记》基本介绍 作者&#xff1a;薛晓刚 等出版时间&#xff1a;2024年6月出版社&#xff1a;机械工业出版社ISBN&#xff1a;9787111757665 本书是一本指导DBA进行数据库开发和运维的实用手册&#xff0c;本书共9章&#xff0c;包括漫谈数据库、如何提升数据库性能…...

多模态大语言模型arxiv论文略读(103)

Are Bigger Encoders Always Better in Vision Large Models? ➡️ 论文标题&#xff1a;Are Bigger Encoders Always Better in Vision Large Models? ➡️ 论文作者&#xff1a;Bozhou Li, Hao Liang, Zimo Meng, Wentao Zhang ➡️ 研究机构: 北京大学 ➡️ 问题背景&…...

汇编语言基础: 搭建实验环境

环境配置 1.Visual Studio 创建空项目 创建成功 2.平台框架改为为WIN32 右键点击项目 点击属性 点击配置管理器 平台改为Win32(本文使用32位的汇编) 3.生成采用MASM 在项目属性里点击"生成依赖项"的"生成自定义" 勾选 masm 4.创建第一个汇编程序 右…...

SIFT 算法原理详解

SIFT 算法原理详解 SIFT&#xff08;尺度不变特征变换&#xff0c;Scale-Invariant Feature Transform&#xff09;是一种经典的局部特征检测和描述算法&#xff0c;它能够在不同的尺度、旋转和光照变化下稳定地检测图像特征。SIFT 主要包括以下几个步骤&#xff1a;尺度空间极…...

基于springboot的益智游戏系统的设计与实现

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;没有什么华丽的语言&#xff0…...

短剧系统开发文案:打造沉浸式互动娱乐新体验

一、项目背景 随着短视频与碎片化娱乐的兴起&#xff0c;短剧市场呈现爆发式增长。用户对剧情紧凑、节奏明快、互动性强的内容需求激增&#xff0c;传统影视平台已难以满足个性化与参与感需求。「XX短剧系统」应运而生&#xff0c;致力于打造集内容创作、分发、互动于一体的短…...

第十二节:第四部分:集合框架:List系列集合:LinkedList集合的底层原理、特有方法、栈、队列

LinkedList集合的底层原理 LinkedList集合的应用场景之一 代码&#xff1a;掌握LinkedList集合的使用 package com.itheima.day19_Collection_List;import java.util.LinkedList; import java.util.List;//掌握LinkedList集合的使用。 public class ListTest3 {public static …...

多模态大语言模型arxiv论文略读(104)

Talk Less, Interact Better: Evaluating In-context Conversational Adaptation in Multimodal LLMs ➡️ 论文标题&#xff1a;Talk Less, Interact Better: Evaluating In-context Conversational Adaptation in Multimodal LLMs ➡️ 论文作者&#xff1a;Yilun Hua, Yoav…...

【C++高级主题】多重继承下的类作用域

目录 一、类作用域与名字查找规则&#xff1a;理解二义性的根源 1.1 类作用域的基本概念 1.2 单继承的名字查找流程 1.3 多重继承的名字查找特殊性 1.4 关键规则&#xff1a;“最近” 作用域优先&#xff0c;但多重继承无 “最近” 二、多重继承二义性的典型类型与代码示…...