前言
在此先对前几篇所写的代码做一下整理,回头看还是挺糟糕的,此篇做一个梳理,也可以说是重写
filterAnimateArgs()
内部自用,处理动画函数的参数,一个抽离的函数
const filterAnimateArgs = (prop, speed, easing, callback) => { $.each(prop, (key, value) => { if ( (typeof value === "number" || typeof value === "string") && !isNaN(value - parseFloat(value)) && /px$/.test(getComputedStyle(document.body)[key]) ) { value += "px"; } prop[key] = value; }); if (typeof speed === "function") { [speed, easing, callback] = [400, "ease", speed]; } else if (typeof easing === "function") { [easing, callback] = ["ease", easing]; } return { prop, speed, easing, callback, };
};
play()
内部函数,核心
- 需要一个函数用于递归,但又不想放外面,因此将其设计成了闭包
- 过滤函数迁移到了动画入口,让这边精简些
- 新加入一个生命周期钩子
mounted
,在动画结束时执行,样式定格的事交给了这个函数 - 新属性,用于区分当前动画是属于隐藏还是显示形动画,拦截一部分没必要执行的动画
- 将
target
从配置当中抽离,对象很忙
const play = (() => { const run = (target, { isHide = false, prop, speed = 400, easing, callback, mounted, beforePlay = () => {} }) => { const state = $(target).css("display"); if ((isHide === true && state === "none") || (isHide === false && state !== "none")) { target._animateQueue.shift(); if (target._animateQueue.length !== 0) { run(target, target._animateQueue[0]); } return; } const animation = new Animation(new KeyframeEffect(target, prop, { easing, duration: speed, })); animation.onfinish = () => { mounted(); if (typeof callback === "function") { callback.bind(target)(); } if ($(target).css("display") !== "none") { target._styleState = null; } target._animateQueue.shift(); if (target._animateQueue.length !== 0) { run(target, target._animateQueue[0]); } }; beforePlay(); animation.play(); }; return (target, config) => { if (target._animateQueue == null) { target._animateQueue = []; } target._animateQueue.push(config); if (target._animateQueue.length === 1) { run(target, target._animateQueue[0]); } };
})();
因为参数在入口函数这边已经不重要了,对此直接改为收集形式,直接塞给filterAnimateArgs()
处理
animate()
const animate = function(prop, ...rest) { if ($.type(prop) !== "object") return this; return this.each(function() { play(this, {isHide: null,mounted: () => { $(this).css(prop); }, ...filterAnimateArgs(prop, ...rest), }); });
};
hide()
const hide = function(...rest) { return this.each(function() { play(this, { isHide: true, mounted: () => { this.style.display = "none"; }, beforePlay: () => { if (this._styleState == null) { this._styleState = $(this).css([ "height", "width", "margin", "padding", "overflow", "opacity", ]); } }, ...filterAnimateArgs({ overflow: "hidden", opacity: 0, height: 0, width: 0, margin: 0, padding: 0, }, ...rest), }); });
};
show()
显示动画做的事要更多
const show = function(...rest) { return this.each(function() { play(this, { mounted: () => { this.style = this._initialStyle; }, beforePlay: () => { if (this._styleState == null) { this._styleState = $(this).css([ "overflow", "opacity", "height", "width", "margin", "padding", ]); } this._initialStyle = this.getAttribute("style"); if (this.style.display === "none") { this._initialStyle = this._initialStyle.replace(/\s*display: none;\s*/, ""); } else { this._initialStyle += "display:block"; }$(this).css({ overflow: "hidden", opacity: 0, height: 0, width: 0, margin: 0, padding: 0, display: "block", }); }, ...filterAnimateArgs(this._styleState, ...rest), }); });
};
在此基础之上,一个简单的toggle()
也可以完成了
toggle()
- 切换,不能直接看当前对象的显示状态,动画是异步的
- 当有动画队列存在的时候,查看队尾动画表现即可,也是
isHead
属性的一个效果
const toggle = function(...rest) { return this.each(function() { const $this = $(this); let isHide = $this.css("display") === "none"; if (this._animateQueue && this._animateQueue.length !== 0) { if (this._animateQueue[this._animateQueue.length - 1].isHide === true) { isHide = true; } }if (isHide) { $this.show(...rest); } else { $this.hide(...rest); }});
};
对于当前效果来说,还未完成,就已经感受到深深的恶意了
当加入stop()
这样的存在时,会更麻烦