REC
首页
文章分类
源码资源
技术教程
程序软件
文创娱乐
玄学修炼
关于我们
其他页面
网站统计
友情链接
用户留言
高清壁纸
关于易航
热门文章
Joe再续前缘主题 - 本站同款
易航网址导航系统 – 功能强大,轻量易用
Js自动播放HTML音乐(不受浏览器限制,无需先与浏览器交互,无需对浏览器进行修改)
网易云官方歌曲解析接口
JsonDb-PHP轻量级文件数据库系统
标签搜索
PHP
Web前端
网站源码
PHP源码
Typecho
课程资料
武术内功
HTML源码
Windows程序
Web
Android软件
Typecho插件
Joe主题
国漫
网络协议
MySQL
Python
NodeJs
Windows
小说
发布
登录
注册
找到
40
篇与
Web前端
相关的结果
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天前
6
25
0
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
31
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
18
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
68
2
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
11
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
50
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
109
0
2025-01-04
手把手教你在 Vue 项目中优雅地封装 axios!
1.axios 简介 axios 是一个用来发起网络请求的 js 库,返回的格式是 Promise。 vue 项目中基本都是用 axios 发起网络请求。 2.安装配置 axios 2.1 安装 axios npm i axios -- save2.2 配置 axios 创建 Axios 实例 添加请求拦截器 添加响应拦截器 配置全局的 loading 在 src/util 下面新建 axios.js 文件 import axios from "axios"; import { ElMessage } from 'element-plus' import { ElLoading } from 'element-plus' import { ref } from 'vue' // -------------------------1. 创建axios实例----------------------- const instance = axios.create({ // 接口 baseURL: "/api", // 超时时间 timeout: 3000, }); // -------------------------2.请求拦截----------------------- instance.interceptors.request.use( config => { let token = sessionStorage.getItem('token'); if (token) { config.headers['token'] = token } // 加载loading addLoading(); return config; }, error => { // 请求发生错误,抛出异常 Promise.reject(error); } ); // -------------------------3.响应拦截----------------------- instance.interceptors.response.use( res => { // 取消加载 loading cancelLoading(); return res; }, error => { // 取消加载 loading cancelLoading(); if (error && error.response) { const status = error.response.status switch (status) { case 400: ElMessage.error("请求错误"); break; case 401: ElMessage.error("未授权,请重新登录"); break; case 403: ElMessage.error("登录过期,请重新登录"); break; case 404: ElMessage.error("请求错误,未找到相应的资源"); break; case 408: ElMessage.error("请求超时"); break; case 500: ElMessage.error("服务器错误"); break; case 504: ElMessage.error("网络超时"); break; default: ElMessage.error("请求失败"); } } else { if (JSON.stringify(error).includes("timeout")) { error.code = "TIMEOUT"; error.message = "服务器响应超时,请刷新页面"; } } return Promise.reject(error); }, ); // -------------------------4.配置全局loading----------------------- let loadCount = 0; let loadingInstance = ref(null); // 加载loading const addLoading = () => { loadCount++; if (loadCount === 1) { loadingInstance.value = ElLoading.service({ fullscreen: false, text: "正在请求数据中....", background: "rgba(0, 0, 0, 0)", }); } }; // 取消加载loading const cancelLoading = () => { loadCount--; if (loadCount === 0) { loadingInstance.value.close(); }; }; // -------------------------配置全局loading----------------------- // 5.导出 axios 实例 export default instance;2.3 封装常用的 http 请求 在 /src/util 下面新建 http.js 文件 其实就是先引入 axios 实例,然后将 axios 的几种常用网络请求封装成 Promise 并返回。import instance from "./axios"; const post = (url, data) => { return new Promise((resolve, reject) => { instance .post(url, data) .then((res) => { resolve(res); }) .catch((err) => { reject(err); }); }); }; const get = (url, data) => { return new Promise((resolve, reject) => { instance .get(url, { params: data }) .then((res) => { resolve(res); }) .catch((err) => { reject(err); }); }); }; const put = (url, data) => { return new Promise((resolve, reject) => { instance .put(url, data) .then((res) => { resolve(res); }) .catch((err) => { reject(err); }); }); }; const del = (url, data) => { return new Promise((resolve, reject) => { instance .delete(url, { params: data }) .then((res) => { resolve(res); }) .catch((err) => { reject(err); }); }); }; export default { post, get, put, del, };2.4 开发接口 在 /src/api 文件夹下新建接口文件 import http from "../utils/http"; // 用户登录 const login = (data) => { return http.post("/index/user/login", data); }; export default { login }图片 3.请求案例 // 导入用户api import userApi from "../api/user"; // 登录 const onSubmit = async () => { const res = await userApi.login(form); if (res.data.code == 200) { // 登录逻辑 } else { ElMessage.error(res.data.message); } };图片
技术教程
# Web前端
# Vue
易航
1月4日
0
96
0
2024-12-28
Web前端与众不同的夜间开关交互效果(附源码)
图片 这是一个精美炫酷的日/夜开关切换按钮效果。这个效果展示了如何通过纯CSS和少量JavaScript来创建复杂、富有动感的用户界面元素。它不仅功能性强,还具有很高的视觉吸引力,为用户提供了愉悦的交互体验。 主要特点和核心实现原理如下: 视觉效果: 一个圆形的切换按钮,模拟了日夜交替的场景。 日间模式显示太阳、云朵和一个小飞机。 夜间模式显示月亮、星星和一个宇航员熊。 切换时有平滑的动画过渡效果。 核心实现原理: a. HTML结构: b. CSS技术: c. JavaScript: 根据复选框状态同步更新body的data-dark-mode属性。 使用CSS变量(如 --dark)控制日/夜模式的状态。 大量使用CSS transitions和animations实现平滑的动画效果。 使用transform属性(如translate、scale、rotate)实现元素的移动和变形。 使用clip-path和overflow控制元素的可见区域。 巧妙运用z-index和定位来管理层叠关系。 使用SVG创建复杂的图形元素(如云、星星、熊等)。 使用嵌套的span元素组织不同的视觉元素。 关键技巧: 使用CSS变量和calc()函数动态计算样式值,实现平滑过渡。 利用SVG的灵活性创建复杂的图形和动画。 通过改变单一CSS变量(--dark)来控制整个场景的变化。 创作来源:Jhey - Night && Day Toggle ☀️/🌙 详细代码解析:与众不同的夜间开关交互效果源代码 可上下滑动查看完整源代码: <!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> *, *:after, *:before { box-sizing: border-box; } :root { --slide-ease: cubic-bezier(.4, -0.3, .6, 1.3); --easing: var(--slide-ease); --speed: 0.5s; --width: clamp(200px, 45vmin, 500px); --ar: 8 / 3; --ray: hsl(0 0% 100% / 0.5); --sun: hsl(47, 91%, 58%); --moon: hsl(212, 13%, 82%); --crater: hsl(221, 16%, 68%); --bg: hsl(219, 30%, 88%); --bear-speed: 10s; --color: hsl(219 30% 20%); } [data-dark-mode=true] { --bg: hsl(219, 30%, 12%); --color: hsl(219 30% 98%); } body { display: grid; place-items: center; min-height: 100vh; overflow: hidden; background: var(--bg); transition: background var(--speed) var(--easing); font-family: sans-serif, system-ui; } .toggle__backdrop:first-of-type .clouds path:first-of-type { fill: var(--ray); } .toggle { -webkit-tap-highlight-color: transparent; width: var(--width); /* random attempts at tackling the overflow iOS issue */ z-index: 10; will-change: transform; isolation: isolate; transform: translate3d(0, 0, 0); /* End of workaround city */ aspect-ratio: var(--ar); border-radius: 100vh; border: 0; position: relative; padding: 0; overflow: hidden; cursor: pointer; transition: background var(--speed) var(--easing); --sky: hsl(204, 53%, 47%); --night: hsl(229, 25%, 16%); outline-color: transparent; background: hsl(calc(204 + (var(--dark, 0) * 25)) calc((53 - (var(--dark, 0) * 28)) * 1%) calc((47 - (var(--dark, 0) * 31)) * 1%)); box-shadow: calc(var(--width) * 0) calc(var(--width) * 0.02) calc(var(--width) * 0.01) calc(var(--width) * -0.0025) hsl(210 10% 100% / 0.95), calc(var(--width) * 0) calc(var(--width) * -0.02) calc(var(--width) * 0.01) calc(var(--width) * -0.0025) hsl(210 10% 10% / 0.2), calc(var(--width) * 0) calc(var(--width) * 0.02) calc(var(--width) * 0.5) 0 hsl(210 10% 100% / 0.15); } .toggle:after { content: ""; position: absolute; inset: 0; box-shadow: calc(var(--width) * 0) calc(var(--width) * -0.025) calc(var(--width) * 0.025) 0 hsl(210 10% 10% / 0.15) inset, calc(var(--width) * 0) calc(var(--width) * 0.025) calc(var(--width) * 0.025) 0 hsl(210 10% 10% / 0.65) inset; border-radius: 100vh; } .toggle__content { position: absolute; top: 0; left: 0; right: 0; bottom: 0; overflow: hidden; border-radius: 100vh; display: block; clip-path: inset(0 0 0 0 round 100vh); } .toggle__backdrop { overflow: visible !important; position: absolute; bottom: 0; width: 100%; left: 0; transition: translate var(--speed) var(--easing); translate: 0 calc(var(--dark, 0) * (100% - (3 / 8 * var(--width)))); } [aria-pressed=false] .toggle__backdrop:last-of-type { transition-timing-function: cubic-bezier(.2, -0.6, .7, 1.6); } [aria-pressed=false] .stars path { transition-delay: 0s; } .stars path { transform-box: fill-box; transform-origin: 25% 50%; scale: calc(0.25 + (var(--dark, 0) * 0.75)); transition: scale var(--speed) calc(var(--speed) * 0.5) var(--easing); } .toggle__indicator { height: 100%; aspect-ratio: 1; border-radius: 0%; display: grid; place-items: center; padding: 3%; } .pilot-bear { position: absolute; width: 25%; } .toggle__star { height: 100%; aspect-ratio: 1; border-radius: 50%; position: relative; transition: translate var(--speed) var(--easing); translate: calc((var(--dark, 0) * -10%) + 5%) 0; /* translate: calc((var(--dark, 0) * -18%) + 5%) 0; */ } .sun { background: var(--sun); position: absolute; inset: 0; border-radius: 50%; overflow: hidden; box-shadow: calc(var(--width) * 0.01) calc(var(--width) * 0.01) calc(var(--width) * 0.02) 0 hsl(210 10% 100% / 0.95) inset, calc(var(--width) * -0.01) calc(var(--width) * -0.01) calc(var(--width) * 0.02) 0 hsl(210 10% 20% / 0.5) inset; } .moon { position: absolute; inset: -1%; border-radius: 50%; background: var(--moon); transition: translate var(--speed) ease-in-out; translate: calc((100 - (var(--dark, 0) * 100)) * 1%) 0%; box-shadow: calc(var(--width) * 0.01) calc(var(--width) * 0.01) calc(var(--width) * 0.02) 0 hsl(210 10% 100% / 0.95) inset, calc(var(--width) * -0.01) calc(var(--width) * -0.01) calc(var(--width) * 0.02) 0 hsl(210 10% 10% / 0.95) inset; } .moon__crater { position: absolute; background: var(--crater); border-radius: 50%; width: calc(var(--size, 10) * 1%); aspect-ratio: 1; left: calc(var(--x) * 1%); top: calc(var(--y) * 1%); box-shadow: calc(var(--width) * 0.01) calc(var(--width) * 0.01) calc(var(--width) * 0.01) 0 hsl(210 10% 6% / 0.25) inset, 0 calc(var(--width) * 0.005) calc(var(--width) * 0.01) 0 hsl(210 10% 100% / 0.25); } .moon__crater:nth-of-type(1) { --size: 18; --x: 40; --y: 15; } .moon__crater:nth-of-type(2) { --size: 20; --x: 65; --y: 58; } .moon__crater:nth-of-type(3) { --size: 34; --x: 18; --y: 40; } .toggle__star:before { content: ""; z-index: -1; width: 356%; background: radial-gradient(hsl(0 0% 100% / 0.25) 40%, transparent 40.5%), radial-gradient(hsl(0 0% 100% / 0.25) 56%, transparent 56.5%) hsl(0 0% 100% / 0.25); border-radius: 50%; aspect-ratio: 1; position: absolute; top: 50%; left: 50%; transition: translate var(--speed) var(--easing); translate: calc((50 - (var(--dark, 0) * 4)) * -1%) -50%; } .toggle__star:after { content: ""; position: absolute; inset: 0; display: block; background: hsl(0 0% 0% / 0.5); filter: blur(4px); translate: 2% 4%; border-radius: 50%; z-index: -1; } .toggle__indicator-wrapper { position: absolute; inset: 0; transition: translate var(--speed) var(--slide-ease); translate: calc(var(--dark, 0) * (var(--width) - (3 / 8 * var(--width)))) 0; } [aria-pressed=true] { --dark: 1; } /* Fun stuff! */ /* We have 11 stars */ .stars g { transform-box: fill-box; transform-origin: 50% 50%; } .stars g:nth-of-type(3) { animation: twinkle 4s -2s infinite; } .stars g:nth-of-type(11) { animation: twinkle 6s -2s infinite; } .stars g:nth-of-type(9) { animation: twinkle 4s -1s infinite; } @keyframes twinkle { 0%, 40%, 60%, 100% { transform: scale(1); } 50% { transform: scale(0); } } .astrobear { width: 12%; position: absolute; top: 100%; left: 0%; transition: translate calc(var(--speed) + (var(--dark, 0) * (var(--bear-speed) - var(--speed)))) calc(var(--bear-speed) * (0.4 * var(--dark, 0))) linear; translate: calc(var(--dark, 0) * 400%) calc(var(--dark, 0) * -350%); } .astrobear svg { transform-origin: 50% 75%; scale: var(--dark, 0); rotate: calc(var(--dark, 0) * 360deg); transition: rotate calc(var(--speed) + (var(--dark, 0) * (var(--bear-speed) - var(--speed)))) calc(var(--bear-speed) * 0.4) linear, scale var(--speed) ease-in-out; } .astrobear__container { position: absolute; overflow: hidden; inset: 0; clip-path: inset(0 0 0 0); opacity: var(--dark, 0); translate: 0 calc(-200% + (var(--dark, 0) * 200%)); transition: opacity var(--speed) var(--easing), translate var(--speed) var(--easing); } .pilot__container { position: absolute; overflow: hidden; inset: 0; clip-path: inset(0 0 0 0); opacity: calc(1 - var(--dark, 0)); translate: 0 calc(var(--dark, 0) * 200%); transition: opacity var(--speed) var(--easing), translate var(--speed) var(--easing); } .pilot-bear { width: 18%; position: absolute; top: 70%; left: 100%; transition: translate calc(var(--speed) + ((1 - var(--dark, 0)) * ((var(--bear-speed) * 0.5) - var(--speed)))) calc((var(--bear-speed) * 0.5) * ((1 - var(--dark, 0)) * 0.4)) linear; translate: calc((0 - (1 - var(--dark, 0))) * (var(--width) + 100%)) calc((0 - (1 - var(--dark, 0))) * (200%)); } .pilot { rotate: 12deg; animation: fly 4s infinite ease-in-out; width: 100%; } @keyframes fly { 50% { translate: 0 -25%; } } .controls { position: fixed; bottom: 1rem; right: 1rem; display: flex; align-items: center; gap: 0.5rem; font-family: sans-serif; color: var(--color); transition: color var(--speed) var(--easing); } [type=checkbox] { accent-color: var(--color); transition: accent-color var(--speed) var(--easing); } </style> </head> <body> <div class="controls"> <label for="sync">Sync <body></label> <input id="sync" type="checkbox" /> </div> <!-- 背景色控制 --> <button class="toggle" aria-pressed="false" title="Toggle Dark Mode"> <span class="toggle__content"> <!-- light模式的云2 --> <svg aria-hidden=true class="toggle__backdrop" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 290 228"> <g class="clouds"> <path fill="#D9D9D9" d="M335 147.5c0 27.89-22.61 50.5-50.5 50.5a50.78 50.78 0 0 1-9.29-.853c-2.478 12.606-10.595 23.188-21.615 29.011C245.699 243.749 228.03 256 207.5 256a50.433 50.433 0 0 1-16.034-2.599A41.811 41.811 0 0 1 166 262a41.798 41.798 0 0 1-22.893-6.782A42.21 42.21 0 0 1 135 256a41.82 41.82 0 0 1-19.115-4.592A41.84 41.84 0 0 1 88 262c-1.827 0-3.626-.117-5.391-.343C74.911 270.448 63.604 276 51 276c-23.196 0-42-18.804-42-42s18.804-42 42-42c1.827 0 3.626.117 5.391.343C64.089 183.552 75.396 178 88 178a41.819 41.819 0 0 1 19.115 4.592C114.532 176.002 124.298 172 135 172a41.798 41.798 0 0 1 22.893 6.782 42.066 42.066 0 0 1 7.239-.773C174.137 164.159 189.749 155 207.5 155c.601 0 1.199.01 1.794.031A41.813 41.813 0 0 1 234 147h.002c.269-27.66 22.774-50 50.498-50 27.89 0 50.5 22.61 50.5 50.5Z" /> </g> </svg> <!-- light模式的小飞机 --> <span aria-hidden=true class="pilot__container"> <span class="pilot-bear"> <img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f13edbe752dd4d11b7c394aaf0f68637~tplv-k3u1fbpfcp-watermark.image?" alt="pilot-bear" class="pilot" /> </span> </span> <!-- light模式的云1 --> <svg aria-hidden=true class="toggle__backdrop" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 290 228"> <g class="clouds"> <path fill="#fff" d="M328 167.5c0 15.214-7.994 28.56-20.01 36.068.007.31.01.621.01.932 0 23.472-19.028 42.5-42.5 42.5-3.789 0-7.461-.496-10.957-1.426C249.671 263.676 233.141 277 213.5 277a42.77 42.77 0 0 1-7.702-.696C198.089 284.141 187.362 289 175.5 289a42.338 42.338 0 0 1-27.864-10.408A42.411 42.411 0 0 1 133.5 281c-4.36 0-8.566-.656-12.526-1.876C113.252 287.066 102.452 292 90.5 292a42.388 42.388 0 0 1-15.8-3.034A42.316 42.316 0 0 1 48.5 298C25.028 298 6 278.972 6 255.5S25.028 213 48.5 213a42.388 42.388 0 0 1 15.8 3.034A42.316 42.316 0 0 1 90.5 207c4.36 0 8.566.656 12.526 1.876C110.748 200.934 121.548 196 133.5 196a42.338 42.338 0 0 1 27.864 10.408A42.411 42.411 0 0 1 175.5 204c2.63 0 5.204.239 7.702.696C190.911 196.859 201.638 192 213.5 192c3.789 0 7.461.496 10.957 1.426 2.824-10.491 9.562-19.377 18.553-24.994-.007-.31-.01-.621-.01-.932 0-23.472 19.028-42.5 42.5-42.5s42.5 19.028 42.5 42.5Z" /> </g> </svg> <span class="toggle__indicator-wrapper"> <span class="toggle__indicator"> <span class="toggle__star"> <span class="sun"> <span class="moon"> <span class="moon__crater"></span> <span class="moon__crater"></span> <span class="moon__crater"></span> </span> </span> </span> </span> </span> <!-- dark模式的星星 --> <svg aria-hidden=true class="toggle__backdrop" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 290 228"> <g> <g class="stars"> <g> <path fill="#fff" fill-rule="evenodd" d="M61 11.5a.75.75 0 0 1 .721.544l.813 2.846a3.75 3.75 0 0 0 2.576 2.576l2.846.813a.75.75 0 0 1 0 1.442l-2.846.813a3.749 3.749 0 0 0-2.576 2.576l-.813 2.846a.75.75 0 0 1-1.442 0l-.813-2.846a3.749 3.749 0 0 0-2.576-2.576l-2.846-.813a.75.75 0 0 1 0-1.442l2.846-.813a3.749 3.749 0 0 0 2.576-2.576l.813-2.846A.75.75 0 0 1 61 11.5Z" clip-rule="evenodd" /> </g> <g> <path fill="#fff" fill-rule="evenodd" d="M62.5 45.219a.329.329 0 0 1 .315.238l.356 1.245a1.641 1.641 0 0 0 1.127 1.127l1.245.356a.328.328 0 0 1 0 .63l-1.245.356a1.641 1.641 0 0 0-1.127 1.127l-.356 1.245a.328.328 0 0 1-.63 0l-.356-1.245a1.641 1.641 0 0 0-1.127-1.127l-1.245-.356a.328.328 0 0 1 0-.63l1.245-.356a1.641 1.641 0 0 0 1.127-1.127l.356-1.245a.328.328 0 0 1 .315-.238Z" clip-rule="evenodd" /> </g> <g> <path fill="#fff" fill-rule="evenodd" d="M32 31.188a.28.28 0 0 1 .27.204l.305 1.067a1.405 1.405 0 0 0 .966.966l1.068.305a.28.28 0 0 1 0 .54l-1.068.305a1.405 1.405 0 0 0-.966.966l-.305 1.068a.28.28 0 0 1-.54 0l-.305-1.068a1.406 1.406 0 0 0-.966-.966l-1.067-.305a.28.28 0 0 1 0-.54l1.067-.305a1.406 1.406 0 0 0 .966-.966l.305-1.068a.281.281 0 0 1 .27-.203Z" clip-rule="evenodd" /> </g> <g> <path fill="#fff" fill-rule="evenodd" d="M41.5 74.219a.329.329 0 0 1 .315.238l.356 1.245a1.641 1.641 0 0 0 1.127 1.127l1.245.356a.328.328 0 0 1 0 .63l-1.245.356a1.641 1.641 0 0 0-1.127 1.127l-.356 1.245a.328.328 0 0 1-.63 0l-.356-1.245a1.641 1.641 0 0 0-1.127-1.127l-1.245-.356a.328.328 0 0 1 0-.63l1.245-.356a1.641 1.641 0 0 0 1.127-1.127l.356-1.245a.328.328 0 0 1 .315-.238Z" clip-rule="evenodd" /> </g> <g> <path fill="#fff" fill-rule="evenodd" d="M34 83.188a.28.28 0 0 1 .27.203l.305 1.068a1.405 1.405 0 0 0 .966.966l1.068.305a.28.28 0 0 1 0 .54l-1.068.305a1.405 1.405 0 0 0-.966.966l-.305 1.068a.28.28 0 0 1-.54 0l-.305-1.068a1.406 1.406 0 0 0-.966-.966l-1.068-.305a.28.28 0 0 1 0-.54l1.068-.305a1.406 1.406 0 0 0 .966-.966l.305-1.068a.281.281 0 0 1 .27-.204Z" clip-rule="evenodd" /> </g> <g> <path fill="#fff" fill-rule="evenodd" d="M63 89.25a.375.375 0 0 1 .36.272l.407 1.423a1.874 1.874 0 0 0 1.288 1.288l1.423.406a.374.374 0 0 1 0 .722l-1.423.406a1.874 1.874 0 0 0-1.288 1.288l-.407 1.423a.374.374 0 0 1-.72 0l-.407-1.423a1.874 1.874 0 0 0-1.288-1.288l-1.423-.406a.374.374 0 0 1 0-.722l1.423-.406a1.874 1.874 0 0 0 1.288-1.288l.407-1.423a.376.376 0 0 1 .36-.272Z" clip-rule="evenodd" /> </g> <g> <path fill="#fff" fill-rule="evenodd" d="M110.5 53.156a.236.236 0 0 1 .225.17l.254.89a1.174 1.174 0 0 0 .805.805l.89.254a.23.23 0 0 1 .122.084.233.233 0 0 1-.122.366l-.89.254a1.167 1.167 0 0 0-.805.805l-.254.89a.232.232 0 0 1-.225.17.235.235 0 0 1-.225-.17l-.254-.89a1.174 1.174 0 0 0-.805-.805l-.89-.254a.23.23 0 0 1-.122-.084.233.233 0 0 1 .122-.366l.89-.254a1.167 1.167 0 0 0 .805-.805l.254-.89a.232.232 0 0 1 .225-.17Z" clip-rule="evenodd" /> </g> <g> <path fill="#fff" fill-rule="evenodd" d="M120 27.188a.279.279 0 0 1 .27.204l.305 1.067a1.41 1.41 0 0 0 .966.966l1.067.305a.283.283 0 0 1 .148.1.286.286 0 0 1 0 .34.283.283 0 0 1-.148.1l-1.067.305a1.403 1.403 0 0 0-.966.966l-.305 1.067a.279.279 0 0 1-.439.147.275.275 0 0 1-.101-.147l-.305-1.067a1.41 1.41 0 0 0-.966-.966l-1.068-.305a.284.284 0 0 1-.147-.1.286.286 0 0 1 0-.34.284.284 0 0 1 .147-.1l1.068-.305a1.405 1.405 0 0 0 .966-.966l.305-1.067a.279.279 0 0 1 .27-.204Z" clip-rule="evenodd" /> </g> <g> <path fill="#fff" fill-rule="evenodd" d="M155 28.5a.753.753 0 0 1 .721.544l.813 2.846a3.746 3.746 0 0 0 2.576 2.576l2.846.813a.747.747 0 0 1 .543.721.75.75 0 0 1-.543.721l-2.846.813a3.75 3.75 0 0 0-2.576 2.576l-.813 2.846a.747.747 0 0 1-.721.543.749.749 0 0 1-.721-.543l-.813-2.846a3.746 3.746 0 0 0-2.576-2.576l-2.846-.813a.747.747 0 0 1-.543-.721.75.75 0 0 1 .543-.721l2.846-.813a3.75 3.75 0 0 0 2.576-2.576l.813-2.846A.751.751 0 0 1 155 28.5Z" clip-rule="evenodd" /> </g> <g> <path fill="#fff" fill-rule="evenodd" d="M147 60.25a.377.377 0 0 1 .36.272l.407 1.423a1.883 1.883 0 0 0 1.288 1.288l1.423.407a.375.375 0 0 1 0 .72l-1.423.407a1.875 1.875 0 0 0-1.288 1.288l-.407 1.423a.371.371 0 0 1-.36.272.377.377 0 0 1-.36-.272l-.407-1.423a1.883 1.883 0 0 0-1.288-1.288l-1.423-.406a.375.375 0 0 1 0-.722l1.423-.406a1.875 1.875 0 0 0 1.288-1.288l.407-1.423a.372.372 0 0 1 .36-.272Z" clip-rule="evenodd" /> </g> <g> <path fill="#fff" fill-rule="evenodd" d="M125.5 76.344a.513.513 0 0 1 .496.374l.559 1.956a2.574 2.574 0 0 0 1.771 1.771l1.956.56a.514.514 0 0 1 .27.805.514.514 0 0 1-.27.186l-1.956.559a2.57 2.57 0 0 0-1.771 1.77l-.559 1.957a.514.514 0 0 1-.806.27.514.514 0 0 1-.186-.27l-.559-1.956a2.574 2.574 0 0 0-1.771-1.771l-1.956-.56a.514.514 0 0 1-.27-.805.514.514 0 0 1 .27-.186l1.956-.559a2.57 2.57 0 0 0 1.771-1.77l.559-1.957a.515.515 0 0 1 .496-.374Z" clip-rule="evenodd" /> </g> </g> </g> </svg> <!-- 太空熊动画 --> <span class="astrobear__container"> <span class="astrobear"> <svg aria-hidden=true xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 316 432"> <circle cx="158" cy="143" r="140" fill="#444" /> <circle cx="158" cy="143" r="140" fill="url(#a)" fill-opacity=".2" /> <circle cx="158" cy="143" r="140" stroke="#000" stroke-width="6" /> <path fill="#AF7128" fill-rule="evenodd" d="M65.98 159.61C49.913 155.643 38 141.134 38 123.842 38 103.495 54.495 87 74.842 87c14.337 0 26.761 8.19 32.85 20.146C119.687 100.674 133.414 97 148 97h20c14.52 0 28.19 3.641 40.146 10.059C214.251 95.15 226.65 87 240.952 87c20.347 0 36.842 16.495 36.842 36.842 0 17.222-11.818 31.685-27.787 35.72A85.104 85.104 0 0 1 253 182v66.56l10.054-10.054c11.325-11.325 29.687-11.325 41.012 0s11.325 29.687 0 41.012l-44.548 44.548a29.004 29.004 0 0 1-6.518 4.906V407c0 12.15-9.85 22-22 22h-44c-12.15 0-22-9.85-22-22v-28.69a41.072 41.072 0 0 1-14 .174V407c0 12.15-9.85 22-22 22H85c-12.15 0-22-9.85-22-22v-77.797a28.99 28.99 0 0 1-6.946-5.137l-44.548-44.548c-11.325-11.325-11.325-29.687 0-41.012 11.326-11.325 29.687-11.325 41.013 0L63 248.988V182a85.106 85.106 0 0 1 2.98-22.39Z" clip-rule="evenodd" /> <path fill="#000" d="m65.98 159.61 2.894.789a3.002 3.002 0 0 0-2.175-3.701l-.72 2.912Zm41.712-52.464-2.673 1.362a3 3 0 0 0 4.098 1.279l-1.425-2.641Zm100.454-.087-1.419 2.643a3 3 0 0 0 4.089-1.274l-2.67-1.369Zm41.861 52.503-.735-2.908a2.997 2.997 0 0 0-2.159 3.698l2.894-.79ZM253 248.56h-3a3 3 0 0 0 5.121 2.121L253 248.56Zm10.054-10.054-2.121-2.121 2.121 2.121Zm41.012 0-2.121 2.122 2.121-2.122Zm0 41.012 2.121 2.122-2.121-2.122ZM253 328.972l-1.448-2.627a3 3 0 0 0-1.552 2.627h3Zm-88 49.338h3a3 3 0 0 0-3.548-2.949l.548 2.949Zm-14 .174.475-2.963a3 3 0 0 0-3.475 2.963h3Zm-88-49.281h3a3 3 0 0 0-1.597-2.651L63 329.203Zm-6.946-5.137-2.121 2.121 2.121-2.121Zm-44.548-44.548-2.121 2.122 2.121-2.122Zm0-41.012 2.122 2.122-2.122-2.122Zm41.013 0-2.122 2.122 2.122-2.122ZM63 248.988l-2.121 2.121A2.999 2.999 0 0 0 66 248.988h-3ZM35 123.842c0 18.704 12.886 34.391 30.26 38.681l1.439-5.825C51.941 153.054 41 139.721 41 123.842h-6ZM74.842 84C52.838 84 35 101.838 35 123.842h6C41 105.151 56.151 90 74.842 90v-6Zm35.524 21.785C103.785 92.862 90.351 84 74.842 84v6c13.165 0 24.58 7.517 30.177 18.508l5.347-2.723ZM148 94c-15.095 0-29.311 3.803-41.733 10.506l2.85 5.281C120.685 103.544 133.924 100 148 100v-6Zm20 0h-20v6h20v-6Zm41.565 10.416C197.183 97.769 183.027 94 168 94v6c14.013 0 27.196 3.512 38.727 9.702l2.838-5.286ZM240.952 84c-15.471 0-28.878 8.82-35.476 21.691l5.34 2.737C216.427 97.481 227.819 90 240.952 90v-6Zm39.842 39.842c0-22.004-17.838-39.842-39.842-39.842v6c18.69 0 33.842 15.151 33.842 33.842h6Zm-30.052 38.629c17.269-4.364 30.052-20 30.052-38.629h-6c0 15.816-10.853 29.104-25.522 32.812l1.47 5.817ZM256 182a88.09 88.09 0 0 0-3.099-23.228l-5.788 1.58A82.082 82.082 0 0 1 250 182h6Zm0 66.56V182h-6v66.56h6Zm-.879 2.121 10.054-10.053-4.242-4.243-10.054 10.054 4.242 4.242Zm10.054-10.053c10.154-10.154 26.616-10.154 36.77 0l4.242-4.243c-12.496-12.497-32.758-12.497-45.254 0l4.242 4.243Zm36.77 0c10.153 10.153 10.153 26.615 0 36.769l4.242 4.243c12.497-12.497 12.497-32.758 0-45.255l-4.242 4.243Zm0 36.769-44.548 44.548 4.243 4.242 44.547-44.547-4.242-4.243Zm-44.548 44.548a26.013 26.013 0 0 1-5.845 4.4l2.896 5.255a32.006 32.006 0 0 0 7.192-5.413l-4.243-4.242ZM256 360v-31.028h-6V360h6Zm0 47v-47h-6v47h6Zm-25 25c13.807 0 25-11.193 25-25h-6c0 10.493-8.506 19-19 19v6Zm-44 0h44v-6h-44v6Zm-25-25c0 13.807 11.193 25 25 25v-6c-10.493 0-19-8.507-19-19h-6Zm0-28.69V407h6v-28.69h-6Zm-4.5 3.69c2.74 0 5.429-.253 8.048-.74l-1.096-5.899c-2.261.42-4.583.639-6.952.639v6Zm-6.975-.554c2.279.365 4.608.554 6.975.554v-6c-2.047 0-4.058-.163-6.025-.479l-.95 5.925ZM154 407v-28.516h-6V407h6Zm-25 25c13.807 0 25-11.193 25-25h-6c0 10.493-8.506 19-19 19v6Zm-44 0h44v-6H85v6Zm-25-25c0 13.807 11.193 25 25 25v-6c-10.493 0-19-8.507-19-19h-6Zm0-47v47h6v-47h-6Zm0-30.797V360h6v-30.797h-6Zm-6.067-3.016a32.008 32.008 0 0 0 7.664 5.668l2.806-5.303a26.002 26.002 0 0 1-6.228-4.607l-4.242 4.242ZM9.385 281.64l44.548 44.547 4.242-4.242-44.547-44.548-4.243 4.243Zm0-45.255c-12.497 12.497-12.497 32.758 0 45.255l4.243-4.243c-10.154-10.154-10.154-26.616 0-36.769l-4.243-4.243Zm45.255 0c-12.497-12.497-32.758-12.497-45.255 0l4.243 4.243c10.153-10.154 26.616-10.154 36.77 0l4.242-4.243Zm10.481 10.481L54.64 236.385l-4.243 4.243 10.482 10.481 4.242-4.243ZM60.001 182v66.988h6V182h-6Zm3.084-23.178A88.1 88.1 0 0 0 60 182h6c0-7.48 1.001-14.722 2.874-21.601l-5.789-1.577Z" /> <path fill="#fff" fill-rule="evenodd" d="M267.721 234.712C241.49 266.061 202.073 286 158 286c-43.749 0-82.91-19.647-109.141-50.598-11.328-8.104-27.18-7.069-37.353 3.104-11.325 11.325-11.325 29.687 0 41.012l44.548 44.548A28.99 28.99 0 0 0 63 329.203V407c0 12.15 9.85 22 22 22h44c12.15 0 22-9.85 22-22v-28.517c2.123.341 4.293.517 6.5.517 2.555 0 5.06-.236 7.5-.69V407c0 12.15 9.85 22 22 22h44c12.15 0 22-9.85 22-22V328.972a29.004 29.004 0 0 0 6.518-4.906l44.548-44.548c11.325-11.325 11.325-29.687 0-41.012-9.875-9.875-25.099-11.139-36.345-3.794Z" clip-rule="evenodd" /> <path fill="url(#b)" fill-opacity=".5" fill-rule="evenodd" d="M267.721 234.712C241.49 266.061 202.073 286 158 286c-43.749 0-82.91-19.647-109.141-50.598-11.328-8.104-27.18-7.069-37.353 3.104-11.325 11.325-11.325 29.687 0 41.012l44.548 44.548A28.99 28.99 0 0 0 63 329.203V407c0 12.15 9.85 22 22 22h44c12.15 0 22-9.85 22-22v-28.517c2.123.341 4.293.517 6.5.517 2.555 0 5.06-.236 7.5-.69V407c0 12.15 9.85 22 22 22h44c12.15 0 22-9.85 22-22V328.972a29.004 29.004 0 0 0 6.518-4.906l44.548-44.548c11.325-11.325 11.325-29.687 0-41.012-9.875-9.875-25.099-11.139-36.345-3.794Z" clip-rule="evenodd" /> <path fill="#000" d="m267.721 234.712-1.64-2.511c-.248.162-.47.359-.66.586l2.3 1.925Zm-218.862.69 2.289-1.94a3.026 3.026 0 0 0-.544-.5l-1.745 2.44Zm-37.353 3.104 2.122 2.121-2.122-2.121Zm0 41.012-2.121 2.122 2.121-2.122Zm44.548 44.548-2.121 2.121 2.121-2.121ZM63 329.203h3a2.999 2.999 0 0 0-1.597-2.651L63 329.203Zm88 49.28.475-2.962a3 3 0 0 0-3.475 2.962h3Zm14-.173h3a3 3 0 0 0-3.548-2.949l.548 2.949Zm88-49.338-1.448-2.627a3 3 0 0 0-1.552 2.627h3Zm51.066-49.454 2.121 2.122-2.121-2.122Zm0-41.012-2.121 2.121 2.121-2.121Zm-38.645-5.719C239.735 263.484 201.148 283 158 283v6c44.999 0 85.245-20.361 112.022-52.362l-4.601-3.851ZM158 283c-42.831 0-81.167-19.23-106.852-49.538l-4.578 3.879C73.347 268.937 113.332 289 158 289v-6ZM13.628 240.627c9.118-9.118 23.331-10.049 33.486-2.785l3.49-4.88c-12.502-8.944-29.991-7.805-41.219 3.423l4.243 4.242Zm0 36.77c-10.154-10.154-10.154-26.616 0-36.77l-4.243-4.242c-12.497 12.497-12.497 32.758 0 45.255l4.243-4.243Zm44.547 44.548-44.547-44.548-4.243 4.243 44.548 44.547 4.242-4.242Zm6.228 4.607a26.002 26.002 0 0 1-6.228-4.607l-4.242 4.242a32.008 32.008 0 0 0 7.664 5.668l2.806-5.303ZM66 360v-30.797h-6V360h6Zm0 47v-47h-6v47h6Zm19 19c-10.493 0-19-8.507-19-19h-6c0 13.807 11.193 25 25 25v-6Zm44 0H85v6h44v-6Zm19-19c0 10.493-8.506 19-19 19v6c13.807 0 25-11.193 25-25h-6Zm0-28.517V407h6v-28.517h-6Zm9.5-2.483c-2.047 0-4.058-.163-6.025-.479l-.95 5.925c2.279.365 4.608.554 6.975.554v-6Zm6.952-.639c-2.261.42-4.583.639-6.952.639v6c2.74 0 5.429-.253 8.048-.74l-1.096-5.899ZM168 407v-28.69h-6V407h6Zm19 19c-10.493 0-19-8.507-19-19h-6c0 13.807 11.193 25 25 25v-6Zm44 0h-44v6h44v-6Zm19-19c0 10.493-8.506 19-19 19v6c13.807 0 25-11.193 25-25h-6Zm0-47v47h6v-47h-6Zm0-31.028V360h6v-31.028h-6Zm7.397-7.027a26.043 26.043 0 0 1-5.845 4.4l2.896 5.255a32.036 32.036 0 0 0 7.192-5.413l-4.243-4.242Zm44.548-44.548-44.548 44.548 4.243 4.242 44.547-44.547-4.242-4.243Zm0-36.77c10.153 10.154 10.153 26.616 0 36.77l4.242 4.243c12.497-12.497 12.497-32.758 0-45.255l-4.242 4.242Zm-32.583-3.403c10.081-6.585 23.732-5.447 32.583 3.403l4.242-4.242c-10.898-10.899-27.697-12.29-40.106-4.184l3.281 5.023Z" /> <path fill="#000" d="M292.654 245.606a3 3 0 1 1-4.243-4.242l4.243 4.242Zm8.485-8.485-8.485 8.485-4.243-4.242 8.485-8.486 4.243 4.243ZM301.208 254.161a3 3 0 1 1-4.242-4.243l4.242 4.243Zm8.486-8.485-8.486 8.485-4.242-4.243 8.485-8.485 4.243 4.243ZM21.919 246.606a3 3 0 1 0 4.242-4.242l-4.242 4.242Zm-8.486-8.485 8.486 8.485 4.242-4.242-8.485-8.486-4.243 4.243ZM13.364 255.161a3 3 0 1 0 4.243-4.243l-4.243 4.243Zm-8.485-8.485 8.485 8.485 4.243-4.243-8.486-8.485-4.242 4.243Z" /> <path fill="#FF1E1E" d="M113.322 154.665h88.371v13.25h-88.371z" /> <path fill="#000" fill-rule="evenodd" d="M225.3 113.481c17.939 14.394 28.018 37.148 28.018 57.504H191.67c-.087-13.669-11.194-24.723-24.883-24.723h-18.56c-13.689 0-24.796 11.054-24.883 24.723H62c0-20.356 10.078-43.11 28.018-57.504C107.957 99.087 132.289 91 157.659 91c25.37 0 49.701 8.087 67.641 22.481Z" clip-rule="evenodd" /> <circle cx="212.665" cy="197.079" r="8.079" fill="#000" /> <circle cx="104.079" cy="197.079" r="8.079" fill="#000" /> <path fill="#000" d="M179.165 211.683c0 8.21-9.868 17.451-20.845 17.451-10.977 0-20.845-9.241-20.845-17.451 0-8.211 9.868-12.281 20.845-12.281 10.977 0 20.845 4.07 20.845 12.281Z" /> <path stroke="#000" stroke-linecap="round" stroke-width="6" d="M198 417v12M222 417v12M95 417v12M119 417v12" /> <circle cx="158" cy="143" r="140" fill="url(#c)" stroke="#000" stroke-width="6" /> <g clip-path="url(#d)"> <path fill="#F5D949" stroke="#000" stroke-width="6" d="m217.543 305.943.704 1.692 1.826.146 12.818 1.027h.001a.17.17 0 0 1 .059.011l.002.001a.147.147 0 0 1 .037.065.15.15 0 0 1 .008.075l-.001.002c0 .001-.01.017-.041.044h-.001l-9.765 8.365-1.391 1.192.425 1.782 2.981 12.506h.001c.009.04.008.058.008.06l-.001.002a.148.148 0 0 1-.05.056.146.146 0 0 1-.069.031h-.002c-.002-.001-.02-.005-.054-.026l-10.974-6.702-1.564-.955-1.564.955-10.974 6.702a.177.177 0 0 1-.053.025l-.002.001c-.004-.001-.032-.005-.069-.032a.146.146 0 0 1-.051-.056l-.001-.002s-.001-.018.008-.058l.001-.001 2.981-12.506.425-1.782-1.391-1.192-9.765-8.365h-.001c-.031-.027-.04-.043-.041-.044l-.001-.002a.15.15 0 0 1 .008-.075.147.147 0 0 1 .037-.065l.002-.001a.17.17 0 0 1 .059-.011h.001l12.818-1.027 1.826-.146.704-1.692 4.938-11.875a.161.161 0 0 1 .028-.051l.001-.001a.146.146 0 0 1 .076-.016c.047 0 .072.013.076.016l.001.001c.001 0 .012.013.028.051l4.938 11.875Z" /> </g> <path stroke="#000" stroke-linecap="round" stroke-width="16" d="M56.884 247.116A143.01 143.01 0 0 0 158 289a143.002 143.002 0 0 0 101.116-41.884" /> <path stroke="#000" stroke-width="6" d="M65.035 404s25.382-6.618 41.965-6.5c17.059.121 43.035 7.5 43.035 7.5M164.012 403.977s25.961-5.606 42.932-5.472C224.402 398.642 251 405 251 405M25 290l7.769-4.072a57.001 57.001 0 0 0 25.067-26.121L62 251M254.363 252l3.867 7.873a57.002 57.002 0 0 0 25.452 25.746l8.694 4.394" /> <defs> <linearGradient id="a" x1="158" x2="158" y1="0" y2="286" gradientUnits="userSpaceOnUse"> <stop offset=".219" /> <stop offset="1" stop-color="#fff" /> </linearGradient> <linearGradient id="b" x1="254" x2="100" y1="419" y2="325" gradientUnits="userSpaceOnUse"> <stop stop-opacity=".98" /> <stop offset="1" stop-opacity="0" /> </linearGradient> <radialGradient id="c" cx="0" cy="0" r="1" gradientTransform="matrix(165.9998 58.9999 -60.6938 170.7657 210 171)" gradientUnits="userSpaceOnUse"> <stop offset=".771" stop-color="#D9D9D9" stop-opacity="0" /> <stop offset="1" stop-color="#fff" stop-opacity=".63" /> </radialGradient> <clipPath id="d"> <path fill="#fff" d="M189 291h47v45h-47z" /> </clipPath> </defs> </svg> </span> </span> </span> </button> </body> <script> const BUTTON = document.querySelector("button"); const SYNC = document.querySelector("#sync") const TOGGLE = () => { const IS_PRESSED = BUTTON.matches("[aria-pressed=true]"); if (SYNC.checked) document.body.setAttribute("data-dark-mode", IS_PRESSED ? false : true); BUTTON.setAttribute("aria-pressed", IS_PRESSED ? false : true); }; BUTTON.addEventListener("click", TOGGLE); </script> </html>使用方式 复制源代码到空白的html格式文件,在浏览中打开运行即可。
技术教程
# Web前端
易航
1年前
3
33
1
1
2
...
5
下一页