找到
58
篇与
Web前端
相关的结果
- 第 6 页
-
纯CSS实现任意元素扫光效果的三种方式 介绍一个比较常见的动画效果。 在日常开发中,为了强调凸显某些文本或者元素,会加一些扫光动效,起到吸引眼球的效果,比如文本的 图片 或者是一个卡片容器,里面可能是图片或者文本或者任意元素 图片 除此之外,还有那种不规则的图片,比如奖品图案 图片 这些是如何实现的呢?一起看看吧 一、CSS 扫光的原理 CSS 扫光动画的原理很简单,就是一个普通的、从左到右的、无限循环的位移动画 图片 位移动画可以选择 transform 或者改变 background-position 都行。 至于扫光,我们只需要绘制一条斜向上45deg的线性渐变就可以了,示意如下 图片 用 CSS 实现就是 background: linear-gradient(45deg, rgba(255,255,255,0) 40%, rgba(255, 255, 255, 0.7), rgba(255,255,255,0) 60%);准备工作做好了,下面看 3 种不同场景的实现 二、文本扫光 首先来看文本扫光。 由于扫光在文本内部,所以需要将这个渐变作为文本的颜色。文本渐变色,可以用 backgrond-clip:text 来实现,假设 HTML 是这样的 <h1 class="shark-txt">前端侦探</h1>为了让效果看起来更加明显,我们用一个比较粗的字体 h1{ font-size: 60px; font-family: "RZGFDHDHJ"; font-weight: normal; color: #9747FF; }效果如下 图片 现在我们通过 background-clip 来添加扫光,由于是裁剪背景,所以需要将当前文本颜色设置透明,建议通过 -webkit-text-fill-color: transparent 来设置,这样可以保留文本原有颜色,好处是其他地方,比如 background-color 可以直接使用原有文本颜色 currentColor ,具体实现如下 .shark-txt{ -webkit-text-fill-color: transparent; background: linear-gradient(45deg, rgba(255,255,255,0) 40%, rgba(255, 255, 255, 0.7), rgba(255,255,255,0) 60%) -100%/50% no-repeat currentColor; -webkit-background-clip: text; }效果如下 图片 最后就是让这个扫光动起来了。 由于是在文本内部,所以这里可以通过改变background-position来实现扫光动画了,动画很简单,如下 @keyframes shark-txt { form{ background-position: -100%; } to { background-position: 200%; } }但是这样做没有动画效果,完全不会动。 这是因为背景默认尺寸是 100% ,根据背景偏移百分比的计算规则,当背景尺寸等于容器尺寸时,百分比完全失效,具体规则如下 给定背景图像位置的百分比偏移量是相对于容器的。值 0% 表示背景图像的左(或上)边界与容器的相应左(或上)边界对齐,或者说图像的 0% 标记将位于容器的 0% 标记上。值为 100% 表示背景图像的 右(或 下)边界与容器的 右(或 下)边界对齐,或者说图像的 100% 标记将位于容器的 100% 标记上。因此 50% 的值表示水平或垂直居中背景图像,因为图像的 50% 将位于容器的 50% 标记处。类似的,background-position: 25% 75% 表示图像上的左侧 25% 和顶部 75% 的位置将放置在距容器左侧 25% 和距容器顶部 75% 的容器位置。 https://developer.mozilla.org/zh-CN/docs/Web/CSS/background-position(container width - image width) * (position x%) = (x offset value) (container height - image height) * (position y%) = (y offset value)所以这种情况下,我们可以手动改小一点背景尺寸,比如 50% .shark-txt { -webkit-text-fill-color: transparent; background: linear-gradient(45deg, rgba(255, 255, 255, 0) 40%, rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0) 60%) -100% / 50% no-repeat currentColor; -webkit-background-clip: text; animation: shark-txt 2s infinite; }这样就能完美实现文本扫光效果了 图片 三、卡片容器扫光 还有一种比较常见的是容器内的扫光动效,通常是在一个圆角矩形的容器里。 像这种情况下就不能直接用背景渐变了,因为会被容器内的其他元素覆盖。所以我们需要创建一个伪元素,然后通过改变伪元素的位移来实现扫光动画了。 假设有一个容器,容器内有一张图片,HTML 如下 <div class="shark-wrap card"> <img src="https://imgservices-1252317822.image.myqcloud.com/coco/b11272023/ececa9a5.7y0amw.jpg"> </div>简单修饰一下 .card{ width: 300px; border-radius: 8px; background-color: #FFE8A3; } .card img{ display: block; width: 100%; }效果如下 图片 下面通过伪元素来创建一个扫光层,设置位移动画 .shark-wrap::after{ content: ''; position: absolute; inset: -20%; background: linear-gradient(45deg, rgba(255,255,255,0) 40%, rgba(255, 255, 255, 0.7), rgba(255,255,255,0) 60%); animation: shark-wrap 2s infinite; transform: translateX(-100%); } @keyframes shark-wrap { to { transform: translateX(100%); } }效果如下 图片 最后直接超出隐藏就行了 .shark-wrap{ overflow: hidden; }最终效果如下 图片 也适合那种圆形头像 图片 四、不规则图片扫光 其实前面两种情况已经适合大部分场景了,其实还有一种情况,就是那种不规则的图片扫光。这种图片无法直接通过 overflow:hidden 去隐藏多余部分,比如这样 图片 很明显在图片之外的地方也出现了扫光,无法做到扫光在图形的"内部"。 那么,有没有办法根据图片的外形去裁剪呢?当然也是有办法的,这里需要用到 CSS mask 遮罩。 简单来说,就是直接将该图片作为遮罩图片,这样只有形状内的部分可见,形状外的直接被裁剪了 图片 在上一种场景的情况下,只需要在此基础之上,添加一个完全相同的 mask 遮罩就行了 <div class="shark-wrap" style="-webkit-mask: url(https://imgservices-1252317822.image.myqcloud.com/coco/s09252023/3af9e8de.00uqxe.png) 0 0/100%"> <img class="logo" src="https://imgservices-1252317822.image.myqcloud.com/coco/s09252023/3af9e8de.00uqxe.png"> </div>这样就可以把扫光多余的部分裁剪掉了 图片 换张图也能很好适配 图片 五、总结一下 以上就本文的全部内容了,共介绍了3种不同的扫光场景,你学到了吗?下面总结一下重点 扫光样式本身可以直接用线性渐变绘制而成 扫光动画原理很简单,就是一个水平的位移动画 文本扫光动画需要通过改变 background-postion 实现 当背景尺寸等于容器尺寸时,设置 background-postion 百分比无效 普通容器的扫光效果需要借助伪元素实现,因为如果使用背景会被容器内的元素覆盖 普通容器的扫光动画可以直接用 transfrom 实现 使用 overflow:hidden 裁剪容器外的部分 不规则图片的扫光效果无法直接根据形状裁剪 借助 CSS mask 可以根据图片本身裁剪掉扫光多余部分 对了,这个属于常规需求,只是普通的动画效果,没有兼容性限制,放心使用,除了本文的样式,还可以根据需求改变扫光的大小,角度,颜色等,这个就看具体需求了。 六、完整案例 CSS shark animation (codepen.io):https://codepen.io/xboxyan/pen/KKLLZOE CSS shark animation (juejin.cn):https://code.juejin.cn/pen/7385810378132815882
-
使用JavaScript动态生成网站文章目录及内容滚动跟随效果 由于之前使用的文章导读插件性能过于臃肿,导致资源消耗高且难以维护,因此我决定自行开发一个轻量级的文章导读功能。参考了郄郄私语的 案例文章,我实现了以下四个主要功能: 根据文章内容自动生成目录 点击目录跳转至相应章节 滚动阅读时,目录随当前章节自动切换 目录跟随文章内容滚动 目标 本文旨在实现以下功能: 自动生成文章目录 点击目录跳转至对应章节 目录根据当前阅读章节自动高亮 目录随文章内容滚动 根据文章内容自动生成目录 实现自动生成目录的前提是文章内容须有清晰的层次结构,这需要使用 h1 至 h6 标签来定义标题。通过 JavaScript,我们可以提取这些标题来生成目录: var articleTitleList = $('.joe_detail__article').find('h1, h2, h3, h4, h5, h6'); articleTitleList.each(function() { const headingLevel = $(this).prop("tagName").toLowerCase(); const headingName = $(this).text().trim(); console.log(headingLevel, headingName); $('#catalogs').append(`<div class="catalog catalog-${headingLevel}">${headingName}</div>`); });在样式表中为目录项定义缩进样式: .catalog-h2 { margin-left: 1em; } .catalog-h3 { margin-left: 2em; } .catalog-h4 { margin-left: 3em; } .catalog-h5 { margin-left: 4em; } .catalog-h6 { margin-left: 5em; }点击目录跳转至对应章节 点击目录跳转至相应章节可以通过为每个标题添加锚点。我们将在 h1 至 h6 标签上添加 id: articleTitleList.each(function() { const headingName = $(this).text().trim(); const enHeadingName = encodeURIComponent(headingName); $(this).attr('id', `${enHeadingName}`); $('.posts-nav-lists>ul').append(`<li><a href="#${enHeadingName}">${headingName}</a></li>`); });目录自动切换至当前章节 为了让目录随着阅读位置自动切换,我们将监控用户的滚动位置并依据此更新目录的高亮条目: const catalogTrack = () => { let $currentHeading = $('h1'); articleTitleList.each(function() { const $heading = $(this); if ($heading.offset().top - $(document).scrollTop() > 20) return false; // 退出条件 $currentHeading = $heading; }); // 更新目录状态 const anchorName = $currentHeading.attr('id'); $('.posts-nav-lists>ul>li').removeClass('active'); $(`#title-${anchorName}`).parent().addClass('active'); };目录随文章内容滚动 为确保当前章节始终可见,我们控制目录区域的滚动: if ($catalog.length > 0) { $('.posts-nav-box').scrollTop($catalog[0].offsetTop - 50); // 确保可见 }性能优化 为了避免在滚动时频繁调用 catalogTrack 导致性能降低,我们可以使用函数节流技术: function throttle(fn, wait) { let lastTime = 0; return function() { const now = Date.now(); if (now - lastTime >= wait) { fn.apply(this, arguments); lastTime = now; } }; } window.addEventListener('scroll', throttle(catalogTrack, 500)); // 每500毫秒调用一次自定义跳转行为 考虑到固定导航条可能遮挡目标章节,我们可以自定义跳转行为,确保目标元素不会被遮挡: document.querySelectorAll('.posts-nav-lists>ul>li>a').forEach(link => { link.addEventListener('click', (event) => { event.preventDefault(); // 阻止默认跳转 const targetId = link.getAttribute('href').substring(1); const targetElement = document.getElementById(targetId); const headerHeight = document.querySelector('.joe_header').offsetHeight; const scrollTop = $(targetElement).offset().top - headerHeight - 10; window.scrollTo({ top: scrollTop, behavior: 'smooth' }); }); });总结 本文记录了生成和控制博客文章目录的思路与方法,针对不同需求可能会有其他解决方案。欢迎大家在评论区讨论和分享建议,感谢阅读! 完整代码示例 HTML 部分 <section class="joe_aside__item posts-nav-box"> <div class="joe_aside__item-title"><i class="fa fa-list-ul"></i><span class="text">文章目录</span><span class="line"></span></div> <div class="joe_aside__item-contain"> <div class="posts-nav-lists"><ul class="bl nav"></ul></div> </div> </section>JavaScript 部分 var articleTitleList = $('.joe_detail__article').find('h1, h2, h3, h4, h5, h6'); if (articleTitleList.length > 0) { // 目录生成及事件绑定 (function () { for (let heading of articleTitleList) { const headingLevel = heading.tagName.toUpperCase(); const $heading = $(heading); // console.log($heading); const headingName = $heading.text().trim(); const enHeadingName = encodeURIComponent(headingName); $heading.attr('id', `${enHeadingName}`); $('.posts-nav-lists>ul').append(`<li class="n-${headingLevel}"><a id="title-${enHeadingName}" href="#${enHeadingName}">${headingName}</a></li>`); // const anchorName = $heading.attr('id'); // console.log(headingLevel, headingName); } if (Joe.IS_MOBILE) { $('.joe_action').append(`<div class="joe_action_item posts-nav-switcher"><i class="fa fa-list-ul"></i></div>`) let joe_aside = $('.joe_aside .joe_aside__item.posts-nav-box .joe_aside__item-contain').html(); $('.joe_aside .joe_aside__item.posts-nav-box').remove(); let html = document.createElement('div'); html.className = 'posts-nav-box'; html.innerHTML = joe_aside; // console.log(html); $('.posts-nav-switcher').append(html); $('.joe_action_item .posts-nav-box').css({ 'position': 'absolute', 'display': 'none', 'right': '50px', 'bottom': '0px', 'padding': '15px', 'border-radius': 'var(--radius-wrap)', 'box-shadow': '0 0 10px 8px var(--main-shadow)', 'overflow': 'auto', 'max-height': '50vh', 'max-width': '80vw' }) $('.joe_action_item.posts-nav-switcher').click(() => { $('.joe_action_item.posts-nav-switcher .posts-nav-box').fadeToggle(200); }); } const catalogTrack = () => { // console.log('页面滚动标题监听'); let $currentHeading = $('h1'); for (let heading of articleTitleList) { const $heading = $(heading); if ($heading.offset().top - $(document).scrollTop() > $('.joe_header').height()) { break; } $currentHeading = $heading; const anchorName = $currentHeading.attr('id'); const $catalog = $(document.getElementById(`title-${anchorName}`)).parent(); if (!$catalog.hasClass('active')) { $('.posts-nav-lists>ul>li').removeClass('active'); $catalog.addClass('active'); } if ($catalog.length > 0) { if ($('.posts-nav-box .joe_aside__item-contain').length > 0) { $('.posts-nav-box .joe_aside__item-contain').scrollTop($catalog[0].offsetTop - 50); } else { $('.posts-nav-box').scrollTop($catalog[0].offsetTop - 50); } } else { $('.posts-nav-lists').scrollTop(0); } } }; /** * 函数节流,时间戳方案 * @param {*} fn * @param {*} wait * @returns */ function throttle(fn, wait) { var pre = Date.now(); return function () { var context = this; var args = arguments; var now = Date.now(); if (now - pre >= wait) { fn.apply(context, args); pre = Date.now(); } } } window.addEventListener('scroll', throttle(() => { catalogTrack(); }, 500)); // 监听文章目录a标签点击 document.querySelectorAll('.posts-nav-lists>ul>li>a').forEach(link => { link.addEventListener('click', (event) => { event.preventDefault(); // 阻止默认跳转行为 // 获取目标元素 ID const targetId = link.getAttribute('href').substring(1); const targetElement = document.getElementById(targetId); if (targetElement) { // 获取目标元素距离页面顶部的距离 const targetTop = $(targetElement).offset().top; // console.log(targetTop); // 获取顶栏高度,考虑动态高度变化和内部元素 const headerHeight = document.querySelector('.joe_header').offsetHeight; // 计算滚动位置 const scrollTop = (targetTop - headerHeight) - 10; // 预留10px的美观距离 window.scrollTo({ top: scrollTop, behavior: 'smooth' }); } else { console.error('目标元素未找到:', targetId); } }); }); }()) } else { $('.joe_aside__item.posts-nav-box').remove(); }通过以上修改,文章的可读性、结构性和实用性得以提升,使读者能够更好地理解和使用所提供的功能。
-
iFrame父子相互获取对方DOM元素 父页面获取子页面dom的方法 /* 必须用onload */ window.onload = () => { const sonWindow = document.querySelector("iframe").contentWindow; const sonDiv = sonWindow.document.querySelector(".son") console.log(sonDiv); }子页面获取父页面dom的方法 /* 必须用onload */ window.onload = () => { const parentWindow = window.parent; const parentDiv = parentWindow.document.querySelector(".parent") console.log(parentDiv); }
-
给你的网站增加一款简洁而功能强大的音乐播放器 H5播放器介绍 APlayer 是一个简洁漂亮、功能强大的 Html5 音乐播放器 MetingJS 是为 APlayer 添加网易云、QQ音乐、酷狗音乐等支持的插件 安装教程 安装很简单,一共需要调用三个文件:APlayer.min.js APlayer.min.css Meting.min.js 你可以使用 CDN 调用,只需要在 <head> 里面插入: <link href="https://cdn.bootcdn.net/ajax/libs/aplayer/1.10.1/APlayer.min.css" rel="stylesheet"> <script src="https://cdn.bootcdn.net/ajax/libs/aplayer/1.10.1/APlayer.min.js"></script>在 footer 里面插入: <script src="https://cdn.bootcdn.net/ajax/libs/meting/2.0.1/Meting.min.js"></script>当然,你可以将这些文件托管在自己的服务器,把上面的调用链接改成自己的就行了 使用方法 APlayer 原生用法 先看一个最简单的例子: <div id="aplayer"></div> <script type="text/javascript"> const ap = new APlayer({ container: document.getElementById('aplayer'), audio: [{ name: '你从未离去', artist: '白挺', url: 'https://doge.ottoli.cn/你从未离去.mp3', cover: 'https://doge.ottoli.cn/你从未离去.jpg' }] }); </script>在js 代码中: 参数 container 值为 document.getElementById('aplayer') 意思是定义当前播放器容器 id 为 aplayer 参数 audio 中有 4 个子参数,定义关于音频的相关参数: 参数 name 定义音频名称 参数 artist 定义艺术家名 参数 url 指向音频文件的地址 参数 cover 指向音频封面的地址 然后,在需要使用播放器的地方,将容器 <div> 的 id 设置为参数 container 中设定的值即可 MetingJS 的用法 前面已经看到,APlayer 原生用法设置参数十分繁琐,而且只能调用音频文件直链,增加服务器开销。而使用 MetingJS 就很好地解决了这个问题 先看一个最简单的例子: 对应的代码为: <meting-js server="netease" type="song" id="31365604" > </meting-js>一个 MetingJS 播放器至少需要三个参数: server 指定调用的 API ,可选 netease, tencent, kugou, xiami, baidu ,分别对应网易云音乐、QQ音乐、酷狗音乐、虾米音乐、百度音乐 type 指定调用类型,可选 song, playlist, album, search, artist ,分别对应单曲、歌单、专辑、搜索结果、艺术家 id 指定调用的 id ,一般可以在地址栏中找到 当 type 选择的是个播放列表时,生成的播放器是这样的: 播放列表默认是打开的,你可以使用参数 listFolded="true" 使其默认折叠 当你设定 fixed="true" ,会生成一个吸附在页面左下角的播放器,就像我的博客左下角那个 当你设定 mini="true" ,会生成一个 mini 播放器: 值得注意的是:除了 mini 模式,MetingJS 生成的播放器默认是带有歌词的(而且关不掉) 全部参数说明请查阅 MetingJS 官方文档(其实除了三个必要参数其余都和 APlayer 原生参数一样)
-
JavaScript Cookie插件 JavaScript Cookie介绍 一个简单、轻量级的JavaScript API,用于处理Cookie 安装 <script src="https://cdn.bootcdn.net/ajax/libs/js-cookie/3.0.1/js.cookie.min.js"></script>基本用法 创建一个在整个网站上有效的cookie: Cookies.set('name', 'value')创建一个7天后过期的cookie,在整个站点上有效: Cookies.set('name', 'value', { expires: 7 })创建对当前页面路径有效的过期cookie: Cookies.set('name', 'value', { expires: 7, path: '' })读取cookie: Cookies.get('name') // => 'value' Cookies.get('nothing') // => undefined读取所有可见cookie: Cookies.get() // => { name: 'value' }*注意:通过传递一个cookie属性(可能是也可能不是)来读取特定的cookie是不可能的 在编写所述cookie时使用):* Cookies.get('foo', { domain: 'sub.example.com' }) // `domain` won't have any effect...!名为的 foo cookie将仅在以下日期提供 .get() 如果从调用代码的地方可见;读取时,域和/或路径属性不会产生影响。 删除cookie: Cookies.remove('name')删除对当前页面路径有效的cookie: Cookies.set('name', 'value', { path: '' }) Cookies.remove('name') // fail! Cookies.remove('name', { path: '' }) // removed!这很重要!删除cookie时,如果您不依赖默认属性,则必须传递与设置cookie完全相同的路径和域属性: Cookies.remove('name', { path: '', domain: '.yourdomain.com' })注意:删除不存在的cookie既不会引发任何异常,也不会返回任何值。 命名空间冲突 如果存在与名称空间 Cookies 冲突的危险,noConflict 方法将允许您定义新名称空间并保留原始名称空间。当在第三方站点上运行脚本时,这尤其有用,例如作为小部件或SDK的一部分。 // 将js cookie api分配给不同的变量,并恢复原始的“window.Cookies” var Cookies2 = Cookies.noConflict() Cookies2.set('name', 'value')注意:在 .noConflict 方法在使用AMD或CommonJS时不是必需的,因此它不会在这些环境中公开。 Cookie属性 Cookie属性默认值可以通过 withAttributes() 创建api实例来全局设置,也可以通过传递一个普通对象作为最后一个参数来单独设置对`Cookie.set(…) 的每次调用。每次调用属性覆盖默认属性。 过期 定义何时删除cookie。值必须是 Number这将被解释为从创建时算起的天数或 日期例如。如果省略,cookie将成为会话cookie。 要创建一个在一天内过期的cookie,您可以查看 Wiki上的常见问题。 默认值:当用户关闭浏览器时,将删除Cookie。 例如: Cookies.set('name', 'value', { expires: 365 }) Cookies.get('name') // => 'value' Cookies.remove('name')路径 在 String 指示cookie可见的路径。 默认值: / 例如: Cookies.set('name', 'value', { path: '' }) Cookies.get('name') // => 'value' Cookies.remove('name', { path: '' })关于Internet Explorer的说明: 由于基础WinINET InternetGetCookie实现中的一个模糊错误,IE的文档。如果设置了包含文件名的路径属性,cookie将不会返回cookie。(From Internet Explorer Cookie Internals (FAQ)) 这意味着不能使用 window.location.pathname 设置路径,如果这样的路径名包含这样的文件名: /check.html (或者至少,这样的cookie无法正确读取)。 事实上,您不应该允许不受信任的输入设置cookie属性,否则您可能会受到XSS攻击。 域名 A String 指示cookie应可见的有效域。cookie也将对所有子域可见。 默认值: Cookie仅对创建Cookie的页面的域或子域可见,Internet Explorer除外(见下文)。 示例: 假设正在 site.com 上创建cookie: Cookies.set('name', 'value', { domain: 'subdomain.site.com' }) Cookies.get('name') // => undefined (need to read at 'subdomain.site.com')关于Internet Explorer默认行为的注意事项: Q3: 如果我没有为cookie指定域属性,IE会将其发送到所有嵌套的子域吗? A: 是的,cookie上的example.com集将被发送到sub2.sub1.example.com。 Internet Explorer在这方面与其他浏览器不同。这意味着如果您省略了 domain 属性,它将在IE的子域中可见。 安全的 可以是 true 或 false ,指示cookie传输是否需要安全协议(https)。 默认值: 没有安全协议要求。 示例: Cookies.set('name', 'value', { secure: true }) Cookies.get('name') // => 'value' Cookies.remove('name')相同站点 A String, 允许控制浏览器是否随跨站点请求一起发送cookie。 默认值:未定义。 请注意,最近的浏览器将“Lax”设置为默认值,即使没有在此处指定任何内容。 示例: Cookies.set('name', 'value', { sameSite: 'strict' }) Cookies.get('name') // => 'value' Cookies.remove('name')设置默认值 const api = Cookies.withAttributes({ path: '/', domain: '.example.com' })转换器 查看 创建api的新实例,以覆盖默认解码实现。所有依赖于正确解码的get方法,如 Cookies.get() 和 Cookies.get('name') ,都将为每个cookie运行给定的转换器。返回的值将用作cookie值。 读取只能使用 escape 功能解码的其中一个cookie的示例: document.cookie = 'escaped=%u5317' document.cookie = 'default=%E5%8C%97' var cookies = Cookies.withConverter({ read: function (value, name) { if (name === 'escaped') { return unescape(value) } // 对于所有其他cookie,返回默认值 return Cookies.converter.read(value, name) } }) cookies.get('escaped') // 北 cookies.get('default') // 北 cookies.get() // { escaped: '北', default: '北' }写入 创建重写默认编码实现的api的新实例: Cookies.withConverter({ write: function (value, name) { return value.toUpperCase() } })