Open bibi7 opened 5 years ago
去年在用vue写玩具的时候遇到了toast相关需求链接自取,当时也没怎么考虑直接做了个组件的形式。如下:
<template lang="html"> <!-- show置为block激活animation,animationEnd时display置为none fade与faded用于animation尚未结束时重新开始 --> <div class="popBox" :class="[show ? 'show': '', fade ? 'fade' : 'faded']" @animationend="animationend" ref="popBox"> <p>{{clickText}}</p> </div> </template> <script> export default { name: 'popBox', data() { return { show: false, fade: true, } }, props: { clickText: { type: String, default: 'some info', } }, methods: { animationend() { this.show = false; } }, watch: { clickText(newValue, oldValue) { if(this.$refs.popBox.classList.contains('fade')) { this.fade = false } else { this.fade = true } this.show = true; } }, } </script>
<style lang="less" scoped> .popBox { position: fixed; border-radius: 10px; display: none; padding: 20px; top: 50%; left: 50%; opacity: .8; background-color: #000; transform: translate(-50%, -50%); p { font-size: .9rem; color: #fff; } &.fade { animation: fade 2s linear; animation-fill-mode: forwards; } &.faded { animation: faded 2s linear; animation-fill-mode: forwards; } &.show { display: block } @keyframes faded { 0% { opacity: .8 } 50% { opacity: .8 } 80% { opacity: .5 } 100% { opacity: 0 } } @keyframes fade { 0% { opacity: .8 } 50% { opacity: .8 } 80% { opacity: .5 } 100% { opacity: 0 } } } </style>
主要的卡点在于多次触发animation,清空上一次未完成的animation。 虽然当时觉得实在太蠢后期优化,但是由于是在太懒的缘故,这篇hape代码现在还挂在blog上。。。🌚🌚 都说自己的代码只有自己能看得懂,但是当我好几个月后再回来瞅一下还是觉得: 这也太难懂了。。🤔相对于一个toast真的没必要这么绕🐴
算了不拖延了,赶紧搞个toast2.0🌝
这东西其实不应该做成类似组件的方式,最好还是做成工具类相关(再次对不起我第一次真的没有细想很多),整个成一个utils是坠好的!
期望:
import { toast } from 'xxx/utils' toast('some info')
开工了开工了!比如有以下一个toast的div:
<div id="toast" class="toast" @animationend="toastEnd"></div>
animation事件结束后隐藏自己
toastEnd() { let toast = document.querySelector('#toast') toast.classList.remove('show') }
对外抛出toast工具函数
export function toast(msg) { let toast = document.querySelector('#toast') toast.innerHTML = msg; document.querySelector("#toast").className = "toast"; document.querySelector("#toast").className = "toast show"; }
toast函数中本来的设想就是,取消show的激活状态,这样display重新为none了,然后再次添加show的class,置于show中的animation属性就会再次出发,但是实测不太行。。。 查了一圈,发现都是基于setTimeout来实现,怎么说呢,有点浮于表面的解决方法。 看的比较多的几种:
webkitAnimationPlayState
继续找一下有没有别的实现?🤔🤔
找了一圈,终于找到了看起来能从根源上解决的api:window.requestAnimationFrame
window.requestAnimationFrame
我们来瞅瞅MDN的解释:
window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行
看起来okk,就是他了!
export function toast(msg) { let toast = document.querySelector('#toast') toast.innerHTML = msg; document.querySelector("#toast").className = "toast"; window.requestAnimationFrame(() => { document.querySelector("#toast").className = "toast show"; }); }
来说一下主要涉及到的步骤:
document.querySelector("#toast").className = "toast"
看到一个其他的类似方案,在两次class的增删中添加如下:
document.querySelector("#toast").className = "toast" //重新设置一次offsetWidth element.offsetWidth = element.offsetWidth document.querySelector("#toast").className = "toast show"
其实和requestAnimationFrame原理是一样的,根本原因是,一个animation只会在挂载到了dom上后触发一次,如何激活第二次甚至多次,实质上你要考虑的其实就是:
requestAnimationFrame
你要如何让这个dom发生重绘或者重排
offsetWidth发生了重绘,所以重新计算后触发animation,requestAnimationFrame手动触发了一次重绘。包括remove节点后再次append,其实也就是触发一次重排。
offsetWidth
当然,这个api主要还是逐帧调用,每秒60次回调从而实现一些比较复杂的自定义动画。回调函数执行次数通常与浏览器刷新率一致,而且性能优秀。当成一个主动触发重绘的语法糖毕竟用途比较少。
其他的一些良好的业内实践: css-tricks
去年在用vue写玩具的时候遇到了toast相关需求链接自取,当时也没怎么考虑直接做了个组件的形式。如下:
主要的卡点在于多次触发animation,清空上一次未完成的animation。 虽然当时觉得实在太蠢后期优化,但是由于是在太懒的缘故,这篇hape代码现在还挂在blog上。。。🌚🌚 都说自己的代码只有自己能看得懂,但是当我好几个月后再回来瞅一下还是觉得: 这也太难懂了。。🤔相对于一个toast真的没必要这么绕🐴
算了不拖延了,赶紧搞个toast2.0🌝
toast2.0
这东西其实不应该做成类似组件的方式,最好还是做成工具类相关(再次对不起我第一次真的没有细想很多),整个成一个utils是坠好的!
期望:
开工了开工了!比如有以下一个toast的div:
animation事件结束后隐藏自己
对外抛出toast工具函数
toast函数中本来的设想就是,取消show的激活状态,这样display重新为none了,然后再次添加show的class,置于show中的animation属性就会再次出发,但是实测不太行。。。 查了一圈,发现都是基于setTimeout来实现,怎么说呢,有点浮于表面的解决方法。 看的比较多的几种:
webkitAnimationPlayState
,讲真不是很了解继续找一下有没有别的实现?🤔🤔
找了一圈,终于找到了看起来能从根源上解决的api:
window.requestAnimationFrame
我们来瞅瞅MDN的解释:
看起来okk,就是他了!
来说一下主要涉及到的步骤:
document.querySelector("#toast").className = "toast"
,去除当前动画window.requestAnimationFrame
告诉浏览器,我希望执行一次动画啦!赶紧给窝重绘看到一个其他的类似方案,在两次class的增删中添加如下:
其实和
requestAnimationFrame
原理是一样的,根本原因是,一个animation只会在挂载到了dom上后触发一次,如何激活第二次甚至多次,实质上你要考虑的其实就是:offsetWidth
发生了重绘,所以重新计算后触发animation,requestAnimationFrame
手动触发了一次重绘。包括remove节点后再次append,其实也就是触发一次重排。当然,这个api主要还是逐帧调用,每秒60次回调从而实现一些比较复杂的自定义动画。回调函数执行次数通常与浏览器刷新率一致,而且性能优秀。当成一个主动触发重绘的语法糖毕竟用途比较少。