记录发布的第一个javascript项目及遇到的一些问题

字数 1176阅读 70

前言

在去年12月份刚开始学习编程的时候,看到这样一句话:“初学者不要写博客,要多记笔记。”
细想言之有理,便没有申请博客,开始在Onenote上记录各种网上复制下来的笔记。

然而过了没多久,又看到这么一句话:“好的代码就相当于笔记。”
细想亦言之有理,便停了onenote的笔记,导致在这半年多的时间里除了代码基本上没有写过些什么。

而在前两天,发布了自己的第一个npm包之后,觉得似乎应该记录下自己编写代码和发布包的过程中遇到的问题,顺便发布项目Demo,
便花一天时间开始一步步折腾搞定github pages,hexo,和域名之类。

项目预览及地址

功能:Youtube之类网站在进行ajax请求时顶部出现的载入条

Demo

Github地址

为什么要写这个项目

之前在一个天气预报App的Demo,搜索然后ajax请求是基本功能,请求过程中就想到了实现载入效果,便在github上寻找开源项目。

因为希望这个天气预报App尽量轻便(使用了React,撇除React-Router及Redux之类),所以放弃一些包含各种载入效果的库,
最后使用了NprogressNanobar两个功能简单的库。

这俩个库相比较,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函数,lodashunderscore也定义了合并函数,另外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;

如果有兴趣的话可以参考这里看更多缓动函数资料

如果你对该项目有兴趣,欢迎你贡献代码。

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
  • 都说一般的产品分析很没用,效率低。 别人浏览后获取信息量少。 最好的产品分析是提出有建设性的意见。 找出产品间质的...
  • 帮哥哥复习语文,有个造句:不仅...而且...,我说这个简单,小明是个多才多艺的人,不仅会画画,而且会唱歌,哥哥说...
  • 爱他的时候,他是你的全世界。你会和他分享你所有的事情,告诉他你每时每刻的心情。初尝爱情的甜蜜,沉浸在你侬我侬的柔情...
  • 抱歉,我的时间也很宝贵 时间即是金钱,这个道理很多人都懂,但只限于用在自己身上,反而对熟人朋友倒是慷慨大方多了。 ...