热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

jQuery源码阅读笔记动画(4)

前言在此先对前几篇所写的代码做一下整理,回头看还是挺糟糕的,此篇做一个梳理,也可以说是重写filterAnimateArgsÿ

前言

在此先对前几篇所写的代码做一下整理,回头看还是挺糟糕的,此篇做一个梳理,也可以说是重写

filterAnimateArgs()

内部自用,处理动画函数的参数,一个抽离的函数

/** * 处理参数 * @param prop 动画目标值 * @param speed 动画时间 * @param easing 动画运动曲线 * @param callback 回调函数 * @returns {{prop, callback, easing: string, speed: number}} */
const filterAnimateArgs = (prop, speed, easing, callback) => { $.each(prop, (key, value) => { //检查当前值是否为纯数字,且可以增补CSS单位 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(); // $(target).css(mountedProp); //固定样式的同时,提取当前盒子状态 //防止并没有传递函数参数 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, {//显示传入null是为了避免误判给拦截下来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", ]); } //新加入属性,记录最初始的style属性 //此处记录的是属性节点而非对象 this._initialStyle = this.getAttribute("style"); //如果是因为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()这样的存在时,会更麻烦


推荐阅读
author-avatar
手机用户2502912857
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有