给博客添加自定义的通知悬浮窗
发表于更新于
字数总计2.2k阅读时长9分钟阅读量
点击查看更新记录
- 悬浮窗允许自定义位置(左上、左下、右上、右下)
- 修改函数参数格式
- 修改悬浮窗显示顺序
- 修改悬浮窗ID的存储方式
- 移动端按钮描述文本改为常显
预览
点击下方按钮就可以查看带按钮的悬浮窗的样式了
点击触发特性
- 打开后超时自动关闭
- 附加关闭按钮,可手动关闭
- 鼠标悬浮在悬浮窗上会重置自动关闭计时器
- 附带全套动画
- 可以在悬浮窗上添加一个附带文字描述的按钮并自定义点击功能
- 可以同时显示多个悬浮窗(有上限,超过上限会直接关闭额外的悬浮窗)
- 适配黑白两色主题
- 好玩还好看
教程
修改[theme]/layout/includes/layout.pug
(注意缩进):
上面这个文件是butterfly
主题用户的路径,其它主题的用户需要自行判断代码插入位置。实际上这个代码只需要保证每个页面都存在且存在于display
不是none
的元素中即可,插入的位置不固定,可以变动。
1 2 3 4 5 6 7 8 9 10 | body + .float-box.left.top + .float-box.left.bottom + .float-box.right.top + .float-box.right.bottom
if theme.preloader !=partial('includes/loading/loading', {}, {cache: true}) if theme.background #web_bg
|
首先我们新建一个JS文件,并写入以下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
document.addEventListener( 'DOMContentLoaded', () => btf.snackbarShow = (text, time = 3500) => kms.pushInfo({text, time}, null) )
const kms = {
isMobile: 'ontouchstart' in document.documentElement, _cache: { winCode: 0 }, pushInfo: (optional, button = null) => { let {text, time, left, bottom} = optional if (typeof optional === 'string') text = optional if (!time) time = 3500 const externalApi = {} const id = kms._cache.winCode++ const cardID = `float-win-${id}` const actionID = `float-action-${id}` const exitID = `float-exit-${id}` const closeWin = id => { const div = document.getElementById(id) if (div) { const {classList, style} = div if (classList.contains('close')) return if (externalApi.onclose) externalApi.onclose() style.maxHeight = `${div.clientHeight + 10}px` classList.add('close') setTimeout(() => div.remove(), 1000) setTimeout(() => { style.maxHeight = style.marginBottom = '0' classList.remove('show') }, 25) } } const closeRedundantWin = className => { const list = document.querySelector(`.float-box${className}`).children const count = list.length - 3 for (let k = 0, i = 0; k < count && i !== list.length; ++i) { closeWin(list[i].id) ++k } } const buildHTML = id => { const buttonDesc = (button && button.desc) ? `<div class="descr"><p ${kms.isMobile ? 'class="open"' : ''}>${button.desc}</p></div>` : '' return `<div class="float-win ${left ? 'left' : 'right'} ${bottom ? 'bottom' : 'top'} ${button ? 'click' : '' }" id="${cardID}" move="0" style="z-index:${id};"><button class="exit" id="${exitID}"><i class="iconfont icon-close"></i></button><div class="text">${text}</div>${button ? '<div class="select"><button class="action" id="' + actionID + '">' + (button.icon ? '<i class="' + button.icon + '">' : '') + '</i><p class="text">' + button.text + `</p></button>${buttonDesc}` : ''}</div></div>` } const className = `${left ? '.left' : '.right'}${bottom ? '.bottom' : '.top'}` document.querySelector(`.float-box${className}`).insertAdjacentHTML('beforeend', buildHTML(id)) const cardDiv = document.getElementById(cardID) const actionButton = document.getElementById(actionID) const exitButton = document.getElementById(exitID) if (actionButton) { actionButton.onclick = () => { if (button.onclick) button.onclick() closeWin(cardID) } } exitButton.onclick = () => closeWin(cardID) cardDiv.onmouseover = () => cardDiv.setAttribute('over', '1') cardDiv.onmouseleave = () => cardDiv.removeAttribute('over') closeRedundantWin(className) setTimeout(() => cardDiv.classList.add('show'), 25) let age = 0 const task = setInterval(() => { const win = document.getElementById(cardID) if (win) { if (win.hasAttribute('over')) return age = 0 age += 100 if (age < time) return } clearInterval(task) closeWin(cardID) }, 100) externalApi.close = () => closeWin(cardID) return externalApi } }
|
接着我们新建一个styl
文件,并写入以下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | :root --km-float-win-bg whitesmoke --km-button-dark-hover #0084ff --km-button-dark-bg #66ccff --km-button-font white --km-button-shadow hsla(0, 0%, 22%, 0.2)
[data-theme="dark"] --km-float-win-bg #141414 --km-button-dark-hover #49505d --km-button-dark-bg #1f1f1f --km-button-font #66ccff --km-button-shadow #212121
.float-box position fixed width 310px display flex flex-wrap wrap flex-direction column z-index 120
&.left left 20px align-items flex-start
&.right right 20px align-items flex-end
&.top top 70px
&.bottom bottom 35px
.float-win position relative overflow hidden width 100% background-color var(--km-float-win-bg) color var(--font-color) border-radius 15px box-shadow 2px 2px 5px 3px var(--km-button-shadow) transition all 1s opacity 0 margin-bottom 15px
&.show opacity 1
&.left left -400px
&.show left 0
&.right right -400px
&.show right 0
@media screen and (max-width: 550px) and (min-width: 250px) width calc(65%)
@media screen and (max-width: 250px) width calc(80%)
& > .select > .descr color transparent
& > i margin-left 10px
& > .text position relative max-width 84% margin-left 8% text-align center margin-top 25px margin-bottom 20px word-break break-word word-wrap break-word
img max-width 90% object-fit contain border-radius 10px
&.click & > .text margin-bottom 0
& > .exit position absolute color var(--text-highlight-color) right 0 top 0 width 30px height 30px transition all 0.5s
&:hover color deepskyblue transform rotate(90deg)
i font-size 1em
& > .select position relative text-align right margin-bottom 10px height 30px
& > .descr position relative margin-top 0 margin-right 6px display inline-block height 30px overflow hidden
& > p position relative margin 0 height 30px z-index 121 transition all 1s
&:not(.open) left 100px opacity 0
& > .action display inline-block position relative float right z-index 121 height 30px margin-right 10px background-color var(--km-button-dark-bg) color var(--km-button-font) box-shadow 1px 1px 2px var(--km-button-shadow) padding 10px border-radius 7px transition all 0.5s
& > .text display inline position relative bottom 4px font-weight bold
& > i position relative bottom 4px margin-right 5px
&:hover background-color var(--km-button-dark-hover)
& + .descr > p left 0 opacity 1
[data-theme='light'] .float-win box-shadow 3px 4px 6px 5px var(--km-button-shadow)
|
最后在合适的地方引入刚刚编写的文件即可,具体用法我就说一点,注释里已经写的很详细了。
在这里写的几个函数中,所有以下划线(_
)开头的函数和变量均为内部调用,外部不应当调用。至于为什么外露出来,一是JS没有像其它语言那样的可见域控制(也有可能是有但是我不知道),二是方便调试。
笔记
虽然这个悬浮窗实现并不复杂,不过对于这种基本不懂前端开发,只会照葫芦画瓢的人来说还是比较有难度的,用了一下午才把悬浮窗弄好。 开发中也遇到了不少问题(不然也不会花这么长时间),难为我最长时间的就是悬浮窗内部元素的排版了,光想办法把按钮放在右边,提示文本放在左边就花费了至少一个小时。
不过通过这次实践,我也学到了不少东西,比如了解了更多的CSS 选择器
、了解了setInterval
的用法……
这的确是应了那句老话:“实践出真知。”多实践就能不断巩固已有的知识,同时学习新的知识。
下面的话主要实给同学看的
现在有不少人都称呼我为“大佬”,实际上我也就是在代码编写方面有稍微多一点的经验,看了我上面的话应该也不难发现,前端开发我就是个渣渣。实际上不仅是我,任何“大佬”都不会擅长所有领域。同时大佬也一定不是一天养成的,现在的我前端是渣渣,说不定以后我就是前端的大牛了呢?
所以也不要因为身边有那么几个水平超出自己非常多的人就自暴自弃,只要自己不放弃就有赶上甚至超越那些人的希望,一旦放弃就毫无希望了。
参考资料
创作不易,扫描下方打赏二维码支持一下吧ヾ(≧▽≦*)o
给博客添加自定义的通知悬浮窗空 梦 | 山岳库博
更新于 2023-02-03
发布于 2022-05-25