diff --git a/chrome-magnifier/README.md b/chrome-magnifier/README.md new file mode 100644 index 000000000..f15df7bb2 --- /dev/null +++ b/chrome-magnifier/README.md @@ -0,0 +1,193 @@ +# 网页放大镜 Chrome 扩展 + +一个简单实用的 Chrome 浏览器扩展,提供网页内容放大镜功能。鼠标移动即可实时放大查看网页内容,帮助你更清晰地查看网页细节。 + +## 功能特点 + +- 🔍 **圆形放大镜**: 跟随鼠标移动的圆形放大区域 +- 🎯 **实时放大**: 鼠标移动即可实时查看放大内容 +- ⚙️ **可调节设置**: + - 放大倍数: 1.5x - 5x + - 放大镜大小: 100px - 400px +- ⌨️ **快捷键支持**: 按 `Alt + M` 快速切换放大镜 +- 🎨 **美观界面**: 现代化的渐变色设计 +- 💡 **轻量级**: 不影响网页性能 + +## 安装方法 + +### 方法一:开发者模式安装(推荐) + +1. 打开 Chrome 浏览器 +2. 在地址栏输入 `chrome://extensions/` 并回车 +3. 打开右上角的「开发者模式」开关 +4. 点击「加载已解压的扩展程序」按钮 +5. 选择本项目的 `chrome-magnifier` 文件夹 +6. 扩展安装完成!你会在浏览器工具栏看到放大镜图标 + +### 方法二:打包安装 + +1. 在 Chrome 扩展管理页面(`chrome://extensions/`) +2. 开启「开发者模式」 +3. 点击「打包扩展程序」 +4. 选择 `chrome-magnifier` 文件夹 +5. 生成的 `.crx` 文件可以分享给其他人安装 + +## 使用方法 + +### 基本使用 + +1. **启动放大镜**: + - 点击浏览器工具栏的放大镜图标 + - 在弹出窗口中点击「启动放大镜」按钮 + - 或者使用快捷键 `Alt + M` + +2. **使用放大镜**: + - 启动后,移动鼠标即可看到圆形放大镜跟随 + - 放大镜会实时显示鼠标位置的放大内容 + +3. **关闭放大镜**: + - 再次点击「关闭放大镜」按钮 + - 或使用快捷键 `Alt + M` + +### 自定义设置 + +在扩展弹出窗口中,你可以调整: + +- **放大倍数**: 拖动滑块调整 1.5x 到 5x 的放大倍数 +- **放大镜大小**: 调整放大镜的直径(100px - 400px) + +所有设置会实时生效,无需重启浏览器。 + +## 文件结构 + +``` +chrome-magnifier/ +├── manifest.json # 扩展配置文件 +├── popup.html # 扩展弹出界面 +├── README.md # 使用说明文档 +├── generate_icons.py # 图标生成脚本 +├── icons/ # 图标文件夹 +│ ├── icon16.png # 16x16 图标 +│ ├── icon48.png # 48x48 图标 +│ └── icon128.png # 128x128 图标 +├── js/ # JavaScript 文件 +│ ├── magnifier.js # 放大镜核心功能 +│ └── popup.js # 弹出窗口控制脚本 +└── css/ # 样式文件 + └── magnifier.css # 放大镜样式 +``` + +## 技术实现 + +- **Manifest V3**: 使用最新的 Chrome 扩展 API +- **Content Script**: 注入到所有网页,实现放大镜功能 +- **Canvas API**: 使用 HTML5 Canvas 绘制放大内容 +- **消息传递**: 使用 Chrome 消息 API 实现弹出窗口与内容脚本的通信 + +## 快捷键 + +- `Alt + M`: 切换放大镜开关 + +## 兼容性 + +- Chrome 88+ +- Edge 88+(基于 Chromium) +- 其他基于 Chromium 的浏览器 + +## 开发与自定义 + +### 修改图标 + +如果你想自定义图标,可以: + +1. 替换 `icons/` 文件夹中的图标文件 +2. 或者修改 `generate_icons.py` 脚本并运行: + ```bash + python3 generate_icons.py + ``` + +### 修改样式 + +编辑以下文件来自定义外观: + +- `css/magnifier.css` - 放大镜样式 +- `popup.html` - 弹出窗口的样式(内联 CSS) + +### 修改功能 + +编辑以下文件来修改功能: + +- `js/magnifier.js` - 放大镜核心逻辑 +- `js/popup.js` - 弹出窗口交互逻辑 + +## 常见问题 + +### Q: 显示「请刷新页面后重试」或「此页面不支持扩展」? +A: 这种情况通常有以下几种原因: + +1. **需要刷新页面**:如果你是在安装扩展后第一次使用,需要刷新网页才能加载扩展脚本 + - 按 `F5` 或 `Ctrl/Cmd + R` 刷新页面 + - 刷新后再点击扩展图标启动放大镜 + +2. **特殊页面不支持**:扩展无法在以下类型的页面运行: + - Chrome 内部页面(`chrome://`) + - 扩展管理页面(`chrome://extensions/`) + - Chrome 网上应用店 + - 新标签页(某些版本) + - 解决方案:切换到普通网页(如 `google.com`、`github.com` 等) + +3. **扩展需要重新加载**:如果刚修改过扩展文件 + - 前往 `chrome://extensions/` + - 找到「网页放大镜」扩展 + - 点击刷新图标 🔄 + +### Q: 放大镜显示的内容不清晰? +A: 可以尝试调整放大倍数,较小的倍数(2x-3x)通常效果更好。 + +### Q: 放大镜没有显示? +A: 请检查: +1. 是否已经点击「启动放大镜」按钮 +2. 状态是否显示「已激活」 +3. 尝试刷新页面后重新启动 +4. 检查浏览器控制台是否有错误信息 + +### Q: 如何在特定网站禁用? +A: 在 Chrome 扩展管理页面,点击扩展详情,在「网站访问权限」中设置。 + +### Q: 快捷键不起作用? +A: 可能与其他扩展或系统快捷键冲突,可以尝试关闭其他扩展测试。 + +## 版本历史 + +### v1.1.0 (2025-11-05) +- ✨ **重大更新**:实现真正的网页内容放大功能 +- 放大镜内现在显示实际的网页内容(文字、图片、CSS样式等) +- 使用DOM克隆和CSS transform技术实现高效放大 +- 添加MutationObserver监听页面变化,自动更新放大内容 +- 性能优化:使用requestAnimationFrame减少重绘 +- 改进圆形遮罩效果和阴影 + +### v1.0.1 (2025-11-05) +- 修复消息通信错误("Could not establish connection" 错误) +- 添加页面类型检测,提示不支持的页面 +- 改进错误处理和用户提示 +- 添加自动重试机制 + +### v1.0.0 (2025-11-05) +- 首次发布 +- 基本放大镜功能 +- 可调节放大倍数和大小 +- 快捷键支持 +- 美观的弹出界面 + +## 许可证 + +本项目为开源项目,仅供学习和个人使用。 + +## 反馈与贡献 + +如有问题或建议,欢迎提出 Issue 或 Pull Request。 + +--- + +**享受使用网页放大镜!** 🔍✨ diff --git a/chrome-magnifier/TESTING.md b/chrome-magnifier/TESTING.md new file mode 100644 index 000000000..fc17b5c73 --- /dev/null +++ b/chrome-magnifier/TESTING.md @@ -0,0 +1,167 @@ +# 测试说明 + +## 如何正确测试网页放大镜扩展 + +### 第一步:加载扩展 + +1. 打开 Chrome 浏览器 +2. 在地址栏输入 `chrome://extensions/` 并回车 +3. 打开右上角的「开发者模式」开关 +4. 点击「加载已解压的扩展程序」 +5. 选择 `chrome-magnifier` 文件夹 +6. 确认扩展已加载,图标显示在工具栏 + +### 第二步:打开测试页面 + +**重要**:不要在 `chrome://` 页面测试! + +推荐测试页面: +- https://www.google.com +- https://www.github.com +- https://www.wikipedia.org +- 任何普通网页 + +**不支持的页面**: +- ❌ `chrome://extensions/` +- ❌ `chrome://settings/` +- ❌ `chrome://newtab/` +- ❌ Chrome 网上应用店 + +### 第三步:首次使用 + +1. 打开一个测试网页(例如 google.com) +2. **刷新页面**(按 F5 或 Ctrl/Cmd + R) + - 这一步很重要!首次安装扩展后必须刷新页面 +3. 点击工具栏的放大镜图标 +4. 应该看到弹出窗口 + +### 第四步:测试放大镜 + +#### 方法一:使用弹出窗口 + +1. 在弹出窗口中点击「启动放大镜」按钮 +2. 状态应该变为「已激活」 +3. 移动鼠标,应该看到圆形放大镜跟随鼠标移动 +4. 调整放大倍数和大小滑块测试 + +#### 方法二:使用快捷键 + +1. 在网页上按 `Alt + M` +2. 放大镜应该立即启动 +3. 再按一次 `Alt + M` 关闭 + +### 预期行为 + +✅ **正常情况**: +- 弹出窗口显示「状态:未激活」 +- 点击启动后显示「状态:已激活」 +- 圆形放大镜跟随鼠标移动 +- 可以调整设置 + +✅ **需要刷新的情况**: +- 弹出窗口显示「等待页面加载...」 +- 然后显示「请刷新页面后重试」 +- 按 F5 刷新页面后即可正常使用 + +✅ **不支持的页面**: +- 弹出窗口显示「此页面不支持扩展」 +- 切换到普通网页即可 + +### 常见测试问题 + +#### 问题 1:点击启动没反应 + +**解决方案**: +1. 按 F5 刷新当前页面 +2. 再次点击扩展图标 +3. 点击「启动放大镜」 + +#### 问题 2:一直显示「请刷新页面后重试」 + +**解决方案**: +1. 确认不是在 `chrome://` 页面 +2. 确实刷新了页面(F5) +3. 尝试重新加载扩展: + - 前往 `chrome://extensions/` + - 找到「网页放大镜」 + - 点击刷新图标 🔄 + - 回到测试页面并刷新 + +#### 问题 3:扩展图标是灰色的 + +这是正常的,点击图标就会显示弹出窗口。 + +### 测试检查清单 + +- [ ] 扩展已加载到 Chrome +- [ ] 在普通网页(不是 chrome:// 页面)上测试 +- [ ] 已刷新页面 +- [ ] 弹出窗口显示正确 +- [ ] 可以启动放大镜 +- [ ] 放大镜跟随鼠标移动 +- [ ] 可以调整放大倍数 +- [ ] 可以调整放大镜大小 +- [ ] 快捷键 Alt+M 工作正常 +- [ ] 可以正常关闭放大镜 + +### 调试技巧 + +如果遇到问题,可以打开控制台查看错误信息: + +1. **查看页面控制台**: + - 在网页上按 F12 + - 切换到 Console 标签 + - 查看是否有错误信息 + +2. **查看扩展控制台**(popup): + - 在 `chrome://extensions/` 页面 + - 找到「网页放大镜」 + - 点击「检查视图」下的「弹出内容」 + - 查看控制台错误 + +3. **查看背景页控制台**: + - 同样在扩展详情页 + - 如果有「背景页」选项,点击查看 + +### 成功标志 + +如果你看到: +- ✅ 控制台输出:「网页放大镜已加载!按 Alt+M 或点击扩展图标来切换放大镜。」 +- ✅ 弹出窗口状态从「未激活」变为「已激活」 +- ✅ 圆形放大镜跟随鼠标移动 +- ✅ **放大镜内显示网页的真实内容(文字、图片等)** + +恭喜!扩展工作正常! + +### 验证真实内容放大 + +v1.1.0 版本的主要改进是显示真实的网页内容。测试方法: + +1. 在任何网页上启动放大镜 +2. 移动鼠标到有文字的地方 +3. 应该能在放大镜圆圈内看到放大的文字 +4. 移动到图片上,应该能看到放大的图片 +5. 移动到按钮、链接等元素上,应该能看到放大的元素 + +**注意**: +- 第一次启动可能需要1-2秒加载页面内容 +- 非常复杂的页面可能加载稍慢 +- 放大镜内的内容是页面的克隆,所以不可交互(这是正常的) + +### 修改代码后 + +如果你修改了扩展代码: + +1. 前往 `chrome://extensions/` +2. 找到「网页放大镜」扩展 +3. 点击刷新图标 🔄(而不是重新加载) +4. 刷新测试页面(F5) +5. 重新测试 + +--- + +**如果按照以上步骤操作后仍有问题,请提供:** +1. 使用的浏览器版本 +2. 测试的具体网址 +3. 控制台的错误信息 +4. 问题的详细描述 diff --git a/chrome-magnifier/css/magnifier.css b/chrome-magnifier/css/magnifier.css new file mode 100644 index 000000000..0fe27c290 --- /dev/null +++ b/chrome-magnifier/css/magnifier.css @@ -0,0 +1,25 @@ +/* 网页放大镜样式 */ + +#page-magnifier { + /* 所有样式都在 JS 中内联设置,以确保优先级 */ + /* 这个文件用于任何额外的全局样式 */ +} + +/* 确保放大镜在所有元素之上 */ +#page-magnifier { + position: fixed !important; + z-index: 2147483647 !important; + pointer-events: none !important; +} + +/* 平滑过渡效果 */ +#page-magnifier canvas { + transition: none; + image-rendering: -webkit-optimize-contrast; + image-rendering: crisp-edges; +} + +/* 防止页面元素干扰放大镜 */ +body.magnifier-active { + cursor: crosshair; +} diff --git a/chrome-magnifier/generate_icons.py b/chrome-magnifier/generate_icons.py new file mode 100644 index 000000000..0e8d4f380 --- /dev/null +++ b/chrome-magnifier/generate_icons.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +""" +生成 Chrome 扩展图标 +使用 PIL (Pillow) 库创建放大镜图标 +""" + +try: + from PIL import Image, ImageDraw + import os + + def create_magnifier_icon(size): + """创建放大镜图标""" + # 创建带透明背景的图像 + img = Image.new('RGBA', (size, size), (0, 0, 0, 0)) + draw = ImageDraw.Draw(img) + + # 计算尺寸 + padding = size // 8 + lens_size = size - 2 * padding + + # 放大镜圆形部分 + circle_left = padding + circle_top = padding + circle_right = padding + lens_size * 0.6 + circle_bottom = padding + lens_size * 0.6 + + # 绘制外圆(边框) + draw.ellipse( + [circle_left, circle_top, circle_right, circle_bottom], + outline=(102, 126, 234, 255), + width=max(2, size // 32), + fill=(255, 255, 255, 200) + ) + + # 绘制内圆 + inner_padding = size // 16 + draw.ellipse( + [circle_left + inner_padding, circle_top + inner_padding, + circle_right - inner_padding, circle_bottom - inner_padding], + outline=(102, 126, 234, 180), + width=max(1, size // 48) + ) + + # 绘制手柄 + handle_width = max(3, size // 16) + handle_start_x = circle_right - size // 8 + handle_start_y = circle_bottom - size // 8 + handle_end_x = size - padding + handle_end_y = size - padding + + draw.line( + [handle_start_x, handle_start_y, handle_end_x, handle_end_y], + fill=(102, 126, 234, 255), + width=handle_width + ) + + # 绘制手柄末端的圆形装饰 + handle_circle_size = handle_width * 1.5 + draw.ellipse( + [handle_end_x - handle_circle_size // 2, + handle_end_y - handle_circle_size // 2, + handle_end_x + handle_circle_size // 2, + handle_end_y + handle_circle_size // 2], + fill=(118, 75, 162, 255) + ) + + return img + + # 创建 icons 目录 + icons_dir = os.path.join(os.path.dirname(__file__), 'icons') + os.makedirs(icons_dir, exist_ok=True) + + # 生成不同尺寸的图标 + sizes = [16, 48, 128] + for size in sizes: + icon = create_magnifier_icon(size) + icon_path = os.path.join(icons_dir, f'icon{size}.png') + icon.save(icon_path, 'PNG') + print(f'已生成图标: {icon_path}') + + print('\n所有图标生成完成!') + +except ImportError: + print('错误: 需要安装 Pillow 库') + print('请运行: pip install Pillow') + print('\n或者,你可以手动创建以下尺寸的图标:') + print('- icons/icon16.png (16x16)') + print('- icons/icon48.png (48x48)') + print('- icons/icon128.png (128x128)') diff --git a/chrome-magnifier/icons/icon128.png b/chrome-magnifier/icons/icon128.png new file mode 100644 index 000000000..e4d427cbe Binary files /dev/null and b/chrome-magnifier/icons/icon128.png differ diff --git a/chrome-magnifier/icons/icon16.png b/chrome-magnifier/icons/icon16.png new file mode 100644 index 000000000..09483933e Binary files /dev/null and b/chrome-magnifier/icons/icon16.png differ diff --git a/chrome-magnifier/icons/icon48.png b/chrome-magnifier/icons/icon48.png new file mode 100644 index 000000000..ca363dc86 Binary files /dev/null and b/chrome-magnifier/icons/icon48.png differ diff --git a/chrome-magnifier/js/magnifier.js b/chrome-magnifier/js/magnifier.js new file mode 100644 index 000000000..a5d6b57b5 --- /dev/null +++ b/chrome-magnifier/js/magnifier.js @@ -0,0 +1,379 @@ +// 网页放大镜 - Content Script +(function() { + 'use strict'; + + // 放大镜配置 + const config = { + magnifierSize: 200, // 放大镜直径 + zoomLevel: 2, // 放大倍数 + borderWidth: 3, // 边框宽度 + isActive: false // 放大镜是否激活 + }; + + // 放大镜元素 + let magnifier = null; + let magnifierLens = null; + + // 创建放大镜元素 + function createMagnifier() { + // 创建放大镜容器 + magnifier = document.createElement('div'); + magnifier.id = 'page-magnifier'; + magnifier.style.cssText = ` + position: fixed; + width: ${config.magnifierSize}px; + height: ${config.magnifierSize}px; + border-radius: 50%; + border: ${config.borderWidth}px solid #333; + box-shadow: 0 0 15px rgba(0, 0, 0, 0.5), inset 0 0 15px rgba(0, 0, 0, 0.1); + pointer-events: none; + z-index: 2147483647; + display: none; + overflow: hidden; + background: white; + `; + + // 创建放大镜内容容器(用于显示放大的页面内容) + magnifierLens = document.createElement('div'); + magnifierLens.id = 'page-magnifier-lens'; + magnifierLens.style.cssText = ` + position: absolute; + width: 100%; + height: 100%; + transform-origin: 0 0; + `; + + magnifier.appendChild(magnifierLens); + document.body.appendChild(magnifier); + } + + // 移除放大镜 + function removeMagnifier() { + if (magnifier && magnifier.parentNode) { + magnifier.parentNode.removeChild(magnifier); + magnifier = null; + magnifierLens = null; + } + } + + // 更新放大镜位置和内容 + function updateMagnifier(e) { + if (!config.isActive || !magnifier || !magnifierLens) return; + + const mouseX = e.clientX; + const mouseY = e.clientY; + + // 设置放大镜位置(中心跟随鼠标) + const magnifierX = mouseX - config.magnifierSize / 2; + const magnifierY = mouseY - config.magnifierSize / 2; + + magnifier.style.left = magnifierX + 'px'; + magnifier.style.top = magnifierY + 'px'; + magnifier.style.display = 'block'; + + // 计算要放大的源区域大小 + const sourceSize = config.magnifierSize / config.zoomLevel; + + // 计算源区域的左上角位置(鼠标为中心) + const sourceX = mouseX - sourceSize / 2; + const sourceY = mouseY - sourceSize / 2; + + // 使用 transform 来实现放大效果 + // 1. 缩放页面内容 + // 2. 平移使得鼠标位置的内容显示在放大镜中心 + const translateX = -sourceX * config.zoomLevel; + const translateY = -sourceY * config.zoomLevel; + + magnifierLens.style.transform = `scale(${config.zoomLevel}) translate(${translateX / config.zoomLevel}px, ${translateY / config.zoomLevel}px)`; + + // 复制页面内容到放大镜中 + updateLensContent(); + } + + // 更新放大镜镜头内容 + let lastUpdateTime = 0; + const updateInterval = 50; // 限制更新频率为20fps,避免性能问题 + + function updateLensContent() { + const now = Date.now(); + if (now - lastUpdateTime < updateInterval) { + return; + } + lastUpdateTime = now; + + // 清空之前的内容 + if (magnifierLens.children.length === 0) { + // 创建一个克隆的文档内容 + // 我们使用一个技巧:通过CSS来显示页面的镜像 + const clonedContent = document.body.cloneNode(true); + + // 移除克隆内容中的放大镜元素,避免递归 + const magnifierClone = clonedContent.querySelector('#page-magnifier'); + if (magnifierClone) { + magnifierClone.remove(); + } + + // 清空lens并添加克隆内容 + magnifierLens.innerHTML = ''; + magnifierLens.appendChild(clonedContent); + } + } + + // 使用更高效的方法:通过背景层克隆实现 + function createMagnifierEfficient() { + // 创建放大镜容器 + magnifier = document.createElement('div'); + magnifier.id = 'page-magnifier'; + + // 创建用于显示放大内容的iframe(更好的隔离) + const magnifierContent = document.createElement('div'); + magnifierContent.className = 'magnifier-content'; + + magnifier.appendChild(magnifierContent); + document.body.appendChild(magnifier); + + magnifierLens = magnifierContent; + } + + // 使用CSS实现的高效放大镜更新 + function updateMagnifierEfficient(e) { + if (!config.isActive || !magnifier) return; + + const mouseX = e.clientX; + const mouseY = e.clientY; + + // 放大镜位置 + magnifier.style.left = (mouseX - config.magnifierSize / 2) + 'px'; + magnifier.style.top = (mouseY - config.magnifierSize / 2) + 'px'; + magnifier.style.display = 'block'; + + // 计算背景位置 + const zoom = config.zoomLevel; + const bgX = -mouseX * zoom + config.magnifierSize / 2; + const bgY = -mouseY * zoom + config.magnifierSize / 2; + + // 更新放大镜样式 + magnifier.style.cssText = ` + position: fixed; + left: ${mouseX - config.magnifierSize / 2}px; + top: ${mouseY - config.magnifierSize / 2}px; + width: ${config.magnifierSize}px; + height: ${config.magnifierSize}px; + border-radius: 50%; + border: ${config.borderWidth}px solid #333; + box-shadow: 0 0 15px rgba(0, 0, 0, 0.5), inset 0 0 15px rgba(255, 255, 255, 0.1); + pointer-events: none; + z-index: 2147483647; + display: block; + overflow: hidden; + background: white; + background-image: var(--page-screenshot); + background-position: ${bgX}px ${bgY}px; + background-size: ${document.documentElement.scrollWidth * zoom}px ${document.documentElement.scrollHeight * zoom}px; + background-repeat: no-repeat; + `; + } + + // 重新实现:使用绝对定位和缩放 + function createMagnifierV2() { + // 创建放大镜外层容器 + magnifier = document.createElement('div'); + magnifier.id = 'page-magnifier'; + + // 创建内层容器,用于放大内容 + magnifierLens = document.createElement('div'); + magnifierLens.id = 'page-magnifier-lens'; + + magnifier.appendChild(magnifierLens); + + // 添加到body,但不是body的子元素,而是添加到html + // 这样可以避免一些样式继承问题 + if (document.body) { + document.body.appendChild(magnifier); + } else { + document.documentElement.appendChild(magnifier); + } + + // 应用样式 + updateMagnifierStyles(); + } + + // 更新放大镜样式 + function updateMagnifierStyles() { + if (!magnifier) return; + + magnifier.style.cssText = ` + position: fixed; + width: ${config.magnifierSize}px; + height: ${config.magnifierSize}px; + border-radius: 50%; + border: ${config.borderWidth}px solid #333; + box-shadow: 0 0 15px rgba(0, 0, 0, 0.5), inset 0 0 8px rgba(255, 255, 255, 0.2); + pointer-events: none; + z-index: 2147483647; + display: none; + overflow: hidden; + background: white; + `; + + if (magnifierLens) { + magnifierLens.style.cssText = ` + position: absolute; + left: 0; + top: 0; + width: 100vw; + height: 100vh; + transform-origin: 0 0; + pointer-events: none; + `; + } + } + + // 更新放大镜 V2 + function updateMagnifierV2(e) { + if (!config.isActive || !magnifier || !magnifierLens) return; + + const mouseX = e.clientX; + const mouseY = e.clientY; + + // 放大镜位置 + magnifier.style.left = (mouseX - config.magnifierSize / 2) + 'px'; + magnifier.style.top = (mouseY - config.magnifierSize / 2) + 'px'; + magnifier.style.display = 'block'; + + // 计算缩放和平移 + const zoom = config.zoomLevel; + + // 计算需要平移的距离,使得鼠标位置显示在放大镜中心 + const translateX = -mouseX * zoom + config.magnifierSize / 2; + const translateY = -mouseY * zoom + config.magnifierSize / 2; + + // 应用变换 + magnifierLens.style.transform = `scale(${zoom}) translate(${translateX / zoom}px, ${translateY / zoom}px)`; + + // 同步页面内容 + syncPageContent(); + } + + // 同步页面内容到放大镜 + function syncPageContent() { + if (!magnifierLens) return; + + // 只在第一次或内容改变时更新 + if (magnifierLens.children.length === 0 || needsUpdate) { + requestAnimationFrame(() => { + // 克隆整个HTML元素 + const htmlClone = document.documentElement.cloneNode(true); + + // 移除克隆中的放大镜本身 + const magnifierInClone = htmlClone.querySelector('#page-magnifier'); + if (magnifierInClone) { + magnifierInClone.remove(); + } + + // 移除克隆中的script标签,避免执行 + const scripts = htmlClone.querySelectorAll('script'); + scripts.forEach(script => script.remove()); + + // 清空并添加新内容 + magnifierLens.innerHTML = ''; + + // 只克隆body内容 + const bodyClone = htmlClone.querySelector('body'); + if (bodyClone) { + // 移除body clone中的放大镜 + const magInBody = bodyClone.querySelector('#page-magnifier'); + if (magInBody) { + magInBody.remove(); + } + + magnifierLens.appendChild(bodyClone); + } + + needsUpdate = false; + }); + } + } + + let needsUpdate = true; + + // 激活放大镜 + function activateMagnifier() { + if (!magnifier) { + createMagnifierV2(); + } + config.isActive = true; + needsUpdate = true; // 标记需要更新内容 + if (magnifier) { + magnifier.style.display = 'block'; + } + } + + // 停用放大镜 + function deactivateMagnifier() { + config.isActive = false; + if (magnifier) { + magnifier.style.display = 'none'; + } + } + + // 切换放大镜状态 + function toggleMagnifier() { + if (config.isActive) { + deactivateMagnifier(); + } else { + activateMagnifier(); + } + return config.isActive; + } + + // 监听鼠标移动事件 + document.addEventListener('mousemove', updateMagnifierV2); + + // 监听页面变化,标记需要更新 + const observer = new MutationObserver(() => { + if (config.isActive) { + needsUpdate = true; + } + }); + + // 开始观察DOM变化 + if (document.body) { + observer.observe(document.body, { + childList: true, + subtree: true, + attributes: true, + characterData: true + }); + } + + // 监听来自popup的消息 + chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { + if (request.action === 'toggleMagnifier') { + const isActive = toggleMagnifier(); + sendResponse({ isActive: isActive }); + } else if (request.action === 'getMagnifierState') { + sendResponse({ isActive: config.isActive }); + } else if (request.action === 'setZoomLevel') { + config.zoomLevel = request.zoomLevel; + sendResponse({ success: true }); + } else if (request.action === 'setMagnifierSize') { + config.magnifierSize = request.size; + if (magnifier) { + updateMagnifierStyles(); + } + sendResponse({ success: true }); + } + return true; // 保持消息通道开启 + }); + + // 键盘快捷键:按 Alt+M 切换放大镜 + document.addEventListener('keydown', (e) => { + if (e.altKey && e.key === 'm') { + e.preventDefault(); + toggleMagnifier(); + } + }); + + console.log('网页放大镜已加载!按 Alt+M 或点击扩展图标来切换放大镜。'); +})(); diff --git a/chrome-magnifier/js/popup.js b/chrome-magnifier/js/popup.js new file mode 100644 index 000000000..fae6b11d7 --- /dev/null +++ b/chrome-magnifier/js/popup.js @@ -0,0 +1,183 @@ +// Popup 控制脚本 +document.addEventListener('DOMContentLoaded', function() { + const toggleBtn = document.getElementById('toggleBtn'); + const zoomSlider = document.getElementById('zoomSlider'); + const sizeSlider = document.getElementById('sizeSlider'); + const zoomValue = document.getElementById('zoomValue'); + const sizeValue = document.getElementById('sizeValue'); + const statusDiv = document.getElementById('status'); + + let contentScriptReady = false; + + // 更新UI状态 + function updateUI(isActive, message) { + if (message) { + statusDiv.textContent = message; + statusDiv.className = 'status inactive'; + toggleBtn.disabled = true; + return; + } + + toggleBtn.disabled = false; + if (isActive) { + toggleBtn.textContent = '关闭放大镜'; + toggleBtn.classList.add('active'); + statusDiv.textContent = '状态:已激活'; + statusDiv.className = 'status active'; + } else { + toggleBtn.textContent = '启动放大镜'; + toggleBtn.classList.remove('active'); + statusDiv.textContent = '状态:未激活'; + statusDiv.className = 'status inactive'; + } + } + + // 检查页面URL是否支持content script + function isValidPage(url) { + if (!url) return false; + // Chrome 扩展无法在这些页面上运行 + const invalidProtocols = ['chrome://', 'chrome-extension://', 'edge://', 'about:', 'view-source:']; + return !invalidProtocols.some(protocol => url.startsWith(protocol)); + } + + // 向当前标签页发送消息(带错误处理) + function sendMessageToCurrentTab(message, callback) { + chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) { + if (!tabs[0]) { + console.error('没有找到活动标签页'); + if (callback) callback(null); + return; + } + + const tab = tabs[0]; + + // 检查是否是有效的页面 + if (!isValidPage(tab.url)) { + console.warn('当前页面不支持扩展:', tab.url); + updateUI(false, '此页面不支持扩展'); + if (callback) callback(null); + return; + } + + // 发送消息 + chrome.tabs.sendMessage(tab.id, message, function(response) { + // 检查是否有错误 + if (chrome.runtime.lastError) { + console.error('消息发送错误:', chrome.runtime.lastError.message); + + // 如果是连接错误,尝试注入脚本 + if (chrome.runtime.lastError.message.includes('Receiving end does not exist')) { + console.log('Content script 未加载,尝试刷新页面或重新加载扩展'); + updateUI(false, '请刷新页面后重试'); + contentScriptReady = false; + } + + if (callback) callback(null); + return; + } + + // 成功接收到响应 + contentScriptReady = true; + if (callback) callback(response); + }); + }); + } + + // 初始化:获取当前放大镜状态 + function initialize() { + chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) { + if (!tabs[0]) return; + + // 检查页面类型 + if (!isValidPage(tabs[0].url)) { + updateUI(false, '此页面不支持扩展'); + return; + } + + // 尝试获取状态 + sendMessageToCurrentTab({ action: 'getMagnifierState' }, function(response) { + if (response) { + updateUI(response.isActive); + contentScriptReady = true; + } else { + updateUI(false, '等待页面加载...'); + // 延迟重试 + setTimeout(function() { + sendMessageToCurrentTab({ action: 'getMagnifierState' }, function(response) { + if (response) { + updateUI(response.isActive); + contentScriptReady = true; + } else { + updateUI(false, '请刷新页面后重试'); + } + }); + }, 500); + } + }); + }); + } + + // 启动初始化 + initialize(); + + // 切换放大镜按钮 + toggleBtn.addEventListener('click', function() { + if (!contentScriptReady) { + updateUI(false, '请刷新页面后重试'); + return; + } + + sendMessageToCurrentTab({ action: 'toggleMagnifier' }, function(response) { + if (response) { + updateUI(response.isActive); + } + }); + }); + + // 放大倍数滑块 + zoomSlider.addEventListener('input', function() { + const value = parseFloat(this.value); + zoomValue.textContent = value + 'x'; + }); + + zoomSlider.addEventListener('change', function() { + const value = parseFloat(this.value); + if (!contentScriptReady) return; + + sendMessageToCurrentTab({ + action: 'setZoomLevel', + zoomLevel: value + }, function(response) { + if (response && response.success) { + console.log('放大倍数已更新:', value); + } + }); + }); + + // 放大镜大小滑块 + sizeSlider.addEventListener('input', function() { + const value = parseInt(this.value); + sizeValue.textContent = value + 'px'; + }); + + sizeSlider.addEventListener('change', function() { + const value = parseInt(this.value); + if (!contentScriptReady) return; + + sendMessageToCurrentTab({ + action: 'setMagnifierSize', + size: value + }, function(response) { + if (response && response.success) { + console.log('放大镜大小已更新:', value); + } + }); + }); + + // 监听来自content script的状态更新 + chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { + if (request.action === 'magnifierStateChanged') { + updateUI(request.isActive); + } + }); +}); diff --git a/chrome-magnifier/manifest.json b/chrome-magnifier/manifest.json new file mode 100644 index 000000000..e6c14bf46 --- /dev/null +++ b/chrome-magnifier/manifest.json @@ -0,0 +1,30 @@ +{ + "manifest_version": 3, + "name": "网页放大镜", + "version": "1.1.0", + "description": "一个简单实用的网页放大镜工具,鼠标移动即可放大查看网页内容", + "permissions": [ + "activeTab" + ], + "action": { + "default_popup": "popup.html", + "default_icon": { + "16": "icons/icon16.png", + "48": "icons/icon48.png", + "128": "icons/icon128.png" + } + }, + "icons": { + "16": "icons/icon16.png", + "48": "icons/icon48.png", + "128": "icons/icon128.png" + }, + "content_scripts": [ + { + "matches": [""], + "js": ["js/magnifier.js"], + "css": ["css/magnifier.css"], + "run_at": "document_end" + } + ] +} diff --git a/chrome-magnifier/popup.html b/chrome-magnifier/popup.html new file mode 100644 index 000000000..79961b715 --- /dev/null +++ b/chrome-magnifier/popup.html @@ -0,0 +1,220 @@ + + + + + + 网页放大镜 + + + +
+

+ 🔍 + 网页放大镜 +

+ +
+
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ 状态:未激活 +
+ +
+ 快捷键: Alt + M 切换放大镜 +
+
+ + + +