Javascript网页设计案例:通过PDF.js实现一款PDF阅读器,包括预览、页面旋转、页面切换、放大缩小、黑夜模式等功能
前言
目前功能包括:
- 切换到首页。
- 切换到尾页。
- 上一页。
- 下一页。
- 添加标签。
- 标签管理
- 页面旋转
- 页面随意拖动
- 双击后还原位置
其实按照自己的预期来说,有很多功能还没有开发完,配色也没有全都搞完,先发出来吧,后期有需要继续添加功能。
功能预览
1.加载后的主页面
白天模式

黑夜模式

2.功能区

一、关于这款PDF阅读器的功能说明
1. 基本布局与样式
- 具有响应式设计,通过
meta标签设置视口以适应不同设备宽度。 - 支持主题切换(明暗模式),定义了一系列CSS变量来管理颜色和布局相关属性,方便切换不同主题风格。
2. 工具栏功能
- 导航功能:包含首页、上一页、下一页、末页的导航按钮,对应
firstPage()、prevPage()、nextPage()、lastPage()函数。 - 缩放功能:有缩小、放大按钮,分别对应
zoomOut()和zoomIn()函数,还提供了一个范围输入控件#zoomControl用于更精确地控制缩放比例。 - 旋转功能:顺时针旋转和逆时针旋转按钮,对应
rotateClockwise()和rotateCounterClockwise()函数。 - 书签功能:可以输入书签名称,点击添加书签按钮(
addBookmark()函数)添加书签,还有书签管理按钮(toggleBookmarkPanel()函数)用于管理已添加的书签。 - 主题切换:有一个主题切换按钮(
toggleTheme()函数),点击可切换明暗主题。
3. 侧边栏与缩略图
- 侧边栏(
.sidebar)用于显示PDF文档的缩略图,缩略图容器为#thumbnails,每个缩略图项(.thumbnail-item)包含一个canvas元素用于显示缩略图,点击缩略图项可进行相关操作。
4. 主内容区域
- 主内容区域(
.main-content)包含PDF文档的显示区域(#pdf-container),其中有一个canvas元素(#pdf-canvas)用于渲染PDF页面,用户可以通过鼠标抓取操作(cursor: -webkit-grab)进行交互。 - 状态栏(
.status-bar)用于显示一些状态信息,如页码等。
5. 模态窗口
- 书签管理模态窗口(
#bookmarks-modal),默认隐藏,用于管理已添加的书签,包含书签列表(#bookmarks-list)和一些操作按钮(.modal-buttons)。
二、Html代码
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"><title>PDF阅读器</title><style>:root {--primary-color: #2196F3;--hover-color: #1976D2;--background-color: #1a1a1a;--surface-color: #2d2d2d;--toolbar-bg: #333333;--text-color: #ffffff;--error-color: #ff4444;--border-radius: 8px;--gap: 20px;--toolbar-height: 60px;}body {font-family: Arial, sans-serif;margin: 0;padding: 20px;background-color: var(--background-color);color: var(--text-color);box-sizing: border-box;}.pdf-reader-container {display: flex;gap: var(--gap);max-width: 100%;min-width: 800px;margin: 0 auto;background-color: var(--surface-color);border-radius: var(--border-radius);box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);padding: var(--gap) ; /* 0 上下内边距 */margin-top: var(--toolbar-height);box-sizing: border-box;flex-wrap: nowrap;}.toolbar {position: fixed;top: 0;left: 0;right: 0;height: var(--toolbar-height);display: flex;align-items: center;justify-content: space-between;padding: 0 var(--gap);background-color: var(--toolbar-bg);border-radius: 0 0 var(--border-radius) var(--border-radius);box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);z-index: 1000;box-sizing: border-box;}.logo {display: flex;align-items: center;padding: 0 20px;height: 60%;}.logo img {height: 100%;object-fit: contain; transition: transform 0.3s ease; }.toolbar-buttons {display: flex;gap: 10px;}/* 修改按钮样式部分 */button {padding: 8px 12px;font-size: 16px;cursor: pointer;background-color: var(--primary-color);color: var(--text-color);border: none;border-radius: 50%;aspect-ratio: 1/1;transition: all 0.3s ease;display: flex;align-items: center;justify-content: center;width: 40px;height: 40px;position: relative;}button:hover {background-color: var(--hover-color);transform: translateY(-2px);box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);}button::before {font-family: "Font Awesome 5 Free";font-weight: 900;}/* 为每个按钮指定图标 */button:nth-child(1)::before { content: "\f100"; } /* 首页 */button:nth-child(2)::before { content: "\f104"; } /* 上一页 */button:nth-child(3)::before { content: "\f105"; } /* 下一页 */button:nth-child(4)::before { content: "\f101"; } /* 末页 */button:nth-child(5)::before { content: "\f068"; } /* 缩小 */button:nth-child(7)::before { content: "\f067"; } /* 放大 */button:nth-child(8)::before { content: "\f2f9"; } /* 顺时针旋转 */button:nth-child(9)::before { content: "\f2ea"; } /* 逆时针旋转 */button:nth-child(11)::before { content: "\f02e"; } /* 添加书签 */button:nth-child(12)::before { content: "\f02e"; } /* 书签管理 */button span {display: none;}.toolbar-buttons {display: flex;gap: 8px;align-items: center;}#zoomControl {width: 100px;height: 6px;background: var(--surface-color);border-radius: 3px;}#bookmarkLabel {padding: 8px;border-radius: var(--border-radius);border: 1px solid var(--primary-color);background: var(--surface-color);color: var(--text-color);}button::after {content: attr(aria-label);position: absolute;bottom: -30px;left: 50%;transform: translateX(-50%);background: var(--toolbar-bg);color: var(--text-color);padding: 4px 8px;border-radius: 4px;font-size: 12px;white-space: nowrap;opacity: 0;transition: opacity 0.2s;pointer-events: none;}button:hover::after {opacity: 1;}::-webkit-scrollbar {width: 8px;}::-webkit-scrollbar-track {background-color: var(--toolbar-bg);border-radius: 4px;}::-webkit-scrollbar-thumb {background-color: var(--primary-color);border-radius: 4px;}::-webkit-scrollbar-thumb:hover {background-color: var(--hover-color);}/* 侧边栏样式 */.sidebar {flex: 0 0 250px;background-color: var(--toolbar-bg);border-radius: var(--border-radius);padding: 16px;height: calc(100vh - var(--toolbar-height) - var(--gap)*2 - 40px);overflow-y: auto;box-sizing: border-box;}.thumbnail-container {display: flex;flex-direction: column;gap: 10px;}.thumbnail-item {position: relative;cursor: pointer;border-radius: 4px;overflow: hidden;box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);}.thumbnail-item canvas {width: 100%;height: auto;}.thumbnail-item.active {border: 2px solid var(--primary-color);}.main-content {flex: 1;background-color: var(--surface-color);border-radius: var(--border-radius);/* padding: 16px; */height: calc(100vh - var(--toolbar-height) - var(--gap)*2 - 40px);box-sizing: border-box;}#pdf-container {width: 100%;height: 96%;/* height: calc(100vh - var(--toolbar-height) - var(--gap)*2 - 80px - 40px); */border: 1px solid var(--toolbar-bg);border-radius: var(--border-radius);overflow: auto;background-color: #333333;/* padding: 16px; */box-sizing: border-box;overflow-x: hidden;}.status-bar {height: 4%;font-size: 14px;color: var(--text-color);/* padding: 5px; */background-color: var(--toolbar-bg);border-radius: var(--border-radius);margin-top: auto;}.error-message {color: var(--error-color);padding: 10px;margin-top: 10px;background-color: #4d4d4d;border-radius: var(--border-radius);}/* 模态窗口样式 */.modal {display: none;position: fixed;top: 0;left: 0;width: 100%;height: 100%;background-color: rgba(0, 0, 0, 0.5);z-index: 1000;transition: opacity 0.3s ease;}.modal-content {background-color: var(--toolbar-bg);margin: 15% auto;padding: 20px;width: 80%;max-width: 500px;position: relative;border-radius: var(--border-radius);}.close {position: absolute;right: 20px;top: 10px;font-size: 28px;cursor: pointer;color: var(--text-color);}.close:hover {color: var(--primary-color);}#bookmarks-list {list-style-type: none;padding: 0;margin: 0;}#bookmarks-list li {padding: 8px;margin: 4px 0;cursor: pointer;border-radius: 4px;}#bookmarks-list li:hover {background-color: var(--primary-color);color: var(--text-color);}.modal-buttons {margin-top: 20px;}.modal-buttons button {margin: 0 5px;}canvas#pdf-canvas {position: relative;cursor: -webkit-grab;}.theme-toggle {position: relative;display: inline-block;}.light-theme {--background-color: #f9fafb; --surface-color: #ffffff;--toolbar-bg: #e5e7eb; --text-color: #374151;--error-color: #ef4444;--primary-color: #3b82f6;--hover-color: #2563eb; }.light-theme .toolbar {background-color: var(--toolbar-bg);}.light-theme .sidebar {background-color: var(--toolbar-bg);}.light-theme .main-content {background-color: var(--surface-color);}.light-theme button {background-color: var(--primary-color);color: var(--text-color);}.light-theme .status-bar {background-color: var(--toolbar-bg);}</style>
</head>
<body><div class="toolbar"><div class="logo"><img src="logo.png" alt="Logo"></div><div class="toolbar-buttons"><button onclick="firstPage()" aria-label="首页"><span>首页</span></button><button onclick="prevPage()" aria-label="上一页"><span>上一页</span></button><button onclick="nextPage()" aria-label="下一页"><span>下一页</span></button><button onclick="lastPage()" aria-label="末页"><span>末页</span></button><button onclick="zoomOut()" aria-label="缩小"><span>缩小</span></button><input type="range" min="0.5" max="2.0" step="0.1" value="1.0" id="zoomControl"><button onclick="zoomIn()" aria-label="放大"><span>放大</span></button><button onclick="rotateClockwise()" aria-label="顺时针旋转"><span>顺时针旋转</span></button><button onclick="rotateCounterClockwise()" aria-label="逆时针旋转"><span>逆时针旋转</span></button><input type="text" id="bookmarkLabel" placeholder="输入书签名称"><button onclick="addBookmark()" aria-label="添加书签"><span>添加书签</span></button><button onclick="toggleBookmarkPanel()" aria-label="书签管理"><span>书签管理</span></button><button onclick="toggleTheme()" aria-label="切换主题" class="theme-toggle"><i class="fas fa-moon"></i></button></div></div><div class="pdf-reader-container"><div class="sidebar"><div class="thumbnail-container" id="thumbnails"></div></div><div class="main-content"><div id="pdf-container"><canvas id="pdf-canvas"></canvas></div><div class="status-bar" id="status-bar"></div></div></div><div id="bookmarks-modal" class="modal" style="display: none;"><div class="modal-content"><span class="close" onclick="closeBookmarkPanel()">×</span><h3>书签管理</h3><ul id="bookmarks-list"></ul><div class="modal-buttons"><button onclick="addBookmark()">添加书签</button><button onclick="clearBookmarks()">清除书签</button><button onclick="closeBookmarkPanel()">关闭</button></div></div></div><script src="pdf.min.js"></script> <script src="pdf-reader.js"></script>
</body>
</html>
三、Javascript代码
let doc = null;
let currentPage = 1;
let scale = 1.5;
let rotation = 0;
let bookmarks = [];
let isDragging = false;
let lastX = 0;
let lastY = 0;
let offsetX = 0;
let offsetY = 0;let thumbnails = [];const thumbnailsContainer = document.getElementById('thumbnails'); const bookmarksList = document.getElementById('bookmarks-list');
const bookmarksPanel = document.getElementById('bookmarks-panel'); const container = document.getElementById('pdf-container'); // 新增变量用于存储当前视口的偏移量
let viewportOffsetX = 0;
let viewportOffsetY = 0;// 在window.onload 中添加键盘事件监听
window.addEventListener('keydown', function(e) {if (e.key === 'ArrowLeft') {prevPage();} else if (e.key === 'ArrowRight') {nextPage();}
});// 初始化滚动条
const zoomControl = document.getElementById('zoomControl');
zoomControl.value = scale.toString(); // 初始化旋转状态
const savedRotation = localStorage.getItem('pdfRotation');
if (savedRotation !== null) {rotation = parseInt(savedRotation);
}// 加载PDF文件
function loadPDF(fileUrl) {pdfjsLib.getDocument(fileUrl).promise.then(function (loadedDoc) {doc = loadedDoc;const numPages = doc.numPages; loadPage(currentPage);createThumbnails(numPages);// 更新状态栏 document.getElementById('status-bar').textContent =`第 ${currentPage} 页 / 共 ${numPages} 页 | 缩放比例: ${scale * 100}% | 旋转: ${rotation}°`;}).catch(function (error) {console.error('Error loading PDF:', error);showError(`加载PDF时出错:${error.message}`); });
}// 创建缩略图
function createThumbnails(numPages) {thumbnailsContainer.innerHTML = '';for (let i = 1; i <= numPages; i++) {const thumbnailItem = document.createElement('div'); thumbnailItem.className = 'thumbnail-item';thumbnailItem.dataset.page = i;const canvas = document.createElement('canvas'); thumbnailItem.appendChild(canvas); thumbnailsContainer.appendChild(thumbnailItem); loadThumbnail(i, canvas);thumbnailItem.addEventListener('click', function() {currentPage = i;loadPage(currentPage);updateThumbnailsActiveState();});}
}// 更新缩略图活动状态
function updateThumbnailsActiveState() {const items = thumbnailsContainer.querySelectorAll('.thumbnail-item'); items.forEach(item => {item.classList.remove('active'); });const currentItem = thumbnailsContainer.querySelector(`[data-page="${currentPage}"]`); if (currentItem) {currentItem.classList.add('active'); }
}// 加载指定页面
// 修改loadPage函数
function loadPage(pageNum) {if (!doc) {showError('未加载PDF文件,请检查文件路径。');return;}doc.getPage(pageNum).then(function (page) {while (container.firstChild) {container.removeChild(container.firstChild); }const defaultViewport = page.getViewport({ scale: scale });const containerWidth = container.clientWidth; const containerHeight = container.clientHeight; const newScale = Math.min( containerWidth / defaultViewport.width, containerHeight / defaultViewport.height );const finalScale = Math.max(0.5, Math.min(2.0, newScale));const viewport = page.getViewport({ scale: scale,rotation: rotation});const canvas = document.createElement('canvas'); canvas.id = 'pdf-canvas';container.appendChild(canvas); adjustCanvasSize(canvas, viewport);const context = canvas.getContext('2d'); if (!context) {showError('无法获取Canvas上下文,请检查浏览器支持。');return;}page.render({ canvasContext: context,viewport: viewport}).promise.then(() => {console.log('Page rendered successfully');hideError();// 初始化偏移量viewportOffsetX = (container.clientWidth - canvas.width) / 2;viewportOffsetY = 0;updateCanvasPosition();}).catch(error => {console.error('Error rendering page:', error);showError(`渲染页面时出错:${error.message}`); });document.getElementById('status-bar').textContent =`第 ${pageNum} 页 / 共 ${doc.numPages} 页 | 缩放比例: ${Number(scale.toFixed(1)) * 100}% | 旋转: ${rotation}°`;}).catch(error => {console.error('Error getting page:', error);showError(`获取页面时出错:${error.message}`); });}// 调整Canvas尺寸以适应页面内容
function adjustCanvasSize(canvas, viewport) {canvas.width = viewport.width; canvas.height = viewport.height; const containerRect = container.getBoundingClientRect(); const canvasRect = canvas.getBoundingClientRect(); canvas.style.left = `${(containerRect.width - canvasRect.width) / 2}px`;canvas.style.top = `${(containerRect.height - canvasRect.height) / 2}px`;
}// 修改导航函数
function firstPage() {currentPage = 1;loadPage(currentPage);
}function prevPage() {currentPage = Math.max(1, currentPage - 1);loadPage(currentPage);
}function nextPage() {currentPage = Math.min(doc.numPages, currentPage + 1);loadPage(currentPage);
}function lastPage() {currentPage = doc.numPages; loadPage(currentPage);
}// 缩放功能
function zoomIn() {scale = Math.min(2.0, scale + 0.1);zoomControl.value = scale.toString(); updateZoomButtons();loadPage(currentPage);
}function zoomOut() {scale = Math.max(0.5, scale - 0.1);zoomControl.value = scale.toString(); updateZoomButtons();loadPage(currentPage);
}// 错误提示功能
function showError(message) {const errorDiv = document.createElement('div'); errorDiv.className = 'error-message';errorDiv.textContent = message;document.body.appendChild(errorDiv);
}function hideError() {const errorDiv = document.querySelector('.error-message'); if (errorDiv) {errorDiv.remove(); }
}// 旋转功能
function rotateClockwise() {rotation += 90;if (rotation >= 360) rotation = 0;localStorage.setItem('pdfRotation', rotation);loadPage(currentPage);
}function rotateCounterClockwise() {rotation -= 90;if (rotation < 0) rotation += 360;localStorage.setItem('pdfRotation', rotation);loadPage(currentPage);
}// 初始化PDF阅读器
window.onload = function () {const fileUrl = 'https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf'; loadPDF(fileUrl);// 恢复旋转状态 if (savedRotation !== null) {rotation = parseInt(savedRotation);loadPage(currentPage);}// 加载保存的书签 loadSavedBookmarks();// 新增事件监听const PDF = document.getElementById('pdf-container'); console.log(PDF);PDF.addEventListener('mousedown', startDrag);PDF.addEventListener('mousemove', drag);PDF.addEventListener('mouseup', stopDrag);PDF.addEventListener('wheel', zoomWithWheel);PDF.addEventListener('dblclick', resetPosition);const currentTheme = localStorage.getItem('theme') || 'dark';if (currentTheme === 'light') {document.body.classList.add('light-theme'); }};// 新增拖拽功能
function startDrag(e) {if (e.button === 0) { // 左键点击isDragging = true;lastX = e.clientX; lastY = e.clientY; }
}// 双击还原
function resetPosition(e) {canvas = document.getElementById("pdf-canvas");viewportOffsetX = (container.clientWidth - canvas.width) / 2;viewportOffsetY = 0;updateCanvasPosition();
}function drag(e) {if (isDragging) {const deltaX = e.clientX - lastX;const deltaY = e.clientY - lastY;// 更新偏移量viewportOffsetX += deltaX;viewportOffsetY += deltaY;// 限制偏移范围const canvas = document.getElementById('pdf-canvas'); // 更新canvas位置updateCanvasPosition();lastX = e.clientX; lastY = e.clientY; }
}function stopDrag() {isDragging = false;
}// 新增滚轮缩放功能
function zoomWithWheel(e) {e.preventDefault(); const delta = e.deltaY; if (delta > 0) {zoomOut();} else {zoomIn();}
}// 更新canvas位置
function updateCanvasPosition() {const canvas = document.getElementById('pdf-canvas'); if (canvas) {canvas.style.left = `${viewportOffsetX}px`;canvas.style.top = `${viewportOffsetY}px`;}
}/*** 更新缩放按钮的显示 */
function updateZoomButtons() {const buttons = document.querySelectorAll('button'); buttons.forEach(button => {if (button.onclick === zoomIn) {button.textContent = `放大 (${scale.toFixed(1)})`; } else if (button.onclick === zoomOut) {button.textContent = `缩小 (${scale.toFixed(1)})`; }});
}/*** 处理滚动条输入事件 */
zoomControl.addEventListener('input', function (e) {const inputValue = parseFloat(e.target.value); // 反向计算 scale 值 scale = inputValue - 0.1;updateZoomButtons();// 使用节流处理以优化性能 throttledLoadPage(currentPage);
});/*** 节流函数 * @param {function} func 目标函数 * @param {number} wait 延迟时间(毫秒)* @returns {function} 节流后的函数 */
function throttle(func, wait) {let timeout;return function (...args) {if (!timeout) {func.apply(this, args);timeout = setTimeout(() => {timeout = null;}, wait);}};
}// 对 loadPage 进行节流处理
const throttledLoadPage = throttle(loadPage, 100);// 监听窗口调整大小事件
window.addEventListener('resize', function () {if (doc && currentPage) {loadPage(currentPage);}
});// 切换书签面板显示状态
function toggleBookmarkPanel() {const isHidden = bookmarksPanel.style.display === 'none';bookmarksPanel.style.display = isHidden ? 'block' : 'none';if (isHidden) {loadBookmarks();}
}// 关闭书签面板
function closeBookmarkPanel() {bookmarksPanel.style.display = 'none';
}// 添加当前页面为书签
function addBookmark() {const bookmarkLabel = document.getElementById('bookmarkLabel').value.trim(); if (!bookmarkLabel) {showError('请输入书签名称');return;}const bookmark = {page: currentPage,scale: scale,label: bookmarkLabel };bookmarks.push(bookmark); saveBookmarks();loadBookmarks();document.getElementById('bookmarkLabel').value = ''; // 清空输入框
}// 加载并显示所有书签
function loadBookmarks() {const bookmarksList = document.getElementById('bookmarks-list'); bookmarksList.innerHTML = '';bookmarks.forEach((bookmark, index) => {const li = document.createElement('li'); li.innerHTML = `<span>${bookmark.label}</span> <button onclick="jumpToBookmark(${index})">跳转</button><button onclick="deleteBookmark(${index})">删除</button>`;li.style.backgroundColor = '#4CAF50';li.style.color = 'white';li.style.padding = '8px';li.style.margin = '4px 0';li.style.borderRadius = '4px';bookmarksList.appendChild(li); });
}// 删除指定索引的书签
function deleteBookmark(index) {bookmarks.splice(index, 1);saveBookmarks();loadBookmarks();
}// 清除所有书签
function clearBookmarks() {bookmarks = [];saveBookmarks();loadBookmarks();
}// 跳转到指定书签
function jumpToBookmark(index) {const bookmark = bookmarks[index];currentPage = bookmark.page; scale = bookmark.scale; loadPage(currentPage);
}// 保存书签到 localStorage
function saveBookmarks() {localStorage.setItem('pdfBookmarks', JSON.stringify(bookmarks));
}// 加载保存的书签
function loadSavedBookmarks() {const saved = localStorage.getItem('pdfBookmarks'); if (saved) {bookmarks = JSON.parse(saved); loadBookmarks();}
}function toggleBookmarkPanel() {const modal = document.getElementById('bookmarks-modal'); modal.style.display = 'block';loadBookmarks();
}function closeBookmarkPanel() {const modal = document.getElementById('bookmarks-modal'); modal.style.display = 'none';
}// 加载缩略图
function loadThumbnail(pageNum, canvas) {doc.getPage(pageNum).then(function (page) {const viewport = page.getViewport({ scale: 0.5 });canvas.width = viewport.width; canvas.height = viewport.height; const context = canvas.getContext('2d'); if (!context) return;page.render({ canvasContext: context,viewport: viewport}).promise.then(() => {if (pageNum === currentPage) {thumbnailItem.classList.add('active'); }});});
}function toggleTheme() {document.body.classList.toggle('light-theme'); const root = document.documentElement; root.style.setProperty('--background-color', document.body.classList.contains('light-theme') ? '#f5f5f5' : '#1a1a1a');root.style.setProperty('--surface-color', document.body.classList.contains('light-theme') ? '#ffffff' : '#2d2d2d');root.style.setProperty('--toolbar-bg', document.body.classList.contains('light-theme') ? '#f0f0f0' : '#333333');root.style.setProperty('--text-color', document.body.classList.contains('light-theme') ? '#333333' : '#ffffff');root.style.setProperty('--error-color', document.body.classList.contains('light-theme') ? '#cc0000' : '#ff4444');}
相关文章:
Javascript网页设计案例:通过PDF.js实现一款PDF阅读器,包括预览、页面旋转、页面切换、放大缩小、黑夜模式等功能
前言 目前功能包括: 切换到首页。切换到尾页。上一页。下一页。添加标签。标签管理页面旋转页面随意拖动双击后还原位置 其实按照自己的预期来说,有很多功能还没有开发完,配色也没有全都搞完,先发出来吧,后期有需要…...
【IoTDB 线上小课 11】为什么 DeepSeek 要选择开源?
新年新气象,【IoTDB 视频小课】第十一期全新来临! 关于 IoTDB,关于物联网,关于时序数据库,关于开源... 一个问题重点,3-5 分钟,我们讲给你听: 开源“加成”再次展现! 现在…...
【ESP32】ESP-IDF开发 | WiFi开发 | HTTPS服务器 + 搭建例程
1. 简介 1.1 HTTPS HTTPS(HyperText Transfer Protocol over Secure Socket Layer),全称安全套接字层超文本传输协议,一般理解为HTTPSSL/TLS,通过SSL证书来验证服务器的身份,并为浏览器和服务器之间的通信…...
ollama离线环境部署deepseek及对话网站开发
ollama离线环境部署deepseek及局域网对话网站开发 需要在离线环境下面部署deepseek大模型,而且局域网内用户能在浏览器直接对话,主机的操作系统是win10 经不断探索,找到一条能走通的路,大致流程和思路如下: 局域网服…...
【Unity】 HTFramework框架(六十)Assistant助手(在Unity中接入DeepSeek等AI语言大模型)
更新日期:2025年2月14日。 Github源码:[点我获取源码] Gitee源码:[点我获取源码] 索引 Assistant助手安装Ollama使用Assistant(在编辑器中)打开Assistant配置Assistant使用Assistant处理Assistant回复的内容使用推理大…...
Spring AI集成DeepSeek,实现流式输出
前面一篇文章我们实现了《Spring AI集成DeepSeek:三步搞定Java智能应用》,大模型的响应速度是很慢的,为了提升用户体验,我们通常会使用流式输出一点点将结果输出给用户。先看下效果: 在 SpringBoot 中实现流式输出可以…...
LeetCode 1299.将每个元素替换为右侧最大元素:倒序遍历,维护最大值,原地修改
【LetMeFly】1299.将每个元素替换为右侧最大元素:倒序遍历,维护最大值,原地修改 力扣题目链接:https://leetcode.cn/problems/replace-elements-with-greatest-element-on-right-side/ 给你一个数组 arr ,请你将每个…...
搭建一个经典的LeNet5神经网络
第一章:计算机视觉中图像的基础认知 第二章:计算机视觉:卷积神经网络(CNN)基本概念(一) 第三章:计算机视觉:卷积神经网络(CNN)基本概念(二) 第四章:搭建一个经典的LeNet5神经网络 一、LeNet-5背景 LeNet-…...
我用Ai学Android Jetpack Compose之CircularProgressIndicator
答案来自 通义千问 Q: 我想学习CircularProgressIndicator,麻烦你介绍一下 当然可以!CircularProgressIndicator 是 Jetpack Compose 中的一个组件,用于显示一个循环的圆形进度条。它非常适用于需要指示加载状态或进程完成度的场景。接下来…...
DeepSeek-R1:通过强化学习激励大型语言模型的推理能力
摘要 我们介绍了第一代推理模型DeepSeek-R1-Zero和DeepSeek-R1。DeepSeek-R1-Zero是一个通过大规模强化学习(RL)训练而成的模型,无需监督微调(SFT)作为初步步骤,展示了卓越的推理能力。通过RL,DeepSeek-R1-Zero自然涌现出许多强大而有趣的推理行为。然而,它也面临诸如…...
为什么要选择3D机器视觉检测
选择3D机器视觉检测的原因主要包括以下几点: 高精度测量 复杂几何形状:能够精确测量复杂的三维几何形状。 微小细节:可捕捉微小细节,适用于高精度要求的行业。全面数据获取 深度信息:提供深度信息,弥补2D视…...
Unity 编辑器热更C# FastScriptReload
工具源码:https://github.com/handzlikchris/FastScriptReload 介绍 用于运行时修改C#后能快速重新编译C#并生效,避免每次改C#,unity全部代码重新编译,耗时旧且需要重启游戏。 使用 需要手动调整AssetPipeline自动刷新模式&…...
DeepSeek在linux下的安装部署与应用测试
结合上一篇文章,本篇文章主要讲述在Redhat linux环境下如何部署和使用DeepSeek大模型,主要包括ollama的安装配置、大模型的加载和应用测试。关于Open WebUI在docker的安装部署,Open WebUI官网也提供了完整的docker部署说明,大家可…...
VNC远程控制Mac
前言 macOS系统自带有VNC远程桌面,我们可以在控制端上安装配置VNC客户端,以此来实现远程控制macOS。但通常需要在不同网络下进行远程控制,为此,我们可以在macOS被控端上使用cpolar做内网穿透,映射VNC默认端口5…...
Next.js国际化:next-i18next
引言 next-i18next 是专门为 Next.js 项目量身定制的国际化解决方案,它基于强大的 i18next 库,能帮助开发者轻松地为 Next.js 应用添加多语言支持 next-i18next 初相识 项目简介 next-i18next 是一个专为 Next.js 应用程序打造的国际化解决方案&#…...
计算机视觉:卷积神经网络(CNN)基本概念(一)
第一章:计算机视觉中图像的基础认知 第二章:计算机视觉:卷积神经网络(CNN)基本概念(一) 第三章:计算机视觉:卷积神经网络(CNN)基本概念(二) 第四章:搭建一个经典的LeNet5神经网络 一、引言 卷积神经网络&…...
Python的那些事第二十三篇:Express(Node.js)与 Python:一场跨语言的浪漫邂逅
摘要 在当今的编程世界里,Node.js 和 Python 像是两个性格迥异的超级英雄,一个以速度和灵活性著称,另一个则以强大和优雅闻名。本文将探讨如何通过 Express 框架将 Node.js 和 Python 结合起来,打造出一个高效、有趣的 Web 应用。我们将通过一系列幽默风趣的实例和表格,展…...
核货宝外贸订货系统:批发贸易企业出海的强劲东风
在全球贸易一体化的汹涌浪潮中,批发贸易企业正积极探寻海外市场的广阔天地,试图开辟新的增长版图。然而,出海之路绝非坦途,众多难题如暗礁般潜藏在前行的航道上。从复杂繁琐的跨境交易流程、变幻莫测的国际市场需求,到…...
最新智能优化算法: 阿尔法进化(Alpha Evolution,AE)算法求解23个经典函数测试集,MATLAB代码
一、阿尔法进化算法 阿尔法进化(Alpha Evolution,AE)算法是2024年提出的一种新型进化算法,其核心在于通过自适应基向量和随机步长的设计来更新解,从而提高算法的性能。以下是AE算法的主要步骤和特点: 主…...
数据结构与算法面试专题——堆排序
完全二叉树 完全二叉树中如果每棵子树的最大值都在顶部就是大根堆 完全二叉树中如果每棵子树的最小值都在顶部就是小根堆 设计目标:完全二叉树的设计目标是高效地利用存储空间,同时便于进行层次遍历和数组存储。它的结构使得每个节点的子节点都可以通过简…...
MongoDB索引介绍
索引简述 索引是什么 索引在数据库技术体系中占据了非常重要的位置,其主要表现为一种目录式的数据结构,用来实现快速的数据查询。通常在实现上,索引是对数据库表(集合)中的某些字段进行抽取、排列之后,形成的一种非常易于遍历读取…...
【一文读懂】WebRTC协议
WebRTC(Web Real-Time Communication)协议 WebRTC(Web Real-Time Communication)是一种支持浏览器和移动应用程序之间进行 实时音频、视频和数据通信 的协议。它使得开发者能够在浏览器中实现高质量的 P2P(点对点&…...
【弹性计算】容器、裸金属
容器、裸金属 1.容器和云原生1.1 容器服务1.2 弹性容器实例1.3 函数计算 2.裸金属2.1 弹性裸金属服务器2.2 超级计算集群 1.容器和云原生 容器技术 起源于虚拟化技术,Docker 和虚拟机和谐共存,用户也找到了适合两者的应用场景,二者对比如下图…...
【个人开发】deepspeed+Llama-factory 本地数据多卡Lora微调
文章目录 1.背景2.微调方式2.1 关键环境版本信息2.2 步骤2.2.1 下载llama-factory2.2.2 准备数据集2.2.3 微调模式2.2.3.1 zero-3微调2.2.3.2 zero-2微调2.2.3.3 单卡Lora微调 2.3 踩坑经验2.3.1 问题一:ValueError: Undefined dataset xxxx in dataset_info.json.2…...
两步在 Vite 中配置 Tailwindcss
第一步:安装依赖 npm i -D tailwindcss tailwindcss/vite第二步:引入 tailwindcss 更改配置 // src/main.js import tailwindcss/index// vite.config.js import vue from vitejs/plugin-vue import tailwindcss from tailwindcss/viteexport default …...
使用 HTML CSS 和 JAVASCRIPT 的黑洞动画
使用 HTML CSS 和 JAVASCRIPT 的黑洞动画 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Black Ho…...
计算机视觉-尺度不变区域
一、尺度不变性 1.1 尺度不变性 找到一个函数,实现尺度的选择特性。 1.2 高斯偏导模版求边缘 1.3 高斯二阶导 用二阶过零点检测边缘 高斯二阶导有两个参数:方差和窗宽(给定方差可以算出窗宽) 当图像与二阶导高斯滤波核能匹配…...
SNARKs 和 UTXO链的未来
1. 引言 SNARKs 经常被视为“解决”扩容问题的灵丹妙药。虽然 SNARKs 可以提供令人难以置信的好处,但也需要承认其局限性——SNARKs 无法解决区块链目前面临的现有带宽限制。 本文旨在通过对 SNARKs 对比特币能做什么和不能做什么进行(相对)…...
DeepSeek 通过 API 对接第三方客户端 告别“服务器繁忙”
本文首发于只抄博客,欢迎点击原文链接了解更多内容。 前言 上一期分享了如何在本地部署 DeepSeek R1 模型,但通过命令行运行的本地模型,问答的交互也要使用命令行,体验并不是很好。这期分享几个第三方客户端,涵盖了桌…...
性格测评小程序07用户登录
目录 1 创建登录页2 在首页检查登录状态3 搭建登录功能最终效果总结 小程序注册功能开发好了之后,就需要考虑登录的问题。首先要考虑谁作为首页,如果把登录页作为首页,比较简单,每次访问的时候都需要登录。 如果把功能页作为首页&…...
