背景
漫长的开发周期结束了,又到了一年为数不多的闲暇时间,早上刚到公司,先打开早餐,然后打开某些上班的时候不能打开的小网站,左手拿着包子,右手握着鼠标,随意的滑动。啪的一下,鼠标点了下去,一点淡蓝色的波纹随着点击的位置缓缓打开,我惊呼:卧槽,好**炫酷!
安排
说干就干,包子就着豆浆一下子灌进了口里面,强行咽下去,思考原理
这个波纹说白了就是个圆,从很小的一个点慢慢放大,同时透明度不断变小,直到为0,由于是固定在父元素的,所以父元素需要设置position:relative,目标圆获取点击的位置,通过position:absolute进行定位,top,left获取位置,最后进行动画
干!
准备阶段
首先创建一个dom
<style>
.box {
width: 400px;
height: 100px;
background-color: bisque;
}
</style>
<div class="box"></div>
添加dom
我们需要添加两块dom,一块用于覆盖在box上面,作为触发事件的target,一个就是那个小圆了
window.onload = () => {
styleDirective(box, { value: 'red' })
}
const box = document.querySelector('.box')
const styleDirective = (el, bind) => {
const color = bind.value
const w = el.getBoundingClientRect().width
const h = el.getBoundingClientRect().height
el.style.position = 'relative'
el.style.overflow = 'hidden'
const outBox = getOutBoxDom(w, h)
const BoLang = getBoLangDom(color)
outBox.appendChild(BoLang)
el.appendChild(outBox)
}
外层覆盖的dom 定位到box的上面。
const getOutBoxDom = (width, height) => {
let div = document.createElement('div')
div.style.width = width + 'px'
div.style.height = height + 'px'
div.style.position = 'absolute'
div.style.top = '0'
div.style.left = '0'
div.style.overflow = 'hidden'
div.style.zIndex = '1'
return div
}
小圆的dom添加
const getBoLangDom = color => {
let div = document.createElement('div')
div.style.width = '10px'
div.style.height = '10px'
div.style.zIndex = '-1'
div.style.opacity = '0'
div.style.borderRadius = '10px'
div.style.backgroundColor = color || 'red'
div.style.position = 'absolute'
div.style.top = '0'
div.style.left = '0'
return div
}
给box添加事件
const styleDirective = (el, bind) => {
const color = bind.value
const w = el.getBoundingClientRect().width
const h = el.getBoundingClientRect().height
el.style.position = 'relative'
el.style.overflow = 'hidden'
const outBox = getOutBoxDom(w, h)
const BoLang = getBoLangDom(color)
outBox.appendChild(BoLang)
el.appendChild(outBox)
el.addEventListener('mousedown', e => {
const x = e.offsetX
const y = e.offsetY
// 截流 单位时间只执行一次
jieliuUtil(
() => changeLocation(BoLang, y, x),
1000,
() => {
// 截流的回调函数,截流执行完之后,执行回调函数,删除动画,小圆不展示
BoLang.style.animation = ''
BoLang.style.display = 'none'
}
)
})
}
添加动画
// 动画1s完成
const changeLocation = (div, top, left) => {
// 小圆定位,并且展示
div.style.display = 'block'
div.style.top = top + 'px'
div.style.left = left + 'px'
let style = document.styleSheets[0]
style.insertRule(`@keyframes secondrotate
{
0%{
z-index: 1;
opacity:1;
transform:scale(1)
}
100%
{transform:scale(100); opacity:0;}
}`)
div.style.animation = `secondrotate 1s ease`
}
截流函数
const jieliu = () => {
let ifNext = true
return (cb, time, callBack) => {
if (ifNext) {
ifNext = false
cb()
setTimeout(() => {
callBack && callBack()
ifNext = true
}, time)
}
}
}
const jieliuUtil = jieliu()
最终效果
vue打包成指令
const styleDirective = (el: HTMLDivElement, bind: DirectiveBinding) => {
const color = bind.value
const w = el.getBoundingClientRect().width
const h = el.getBoundingClientRect().height
el.style.position = 'relative'
el.style.overflow = 'hidden'
const outBox = getOutBoxDom(w, h)
const BoLang = getBoLangDom(color)
outBox.appendChild(BoLang)
el.appendChild(outBox)
el.addEventListener(
'mousedown',
e => {
// outBox.style.zIndex = '1'
const x = e.offsetX
const y = e.offsetY
jieliuUtil(
() => changeLocation(BoLang, y, x),
1000,
() => {
BoLang.style.animation = ''
BoLang.style.display = 'none'
}
)
},
true
)
}
const changeLocation = (div: HTMLDivElement, top: number, left: number) => {
div.style.display = 'block'
div.style.top = top + 'px'
div.style.left = left + 'px'
let style = document.styleSheets[0]
style.insertRule(`@keyframes secondrotate
{
0%{
z-index: 1;
opacity:1;
transform:scale(1)
}
100%
{transform:scale(120); opacity:0;}
}`)
div.style.animation = `secondrotate 1s ease`
}
const getBoLangDom = (color: string) => {
let div = document.createElement('div')
div.style.width = '10px'
div.style.height = '10px'
div.style.zIndex = '-1'
div.style.opacity = '0'
div.style.borderRadius = '10px'
div.style.backgroundColor = color || 'red'
;(div.style.position = 'absolute'), (div.style.top = '0')
div.style.left = '0'
return div
}
const getOutBoxDom = (width: number, height: number) => {
let div = document.createElement('div')
div.style.width = width + 'px'
div.style.height = height + 'px'
div.style.position = 'absolute'
div.style.top = '0'
div.style.left = '0'
div.style.overflow = 'hidden'
div.style.zIndex = '1'
return div
}
const jieliu = () => {
let ifNext = true
return (cb: Function, time: number, callBack?: Function) => {
if (ifNext) {
ifNext = false
cb()
setTimeout(() => {
callBack && callBack()
ifNext = true
}, time)
}
}
}
const jieliuUtil = jieliu()
export default styleDirective
注册
const app = createApp(App)
.use(store)
.use(live2d)
.use(router)
app.directive('bolang', styleDirective)
使用
<div class="advice" v-bolang="'#aaa'"></div>
结尾
中午了,又是忙碌的一早上,早餐吃的有点多,午餐只加1个鸡腿好了
注:那个小网站其实是我的博客