REC
首页
文章分类
源码资源
技术教程
程序软件
文创娱乐
玄学修炼
关于我们
其他页面
网站统计
友情链接
用户留言
高清壁纸
关于易航
热门文章
Joe再续前缘主题 - 搭建本站同款网站
易航网址导航系统 – 功能强大,轻量易用
JsonDb-PHP轻量级文件数据库系统
Typecho一键调整网站为冬天情景插件
V免签全开源免签约码支付系统(支持:支付宝 微信 QQ)
标签搜索
PHP
Web前端
网站源码
PHP源码
Typecho
Typecho插件
课程资料
Windows程序
Android软件
武术内功
HTML源码
Web
Joe主题
Python
Windows
国漫
网络协议
MySQL
NodeJs
小说
发布
登录
注册
找到
42
篇与
Web前端
相关的结果
2025-02-03
掌握这 8 个技巧,让你的 CSS 代码更简洁优雅
写出简洁优雅的 CSS 代码不仅能提高代码的可维护性,还能减少样式冲突。本文将介绍 8 个实用技巧,帮你提升 CSS 代码质量。 1. 善用 CSS 变量,提升代码复用性 图片 使用 CSS 变量的好处: 集中管理主题色值 一处修改,处处生效 支持运行时动态修改 提高代码可维护性 2. 使用 :is() 和 :where() 简化选择器 /* 之前的写法 */ .card h2, .card h3, .card h4, .card h5, .card h6 { margin-bottom: 16px; } /* 使用 :is() 简化后 */ .card :is(h2, h3, h4, h5, h6) { margin-bottom: 16px; } /* 使用 :where() 降低优先级 */ :where(.card, .panel, .box) p { line-height: 1.5; }这个技巧可以: 减少代码重复 提高代码可读性 灵活控制选择器优先级 3. 巧用 aspect-ratio 控制宽高比 /* 常见的 16:9 视频容器 */ .video-container { width: 100%; aspect-ratio: 16 / 9; background: #000; } /* 保持正方形的头像容器 */ .avatar { width: 100px; aspect-ratio: 1; object-fit: cover; border-radius: 50%; }这个属性特别适合: 响应式图片布局 视频容器 卡片网格布局 保持元素固定比例 4. 使用 clamp() 实现响应式数值 .title { /* 最小 16px,最大 32px,基于视窗宽度动态计算 */ font-size: clamp(16px, 4vw, 32px); } .container { /* 响应式内边距 */ padding: clamp(1rem, 3vw, 3rem); /* 响应式宽度 */ width: clamp(320px, 80vw, 1200px); }clamp() 的优势: 无需媒体查询 平滑过渡 代码更简洁 避免内容溢出 5. 使用 gap 属性简化布局间距 .grid-container { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; } .flex-container { display: flex; flex-wrap: wrap; gap: 16px; /* 可以分别设置行列间距 */ /* gap: 16px 24px; */ }gap 属性的优点: 替代传统的 margin 更容易维护间距 支持 flex 和 grid 布局 避免临边距叠加问题 6. 使用逻辑属性适配不同书写方向 .container { /* 替代 margin-left/right */ margin-inline: auto; /* 替代 padding-top/bottom */ padding-block: 2rem; /* 替代 width */ inline-size: 100%; /* 替代 height */ block-size: auto; }逻辑属性的优势: 更好的国际化支持 适应不同书写模式 代码更具语义化 简化 RTL 适配 7. 使用 :has() 实现父元素选择 /* 当卡片包含图片时应用样式 */ .card:has(img) { padding: 0; } /* 当表单存在错误输入时改变样式 */ .form:has(:invalid) { border-color: red; } /* 调整空列表的样式 */ ul:not(:has(li)) { display: none; }:has() 选择器的应用场景: 基于子元素状态修改父元素 实现复杂的条件样式 减少 JavaScript 的使用 提高样式的动态性 8. 使用 @layer 管理样式优先级 @layer base, components, utilities; @layer base { h1 { font-size: 2rem; margin-bottom: 1rem; } } @layer components { .button { padding: 0.5rem 1rem; border-radius: 4px; } } @layer utilities { .text-center { text-align: center; } }@layer 的优势: 明确的样式优先级 更好的代码组织 避免优先级混乱 便于维护大型项目 欢迎大家留言补充。
技术教程
# Web前端
易航
2天前
0
9
0
2025-01-30
CSS view():JavaScript 滚动动画的终结者
每当我与 UI/UX 设计师开完会,他们要求我实现滚动动画时,我内心都忍不住想大声尖叫。为什么?因为我讨厌实现滚动动画。虽然它看起来很棒,实现起来也“相当简单”,但它有许多需要处理的怪癖,尤其是当涉及多个动态元素时。但当客户要求那些花哨的“滚动时淡入”效果时,你能怎么办?你只能卷起袖子写一些 JavaScript,即使这让你内心崩溃。 JavaScript 时代(黑暗时期) 以下是我曾经写过的代码(如果你还在这样做,我理解你的痛苦): window.addEventListener('scroll', () => { const elements = document.querySelectorAll('.fade-in'); elements.forEach((element) => { const elementTop = element.getBoundingClientRect().top; const windowHeight = window.innerHeight; if (elementTop < windowHeight * 0.8) { element.style.opacity = '1'; element.style.transform = 'translateY(0)'; } }); });我要么改变 CSS 属性,要么为具有动画属性的元素添加类,让它执行一些“魔法”。有趣的是,我还必须实现反向操作。这很丑陋,我讨厌它。可怜的浏览器每次滚动事件都要处理这些计算。有时我甚至能听到它的“哭泣”。在移动设备上,电池指示器会像自由落体一样下降。 在积累了一些 JavaScript 经验后,我使用防抖来减少需要运行的计算次数。当然,这稍微好了一些,但这不可能是最好的解决方案,对吧? 发现 Intersection Observer(一线希望) 然后我发现了 Intersection Observer API。终于,有了更好的东西!我不再需要不断检查滚动位置,而是可以告诉浏览器:“嘿,当这个东西变得可见时告诉我”: const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { entry.target.classList.add('visible'); observer.unobserve(entry.target); // 触发后不再观察! } }); }, { threshold: 0.2 } ); document.querySelectorAll('.animate-on-scroll').forEach((el) => observer.observe(el));结合一些 CSS: .animate-on-scroll { opacity: 0; transform: translateY(20px); transition: all 0.6s ease-out; } .animate-on-scroll.visible { opacity: 1; transform: translateY(0); }这确实好多了!但仍然……缺少了一些东西。动画要么开启,要么关闭——没有基于滚动位置的平滑控制。当客户要求那些超级平滑的视差效果或渐进式显示时,我又回到了编写复杂 JavaScript 或使用网上找到的 CSS 技巧的老路上。 然后一切都变了:view() 函数 然后,像 CSS 之神的礼物一样,我发现了 view()。天哪,看看这个: @keyframes fadeIn { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .fade-in { animation: fadeIn linear; animation-timeline: view(); /* 关键所在 */ animation-range: entry 10% cover 30%; /* 细节控制 */ }就这样!没有 JavaScript,没有事件监听器,没有性能噩梦。只有如丝般顺滑的滚动响应动画。 最棒的部分? 你知道最神奇的是什么吗?性能。那些旧的 JavaScript 滚动处理程序会让我的 MacBook 风扇像要起飞一样疯狂旋转。但 view()?即使在移动设备上,也如丝般顺滑。浏览器处理所有繁重的工作,所有操作都在合成器线程上运行(这是一个花哨的说法,意思是“非常快”)。 根据我们的性能测试: 动画类型CPU 使用率平均 FPS内存占用JavaScript38%4512MBIntersectionObserver22%558MBCSS view()5%601MB而且,它只是……有效。不再需要调试为什么动画在错误的时间触发,或者为什么滚动位置计算偏差了几个像素。当有很多动画元素时,不再有“滚动卡顿”。它只是有效。 深入理解 CSS view() 函数 CSS view() 函数的引入标志着我们处理滚动驱动动画方式的重大转变。让我们深入了解这个强大的功能,并理解为什么它正在彻底改变网页动画。 animation-timeline: view() view() 的核心是基于元素在视口中的可见性创建一个进度时间轴。你可以将其视为一个虚拟时间轴,随着元素进入视口而向前移动,随着元素退出视口而向后移动。 .element { animation-timeline: view(); }你可以自定义视口的测量方式: /* 为视口检测添加边距 */ animation-timeline: view(block 10px); /* 垂直边距 */ animation-timeline: view(inline 50%); /* 水平边距 */ animation-timeline: view(10px 20px 30px 40px); /* 所有边距 */理解 animation-range animation-range 属性定义了动画相对于元素在视口中的位置开始和结束的时间。就像为动画设置检查点。 实际应用场景: 1. 渐进式图片加载 @keyframes progressive-load { 0% { filter: blur(20px); scale: 1.1; } 100% { filter: blur(0); scale: 1; } } .lazy-image { animation: progressive-load linear; animation-timeline: view(); animation-range: entry 10% cover 50%; }2. 分步式文字浮现 @keyframes staggered-text { 0% { opacity: 0; transform: translateX(-50px); } 20% { opacity: 1; transform: translateX(0); } 80% { opacity: 1; transform: translateX(0); } 100% { opacity: 0; transform: translateX(50px); } } .staggered-text { animation: staggered-text linear; animation-timeline: view(); animation-range: entry 0% exit 100%; }3. 动态进度条 @keyframes progress-bar { from { width: 0%; } to { width: 100%; } } .progress-bar { animation: progress-bar linear; animation-timeline: view(); animation-range: entry cross(10% 20%); }高级技巧 1. 多时间轴组合 .card { --rotate-axis: 30deg; animation: rotate linear, scale ease-in-out; animation-timeline: view(), scroll(root block); animation-range: entry 0% cover 50%, entry 10% cover 90%; }2. 嵌套动画控制 .parent-element { animation-timeline: view(); } .child-element { animation-delay: calc(parent-animation-progress * 0.2s); }3. 视差滚动系统 .parallax-layer { animation: translate linear; animation-timeline: view(); } .layer-1 { animation-range: entry 0% cover 100%; } .layer-2 { animation-range: entry 10% cover 90%; } .layer-3 { animation-range: entry 20% cover 80%; }浏览器支持与渐进增强 截至 2024 年 12 月,推荐使用以下兼容方案: @supports (animation-timeline: view()) { /* 现代浏览器样式 */ } @supports not (animation-timeline: view()) { /* 回退方案 */ .fallback { transition: all 0.5s ease; } /* 使用 IntersectionObserver 添加 .active 类 */ }推荐使用 @scroll-timeline polyfill 为旧浏览器提供基本支持。 未来展望 即将推出的 CSS 滚动驱动动画规范将带来更多激动人心的特性: scroll() 时间轴类型 时间轴范围自定义单位(svh, lvw) 动画相位控制(animation-phase) 时间轴事件监听(实验性提案) 这些新功能将使创建如下效果成为可能: 基于滚动速度的动画 分页滚动动画 嵌套滚动容器联动 总结 CSS view() 的出现不仅改变了我们实现滚动动画的方式,更重新定义了网页动画的可能性边界。从简单的淡入效果到复杂的视差系统,现在都可以用声明式的简洁语法实现。虽然浏览器支持仍在推进中,但现代前端开发者应该: 优先使用 CSS view() 实现基础动画 使用 JavaScript 作为高级交互的补充 始终考虑渐进增强策略 定期关注 Scroll-driven Animations 规范 的更新 下次设计师再提出复杂的滚动动画需求时,你可以微笑着打开 CSS 文件——那些需要复杂 JavaScript 才能实现的效果,现在可能只需要几行优雅的 CSS 代码。 图片 如果您觉得内容对您有帮助,欢迎在看、点赞、分享 ⬇️❤️⬇️
技术教程
# Web前端
易航
6天前
0
18
0
2025-01-23
为你的网站添加 CSS 烟花绽放🎆特效
烟花动画的 CSS 实现:随机大小与位置的完美结合 图片 引言 在项目开发中,我们经常需要添加一些动画效果来提升用户体验。最近,我就接手了一个需要实现烟花动画的任务,要求烟花能够随机大小,并在不同位置出现。经过一番探索,我决定采用 CSS 来实现这一效果。 动画选择的艺术 选择合适的动画方式是关键。对于轻量、装饰性的动画,CSS 动画足以胜任;而对于复杂度较高的运营活动或创意小游戏,JS 动画或图形库如 Pixi.js 可能是更好的选择。同时,我们还需要考虑学习成本和工程化问题。例如,lottie-web 虽然功能强大,但文件大小可能不适合仅用于一个动画场景。综合考虑,我决定采用 CSS 来实现烟花动画。 单个烟花的诞生 我采用了序列帧的方式来实现单个烟花。首先,我要求设计师导出一组序列帧图片,然后将这些图片合成为一张图片。利用 CSS 的steps()功能符,我们可以轻松实现逐帧动画。 随机位置的奥秘 为了使烟花出现在不同位置,我添加了一个关键帧,并在不同百分比处设置了不同的transform属性。通过组合烟花动画和随机位置动画,实现了烟花在随机位置绽放的效果。 大小随机的魅力 在随机位置的基础上,我进一步添加了scale属性,使烟花的大小也能随机变化。这样,每个烟花都拥有了独特的形态。 多个烟花的盛宴 为了打造更加绚丽的视觉效果,我添加了多个烟花元素,并给每个元素设置了不同的位置和延时。这样,多个烟花就能在不同时间、不同位置随机绽放。 色彩斑斓的烟花 为了让烟花更加多彩,我利用了 CSS Mask 技术。通过将背景图作为遮罩背景,并设置不同的背景颜色,实现了烟花的颜色变化。进一步地,我定义了一个颜色变化的关键帧,使烟花在绽放过程中呈现出多种颜色。 IE 浏览器的降级处理 考虑到 IE 浏览器不支持 Mask 遮罩,我进行了降级处理。通过使用 IE 不支持的选择器:default,为 IE 浏览器提供了不同的样式,确保烟花动画在 IE 下也能正常显示。 动画与用户体验的平衡 适当的动画可以提升用户体验,但并非所有用户都喜欢动画。为了尊重用户的选择,我增加了媒体查询prefers-reduced-motion,以便在用户系统设置中关闭动画时,禁用不必要的动画。 完整代码 HTML 部分 隐藏内容,请前往内页查看详情 CSS 部分 隐藏内容,请前往内页查看详情 总结 通过本次实践,我深刻体会到了 CSS 动画的强大和灵活。选择合适的动画方式、利用 CSS 序列帧动画、组合多个动画、使用 Mask 改变颜色、区分 IE 和现代浏览器、跟随系统设置关闭动画——这些技巧共同构成了一个完美烟花动画的实现。 CSS 实现烟花动画并不复杂,但要做到完美却需要细心和技巧。希望我的分享能对你有所帮助,如果你觉得不错,欢迎点赞、收藏、转发!
技术教程
# Web前端
易航
1月23日
11
112
1
2025-01-12
我常用的十个 CSS 代码技巧
在 CSS 开发中,一些简单的一行代码往往可以让你的页面变得更加优雅高效。以下是 10 个我喜欢使用的 CSS 一行代码,它们不仅简洁,还能在实际项目中起到很大的作用。 1. 设置宽高比例(Aspect Ratio) 通过 aspect-ratio 属性,可以根据指定的宽度自动调整高度(反之亦然)。 .box { width: 90%; aspect-ratio: 16/9; }适合用在视频播放器或图片容器中,确保它们以正确的比例呈现。 2. 逻辑属性(Logical Properties) 使用 margin-block 和 margin-inline 替代传统的 margin-top、margin-right 等,更加简洁直观。 .box { margin-block: 5px 10px; /* 上边距 5px,下边距 10px */ margin-inline: 20px 30px; /* 左边距 20px,右边距 30px */ }对于 padding 也是一样的: .box { padding-block: 10px 20px; /* 上下内边距 */ padding-inline: 15px 25px; /* 左右内边距 */ }这些属性会自动适配文本方向(如从左到右或从右到左)。 3. 全局盒模型设置 避免因默认 box-sizing 属性引起的布局问题,通过以下一行代码可以让所有元素包含其内边距和边框: *, *::before, *::after { box-sizing: border-box; }这可以大幅减少布局错误,让开发更加省心。 4. 平滑滚动(Smooth Scroll) 为整页启用平滑滚动,提升用户体验: html { scroll-behavior: smooth; }在单页网站或锚点导航中尤为实用。 5. 垂直书写模式(Vertical Writing Mode) 让文字从右向左垂直排列,可用于特殊设计场景或支持垂直书写的语言: .vertical-text { writing-mode: vertical-rl; }6. 文本溢出省略号(Truncating Text with Ellipsis) 对于超出容器的长文本,可以用省略号代替多余部分: .ellipsis { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }适合用在标题、卡片或链接预览中。 7. 居中对齐(Place-items) 使用 place-items 快速实现网格容器的水平和垂直居中对齐: .box { display: grid; place-items: center; }8. 限制文本宽度(Limit Text Width) 通过限制每行文本的最大字符数,提升可读性: p { max-width: 100ch; }“ch” 单位表示一个字符的宽度,非常适合用于段落样式。 9. 占位符样式(Styling Placeholder Text) 给输入框的占位符文本添加样式: ::placeholder { color: #999; font-style: italic; }10. 统一的强调色(Accent Color) 通过 accent-color 为交互元素(如按钮、复选框)设置统一的主题颜色: body { accent-color: green; }可以在整个网站中保持一致的视觉风格,而无需单独为每个元素定义样式。 总结 这些 CSS 一行代码涵盖了布局优化、用户体验提升和样式统一等多个方面,既实用又高效。将它们融入你的日常工作流,可以让项目的开发更加轻松,同时大幅提升代码的质量和可维护性。 试试这些技巧,感受它们带来的改变吧!
技术教程
# Web前端
易航
1月12日
0
43
0
2025-01-10
JavaScript逆向时,常用的11个hook
在逆向分析JavaScript代码时,开发者经常使用一些用于hook(钩子)的技术来监视或修改程序的行为。以下是一些常用的hook技术及其示例代码。 01、dom操作 在JS逆向油猴脚本中,DOM操作是最常用的一种Hook方式。通过修改DOM元素的属性和样式,我们可以实现对网页的控制和修改。 // 修改DOM元素的属性 document.getElementById('elementId').setAttribute('attrName', 'attrValue'); // 修改DOM元素的样式 document.getElementById('elementId').style.property = 'value';02、Cookie操作 Cookie Hook 用于定位 Cookie 中关键参数生成位置,以下代码演示了当 Cookie 中匹配到了 \_\_dfp 关键字, 则插入断点: (function () { 'use strict'; var cookieTemp = ''; Object.defineProperty(document, 'cookie', { set: function (val) { if (val.indexOf('__dfp') != -1) { debugger; } console.log('Hook捕获到cookie设置->', val); cookieTemp = val; return val; }, get: function () { return cookieTemp; }, }); })(); (function () { 'use strict'; var org = document.cookie.__lookupSetter__('cookie'); document.__defineSetter__('cookie', function (cookie) { if (cookie.indexOf('__dfp') != -1) { debugger; } org = cookie; }); document.__defineGetter__('cookie', function () { return org; }); })();03、事件监听操作 事件监听也是JS逆向油猴脚本中常用的一种Hook方式。通过监听网页上的事件,我们可以触发自定义的操作和行为。 // 监听按钮点击事件 document.getElementById('buttonId').addEventListener('click', function() { // 自定义操作和行为 });04、AJAX拦截操作 AJAX拦截也是JS逆向油猴脚本中常用的一种Hook方式。通过拦截网页上的AJAX请求,我们可以实现对数据的控制和修改。 // 拦截AJAX请求 XMLHttpRequest.prototype._send = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.send = function() { // 自定义操作和行为 this._send.apply(this, arguments); };05、函数替换操作 函数替换也是JS逆向油猴脚本中常用的一种Hook方式。通过替换网页上的函数,我们可以实现对函数的控制和修改。 // 替换原有函数 var originalFunction = window.functionName; window.functionName = function() { // 自定义操作和行为 originalFunction.apply(this, arguments); };06、Header操作 Header Hook 用于定位 Header 中关键参数生成位置,以下代码演示了当 Header 中包含 Authorization 关键字时,则插入断点: (function () { var org = window.XMLHttpRequest.prototype.setRequestHeader; window.XMLHttpRequest.prototype.setRequestHeader = function (key, value) { if (key == 'Authorization') { debugger; } return org.apply(this, arguments); }; })()07、URL操作 URL Hook 用于定位请求 URL 中关键参数生成位置,以下代码演示了当请求的 URL 里包含 login 关键字时,则插入断点: (function () { var open = window.XMLHttpRequest.prototype.open; window.XMLHttpRequest.prototype.open = function (method, url, async) { if (url.indexOf("login") != 1) { debugger; } return open.apply(this, arguments); }; })();08、JSON.stringify操作 JSON.stringify() 方法用于将 JavaScript 值转换为 JSON 字符串,在某些站点的加密过程中可能会遇到,以下代码演示了遇到 JSON.stringify() 时,则插入断点: (function() { var stringify = JSON.stringify; JSON.stringify = function(params) { console.log("Hook JSON.stringify ——> ", params); debugger; return stringify(params); } })();09、JSON.parse操作 JSON.parse() 方法用于将一个 JSON 字符串转换为对象,在某些站点的加密过程中可能会遇到,以下代码演示了遇到 JSON.parse() 时,则插入断点: (function() { var parse = JSON.parse; JSON.parse = function(params) { console.log("Hook JSON.parse ——> ", params); debugger; return parse(params); } })();10、eval操作 JavaScript eval() 函数的作用是计算 JavaScript 字符串,并把它作为 脚本代码来执行。如果参数是一个表达式,eval() 函数将执行表达式。如果参数是 Javascript 语句,eval() 将执行 Javascript 语句,经常被用来动态执行 JS。以下代码执行后,之后所有的 eval() 操作都会在控制台打印输出将要执行的 JS 源码: (function() { // 保存原始方法 window.__cr_eval = window.eval; // 重写 eval var myeval = function(src) { console.log(src); console.log("=============== eval end ==============="); debugger; return window.__cr_eval(src); } // 屏蔽 JS 中对原生函数 native 属性的检测 var _myeval = myeval.bind(null); _myeval.toString = window.__cr_eval.toString; Object.defineProperty(window, 'eval', { value: _myeval }); })();11、Function操作 以下代码执行后,所有的函数操作都会在控制台打印输出将要执行的 JS 源码: (function() { // 保存原始方法 window.__cr_fun = window.Function; // 重写 function var myfun = function() { var args = Array.prototype.slice.call(arguments, 0, -1).join(","), src = arguments[arguments.length - 1]; console.log(src); console.log("=============== Function end ==============="); debugger; return window.__cr_fun.apply(this, arguments); } // 屏蔽js中对原生函数native属性的检测 myfun.toString = function() { return window.__cr_fun + "" } Object.defineProperty(window, 'Function', { value: myfun }); })();本文到此结束,感谢您的阅读,祝编程愉快!
技术教程
# Web前端
易航
1月10日
0
31
0
2025-01-10
绝了!图片可以直接转成代码!开发是越来越简单了~~~
平时我们浏览网站的时候,经常会遇到令人眼前一亮的网页设计,如果能够将这些设计元素或者整个页面的布局应用到自己的项目中,那该多好? 今天就就给大家介绍一个能够将截图转为代码的开源项目 screenshot-to-code。 screenshot-to-code 项目通过结合先进的 AI 技术,为开发者和设计师提供了一个从视觉设计到代码实现的高效工具,极大地简化了开发流程。 screenshot-to-code 可以把界面设计截图直接转化为 HTML、CSS 或 JavaScript 代码,这样能帮助开发者快速生成网页前端代码。无论是一个按钮、一个导航栏,还是整个页面布局都能从截图转换成实际的前端代码。 图片 screenshot-to-code支持将截图转换成 HTML、Tailwind CSS、React 和 Vue 等现代技术栈的代码 。 用户还可以输入 URL 来克隆实时网站 。 图片 现在支持 Claude Sonnet 3.5 和 GPT-4o! {iframe src="https://www.lequxiang.com.cn/view.php/5bdd13d974c3bcdf09c0f55de4c58840.mp4" height="40vh"/} {iframe src="https://www.lequxiang.com.cn/view.php/d5ec12c1432e8e9fef6d375a2f452b99.mp4" height="40vh"/} 开源地址:隐藏内容,请前往内页查看详情 官方网站:隐藏内容,请前往内页查看详情 支持的技术栈: HTML + Tailwind HTML + CSS React + Tailwind Vue + Tailwind Bootstrap Ionic + Tailwind SVG 支持的人工智能模型: Claude Sonnet 3.5 GPT-4o DALL-E 3 或 Flux Schnell(使用 Replicate)用于图像生成 此外,该项目还可以将网站的视频/录屏转换成网页,演示如下: {iframe src="https://www.lequxiang.com.cn/view.php/603a2aed15a31b1962e64eee146f299e.mp4" height="50vh"/} 安装使用 该项目使用 React/Vite 作为前端, FastAPI 作为后端。 需要 GPT-4 的 OpenAI API 密钥或 Anthropic 密钥(可选), 推荐两者都使用,以便你可以比较 Claude 和 GPT4o 的结果。 运行后端(使用 Poetry 进行包管理 - 如果你没有它,请安装 pip install poetry): cd backend echo "OPENAI_API_KEY=sk-your-key" > .env echo "ANTHROPIC_API_KEY=your-key" > .env poetry install poetry shell poetry run uvicorn main:app --reload --port 7001OpenAI API 密钥也可以通过前端的对话框设置密钥(加载前端后点击齿轮图标)。 图片 运行前端: cd frontend yarn yarn dev然后打开 http\://localhost:5173 就可以开始使用了。 图片 如果要使用其他端口,请更新 frontend/.env.local 中的 VITE\_WS\_BACKEND\_URL 配置选项。 如果你不想浪费 GPT4-Vision,你可以在模拟模式下运行后端: MOCK=true poetry run uvicorn main:app --reload --port 7001Docker 上安装 如果你已经安装了 Docker,可以使用 docker-compose 命令启动该项目: echo "OPENAI_API_KEY=sk-your-key" > .env docker-compose up -d --build接下来就可以在浏览器中打开 http://localhost:5173 使用了。 图片
源码资源
技术教程
# Web前端
# Python
# TypeScript
易航
1月10日
0
111
3
2025-01-08
getHTML() - 替代 innerHTML 的最佳方法
随着所有主流浏览器现已支持 getHTML() 方法,前端开发者有了一个强大的新工具来操作DOM。本文主要探讨 getHTML()的独特优势,特别是在处理Shadow DOM时的卓越表现。 getHTML()与innerHTML的异同 getHTML()和 innerHTML 的 getter 在基本功能上相似,都返回元素内部DOM树的HTML表示。但getHTML()的真正优势在于它能够包含Shadow DOM的HTML,而innerHTML则完全忽略Shadow DOM。 getHTML()的高级用法 getHTML()接受一个可选的options对象参数,通过适当的选项可以获取完整的HTML,包括Shadow DOM: const container = document.body; const host = createDiv(123); const root = attachShadowDOM(host); container.append(host); console.log(container.getHTML({ shadowRoots: [root] }));图片 这段代码会返回包含声明式Shadow Root的完整HTML: <div> <template shadowrootmode="open"> <p>Paragraph <slot>default</slot></p> </template> 123 </div>如果在浏览器中将返回的 上面的 HTML 作为新页面打开,则会再现原始 DOM 树: 图片 通常,shadow trees和slots是在自定义元素的构造函数中创建的,但为了保持上面和下面示例页面中的代码简单,这里没有创建任何自定义元素。相反,使用了两个辅助函数: // shared.js export function attach(host) { const shadowRoot = host.attachShadow({ mode: 'open' }); shadowRoot.innerHTML = '<p>Paragraph <slot>default</slot></p>'; return shadowRoot; } export function div(n) { const el = document.createElement('div'); if (n) el.innerHTML = n; return el; }div(n)创建一个新的div元素,里面包含数字n,例如<div>123</div>,而attach(host)将HTML为<p>Paragraph <slot>default</slot></p>的shadow树附加到host元素上。为了用常见情况挑战getHTML(),div中的数字123被分配到shadow DOM的slot中。 处理嵌套的Shadow DOM 在上面的页面中,getHTML()被调用时使用了所有两个可能的选项: <script type="module"> import { attach, div } from './shared.js'; const container=document.body; const host=div(123); const root=attach(host); container.append(host); console.log('>innerHTML',container.innerHTML); console.log('>getHTML',container.getHTML()); console.log('>getHTML2',container.getHTML({ serializableShadowRoots: true })); console.log('>getHTML3',container.getHTML({ shadowRoots: [root] })); </script>options对象可以有两个属性:serializableShadowRoots和shadowRoots。 当getHTML()在没有options的情况下被调用时,Shadow DOM会被忽略,就像在innerHTML中一样。 如果serializableShadowRoots为true,HTML将包括具有serializable属性设置为true的shadow roots。这样的roots通常不应该存在,因为serializable是与getHTML()一起引入的,默认情况下它是false。 要获取shadow roots的HTML,需要在shadowRoots属性中提供要序列化的shadow roots。当shadow roots是open的时候,可以很容易地递归检索网页中的所有shadow roots。在网页上下文中无法检索closed shadow roots,但可以在浏览器扩展注入的内容脚本中检索。 提供的shadow roots不一定会被序列化。在下一个示例页面中,创建了两个shadow trees。第二个shadow DOM嵌套在第一个中: <script type="module"> import { attach, div } from './shared.js'; const container=document.body; const host=div(123); const root=attach(host); container.append(host); const host2=div(456); const root2=attach(host2); container.append(host); root.append(host2); console.log('>innerHTML',container.innerHTML); console.log('>getHTML',container.getHTML()); console.log('>getHTML2',container.getHTML({ serializableShadowRoots: true })); console.log('>getHTML3',container.getHTML({ shadowRoots: [root] })); console.log('>getHTML4',container.getHTML({ shadowRoots: [root2] })); console.log('>getHTML5',container.getHTML({ shadowRoots: [root,root2] })); </script>如果第一个shadow DOM不包含在options中,getHTML()不会返回第二个shadow DOM的HTML: 要被序列化,shadow roots需要直接连接到要被序列化的DOM。如果省略了父shadow root,嵌套的shadow root也不会被序列化。 getHTML 局限性 缺少outerHTML等价物:目前还没有获取包含元素自身在内的HTML的方法。 单根元素限制:getHTML()返回的HTML如果没有单一根元素,浏览器可能无法正确解析为声明式Shadow DOM。 封闭的Shadow DOM:在网页上下文中无法获取封闭的Shadow DOM,但可以通过浏览器扩展的内容脚本来实现。 结语 getHTML()为开发者提供了一种强大的方法来处理包含Shadow DOM的复杂DOM结构。虽然它有一些限制,但在处理现代Web组件和复杂UI时,getHTML()的优势是显而易见的。随着Web组件的普及,掌握getHTML()将成为前端开发者的重要技能。 在实际开发中,getHTML()可以用于创建更精确的DOM快照、调试复杂的组件结构,以及在需要保留Shadow DOM结构的情况下序列化页面内容。随着Web标准的不断发展,我们可以期待看到更多类似getHTML()这样的强大API,进一步增强前端开发的能力和灵活性。
技术教程
# Web前端
易航
1月8日
0
24
0
2025-01-08
超高级的CSS印章效果!附源码!!
效果预览 图片 源码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>印章</title> </head> <body> <canvas id="canvas" width="250" height="250"></canvas> </body> <script> function createSeal(id, company, name) { var canvas = document.getElementById(id); var context = canvas.getContext('2d'); // 绘制印章边框 var width = canvas.width / 2; var height = canvas.height / 2; context.lineWidth = 7; context.strokeStyle = "#f00"; context.beginPath(); context.ellipse(width, height, 110, 80, 0, 0, Math.PI * 2); // 修改为椭圆形 context.stroke(); // 绘制印章中间的文字 context.font = '16px Helvetica'; context.textBaseline = 'middle';//设置文本的垂直对齐方式 context.textAlign = 'center'; //设置文本的水平对对齐方式 context.lineWidth = 1; context.fillStyle = '#f00'; context.fillText('全国二次元聚集地', width, height + 5); // 绘制印章名称 context.font = '20px Helvetica'; context.textBaseline = 'middle';//设置文本的垂直对齐方式 context.textAlign = 'center'; //设置文本的水平对对齐方式 context.lineWidth = 1; context.fillStyle = '#f00'; context.fillText(name, width, height + 55); // 绘制印章单位 context.translate(width, height); // 平移到此位置 context.font = '18px Helvetica'; var count = company.length; // 字数 var angle = 2 * Math.PI / 15; // 修改为围绕椭圆的角度 var radiusX = 86; // 椭圆的X半径 var radiusY = 60; // 椭圆的Y半径 var chars = company.split(""); var c; var startAngle = Math.PI / 1; // 设置开始文字的角度 for (var i = 0; i < count; i++) { c = chars[i]; // 需要绘制的字符 var currentAngle = startAngle + angle * i; // 当前字符的角度 var x = radiusX * Math.cos(currentAngle); // X坐标 var y = radiusY * Math.sin(currentAngle); // Y坐标 context.save(); context.translate(x, y); // 平移到字符位置 context.rotate(currentAngle + Math.PI / 2); // 旋转字符 context.fillText(c, 0, 0); // 绘制字符 context.restore(); } } createSeal('canvas', '全国统一发票监制章', '749局'); </script> </html>
技术教程
# Web前端
易航
1月8日
0
76
0
2025-01-04
用 iframe 必定遇到过这六种“坑”之一(以 vue 为示例)
前言 如果你是做web前端,那么不可避免早晚都会用到iframe的。其实博主很久前用过,但最近又要有项目用了,由于年代久远对iframe的注意事项都有点忘记了,然后想着总结一下比较需要注意的几个重点事项,除了便于高效工作还能分享给有需要用到iframe的小伙伴。 iframe基于父窗口大小自适应宽高 「简述:」 这是iframe最常见的需求了,有时候我们用iframe嵌入一个页面时,不想固定宽高想跟随父系统屏幕大小动态变化,从而大大提高适配性。 「实现思路:」 iframe标签绑定:style 来动态设置宽高,监听父窗口宽高变化时动态获取并且绑定到:style,但监听变化需要考虑到初始化和窗口缩放的情况,并且记得移除事件监听器防止内存泄漏。 「完整实现代码如下所示」 <template> <div class="box" @resize="iframeResize"> <iframe :src="iframeSrc" :style="{ width: '100%', height: frameHeight + 'px' }" ref="myRef"></iframe> </div> </template> <script setup> import { onMounted, onUnmounted, ref, watchEffect } from 'vue'; const myRef = ref(null); const iframeSrc = 'https:******.com'; const frameHeight = ref(0); // 调整iframe的高度的方法 function initHeight() { if (myRef.value) { frameHeight.value = window.innerHeight; } } // 窗口大小变化触发 function iframeResize() { initHeight(); } // 移除事件监听器, 防止内存泄漏 onUnmounted(() => { window.removeEventListener('resize', iframeResize); }); // 在组件挂载时先获取一次iframe高度 onMounted(() => { initHeight(); window.addEventListener('resize', iframeResize); }); // 时刻监听变化,防止iframeRef没有赋值 watchEffect(() => { if (myRef.value) { initHeight(); } }); </script> <style scoped> .box { position: relative; width: 100%; height: 100vh; } </style>iframe基于内容动态宽高 「简述:」 例如我们业务需求嵌入的是一个表格而不是一个页面,并且表格高度并不确定时我们不能固定iframe的高度,否则只有一条内容或者没有内容的时候会不好看,这里要根据内容的数量去决定ifram嵌入窗口的高度。 「实现思路:」 思路是子窗口通信告诉父窗口具体高度,然后父窗口再动态设置高度即可。具体实现是子窗口利用 window.postMessage 来发送具体高度,然后父窗口用window.addEventListener('message', 方法)接收内容,从而根据接收到的内容动态调整iframe的高度。 「子窗口(被嵌入页面)代码」 <script setup> window.onload = function () { let height = '想要告诉父窗口的高度' if (window.parent && window.parent.postMessage) { window.parent.postMessage({ height: height }, '*'); } }; </script>「父窗口代码」 <template> <div> <iframe :src="iframeSrc" ref="myRef"></iframe> </div> </template> <script setup> import { onMounted, ref } from 'vue'; const iframeSrc = 'https:******.com'; const myRef = ref(null); onMounted(() => { window.addEventListener('message', iframeMessage); }); const iframeMessage = (event) => { // 验证消息来源,确保安全性 if (event.origin !== 'https:******.com') return; const newHeight = event.data.height; if (newHeight && myRef.value) { myRef.value.style.height = `${newHeight}px`; } }; </script>「注意:」 if (event.origin !== 'https:******.com') return 这行代码记得加上。 iframe嵌入页面免登录处理 这个小弟有单独写过相关详细文章,并且分析了几种情况的处理方式,移步:iframe嵌入页面实现免登录 http无法嵌入https 「简述:」 例如开发环境是HTTP,嵌入的环境是生产环境的HTTPS,这时就会发现这个问题了。其实是现代浏览器的安全机制,会认为是跨域不同源而禁止。因为HTTP协议传输的数据未加密的会有安全风险。 「解决思路:」 网上五花八门的方法,但其实真正靠谱的就两种,(1)将 HTTP 转换成 HTTPS (2)使用代理服务。 方法一:将 HTTP 转换成 HTTPS(推荐) 1、获取 SSL 证书并安装 方法不止一种,这里就细说啦。 2、配置服务器(这里以nginx为例) 下面我把核心部分解释放在注释说明,这是简单版本用于说明HTTP转HTTPS,如果真的上生产其实还有不少其它配置。 server { listen 80; // 监听http默认的80端口 return 301 https://$host$request_uri; // 把所有http永久重定向到https server_name ****.com www.****.com; // 指定域名,这里视真实情况而定 } server { listen 443 ssl; // 监听https默认的443端口。 server_name ****.com www.****.com; // 指定域名,这里视真实情况而定 // 指定 SSL 证书文件路径。 ssl_certificate /etc/lets/live/****.com/fullchain.pem; // 指定 SSL 私钥文件路径 ssl_certificate_key /etc/lets/live/****.com/privkey.pem; location / { index index.html index.htm; root /var/w/html; } }方法二:使用代理服务 用 nodejs 搭建个简单的代理服务器(这里是用 nodejs 举例,真实业务场景可能是后端那边搞) 要先安装对应的依赖,例如 npm init -y npm install express http-proxy然后再配置对应的代理服务器,主要核心是下面四个模块 express:作用是构建 Web 应用。 http:作用是处理 HTTP 请求。 https:作用是处理 HTTPS 请求。 httpProxy:作用是创建代理服务器。 const express = require('express'); const fs = require('fs'); const https = require('https'); const http = require('http'); const httpProxy = require('http-proxy'); const app = express(); const proxy = httpProxy.createProxyServer(); const port = 3000; // 读取对应的SSL证书文件 const options = { key: fs.readFileSync('/etc/lets/live/proxy.****.com/privkey.pem'), cert: fs.readFileSync('/etc/lets/live/proxy.****.com/fullchain.pem') }; // 设置路由信息 app.all('/proxy/*', (req, res) => { const targetUrl = `http://${req.params[0]}`; proxy.web(req, res, { target: targetUrl }, (error) => { res.status(500).send('Proxy request failed'); }); }); // 创建HTTPS服务器 const server = https.createServer(options, app); // 启动代理服务器 server.listen(port, () => { console.log(`HTTPS`); });跨域问题 「简述:」 iframe 页面的跨域问题是因为涉及到浏览器的安全策略,即同源策略。同源策略限制了一个网页脚本不能读写不同源页面的 DOM 与 Cookie之类的信息。即如果 iframe 中的内容与包含它的页面不在同一个源上,那么这两个页面之间会受到跨域限制。 「解决思路:」 1、使用 window.postMessage 实现跨域通信 父页面代码: 主要用window.addEventListener监听消息用postMessage发送消息。 「注意」: @load加载完成后再监听和window.removeEventListener取消监听这两个细节。 <template> <div> <iframe :src="iframeSrc" ref="iframeRef" @load="onIframeLoad" style="width: 100%; height: 400px;"></iframe> <button @click="sendMessage">发送消息</button> </div> </template> <script setup> import { ref, onMounted } from 'vue'; onMounted(() => { window.addEventListener('message', handleMessage); // 在组件卸载时移除事件监听器 return () => { window.removeEventListener('message', handleMessage); }; }); const iframeSrc = 'http://***.com'; const iframeRef = ref(null); // 当 iframe 加载完成后,再设置监听器 const onIframeLoad = () => { window.addEventListener('message', handleMessage); }; const sendMessage = () => { const iframe = iframeRef.value; if (iframe.contentWindow) { iframe.contentWindow.postMessage('Hello!', 'http://***.com'); } }; const handleMessage = (event) => { // 确保来自想要的源才处理消息 if (event.origin !== 'http://***.com') return; console.log(event.data); }; </script>子页面代码:和父页面一样,用window.addEventListener监听消息用postMessage发送消息。 <template> <div> <button @click="sendMessage">发送消息到父页面</button> </div> </template> <script setup> import { ref, onMounted } from 'vue'; onMounted(() => { window.addEventListener('message', handleMessage); // 在组件卸载时移除事件监听器 return () => { window.removeEventListener('message', handleMessage); }; }); const sendMessage = () => { const parentWindow = window.parent; if (parentWindow) { parentWindow.postMessage('Hello!', 'http://****.com'); } }; const handleMessage = (event) => { if (event.origin !== 'http://****.com') return; console.log(event.data); }; </script>2、使用 document.domain document.domain用于解决二级域名之间跨域问题的方法,例如:a.tty.com 和 b.tty.com,它们都属于同一个顶级域名 tty.com,这时就适合用document.domain来让这两个页面能够相互访问。用法相当于简单,就是分别设置两个页面的document.domain。 核心代码在第10与19行。 <template> <div> <iframe :src="iframeSrc" @load="onIframeLoad" ref="iframeRef"></iframe> </div> </template> <script setup> import { ref } from 'vue'; document.domain = 'tty.com'; // 设置顶级域名 const iframeRef = ref(null); const iframeSrc = 'http://b.tty.com'; const onIframeLoad = () => { const iframe = iframeRef.value; if (iframe.contentWindow) { // 设置iframe的 document.domain iframe.contentWindow.document.domain = 'tty.com'; } }; </script>3、使用 CORS 这里主要是后端的配置了,通过调整服务器响应头中的 Access-Control-Allow-Origin 来控制哪些源是可以安全访问资源。 以为nginx为例,*设置为所有。 http { server { listen 80; server_name yourdomain.com; # 替换为你的域名 # 代理 iframe 请求并添加 CORS 头部 location /iframe-proxy/ { # 添加CORS头部 add_header Access-Control-Allow-Origin *; # 其他配置... } } }4、nginx配置代理 算是常见解决方案了,思路是通过 Nginx 反向代理,将请求重定向到想要请求的目标服务器。 核心就是第10行代码,具体可以特意去看看nginx。 http { server { listen 80; server_name yourdomain.com; # 替换为你的域名 # 代理 iframe 请求并添加 CORS 头部 location /iframe-proxy/ { # 将请求代理到目标 proxy_pass http://tty.com/; # 其他配置... } } }iframe嵌入后报拒绝连接请求 不知道你用iframe有没有见过这个页面,这通常是目标页面设置了 X-Frame-Options 响应头来限制内容被嵌入到其他站点的 iframe 中。这个可以找后端看看 X-Frame-Options 。 图片 小结 都是把遇到的场景总结了一下,感觉都是比较常见的情况。 如果大佬们有什么 iframe 的 “坑” 也可以分享一下我同步学习一下,还有那里写的不好也可以指出更正鸭
技术教程
# Web前端
# Vue
易航
1月4日
0
117
0
1
2
...
5
下一页