前言
在去年12月份刚开始学习编程的时候,看到这样一句话:“初学者不要写博客,要多记笔记。”
细想言之有理,便没有申请博客,开始在Onenote上记录各种网上复制下来的笔记。
然而过了没多久,又看到这么一句话:“好的代码就相当于笔记。”
细想亦言之有理,便停了onenote的笔记,导致在这半年多的时间里除了代码基本上没有写过些什么。
而在前两天,发布了自己的第一个npm包之后,觉得似乎应该记录下自己编写代码和发布包的过程中遇到的问题,顺便发布项目Demo,
便花一天时间开始一步步折腾搞定github pages,hexo,和域名之类。
项目预览及地址
功能:Youtube之类网站在进行ajax请求时顶部出现的载入条
为什么要写这个项目
之前在一个天气预报App的Demo,搜索然后ajax请求是基本功能,请求过程中就想到了实现载入效果,便在github上寻找开源项目。
因为希望这个天气预报App尽量轻便(使用了React,撇除React-Router及Redux之类),所以放弃一些包含各种载入效果的库,
最后使用了Nprogress和Nanobar两个功能简单的库。
这俩个库相比较,Nanobar体积更小,功能更简单(只有一个go函数),Nprogress则更漂亮(有一个peg元素,制造了box-shadow阴影),
可以调用的函数也更多(有渐渐载入的效果)。
但是这两个库年代都比较久远,都没有选择构造函数来创建对象,所有函数都没有绑在原型链上(当然对于这种功能简单的库来说没有什么大的影响)、
都是使用setTimeout方式实现动画(当然对这种简单的动画也没有什么大的影响)、都需要引入css文件使用transform作为实现动画效果的辅助。
最终在参考了两个库的源代码后决定自己写一个。
在项目中遇到的问题和解决方法
构造函数、打包工具
使用ES6 class
构造对象,使用rollup
打包成umd
形式的函数。
rollup
在打包之后不是生成传统的Object.prototype
形式,而是定义了一个createClass
函数
var createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
动画函数
使用window.requestAnimationFrame
来进行动画,使用了一般通用的polyfill
const rAF = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) { window.setTimeout(callback, 1000 / 60); };
setTimeout
函数间隔为1000 / 60以满足帧数。
合并对象
在库中使用了Object.assign
函数合并对象,该函数有浏览器兼容问题,需要额外使用babel
插件进行编译。
一般比较大的库都会定义自己的merge
函数,lodash
或underscore
也定义了合并函数,另外ES6
有实验性质的展开对象合并的方法,
这应该也是极为遥远的将来展开和合并对象的标准方法
限制对象属性的值
渲染进度条最重要的是确定什么时候渲染完成,按照逻辑来说进度条达到最大值的时候就应该淡出,
用代码表示就应该是if (this.barWidth === this.maxWidth) this.stop();
,
但实际运行中发现由于递增barWidth
使用的是缓动函数,
所以barWidth
未必会接触到maxBarWitdh
,可能上一帧在99.8%,下一帧却出现在100.2%。
nanobar
解决这个问题是直接重新创建实例
if (p >= 100) {
init()
}
这个方式可能有点太粗暴了。而我最终选择的是Object.defineProperty
方法:
Object.defineProperties(this, {
// constant max width
'maxWidth': { value: 100 },
// limit bar width
'barWidth': {
get() { return barWidth; },
set(value) {
if (value <= 0) value = 0;
// set to 0 if width touch 100%
if (value >= 100) value = 100;
barWidth = value;
}
}
});
一旦barWidht
超过100,就会被设置成100,进而出发stop
函数。
当然这个方法是否有什么弊端我还没有研究过。
youtube的进度条在淡出的时候是定格在101%的。
Promise
原本处理进度条淡出后移除DOM是选择了Promise
_fadeOut(el) {
if (!el.style.opacity) el.style.opacity = 1;
el.style.opacity -= 0.1;
if (el.style.opacity > 0) rAF(this._fadeOut.bind(this, el));
return Promise.resolve();
}
最终还是因为浏览器兼容问题选择callback
_fadeOut(el, callback) {
el.style.opacity -= 0.1;
if (el.style.opacity > 0) {
rAF(this._fadeOut.bind(this, el, callback));
} else {
setTimeout(() => { callback(); }, 300);
}
}
ease function
第一次接触了javascript的缓动函数,看了很多资料还是一知半解,项目里使用的欢动函数属于比较简单的
const easing = (t, b, c, d) => c * t / d + b;
如果有兴趣的话可以参考这里看更多缓动函数资料
如果你对该项目有兴趣,欢迎你贡献代码。