JavaScript是本人自学的第一门语言,也是本人目前最喜欢的一门语言。由于是自学,加上没有做过任何项目(只是偶尔自己做一些小效果玩玩),所以水平不咋地,写得不好之处,欢迎各位指正。

前言

在我自学JavaScript的时候,一直想实现类似安卓手机状态栏那种下拉上滑效果。在网上搜索一番后,我知道了jQueryslideUp()slideDown()方法。这两个方法让我很着迷,我迫不及待知道他是如何实现的。可当我尝试在网上寻找答案时,得到答案几乎都是:你去看jQuery源码不就知道了吗?于是我就去看了jQuery源码,但是面对几万行代码,让我望而却步。所以至今我也不知道slideUp()和slideDown()这两个方法在jQuery中到底是怎样实现的。

第一次尝试

在进一步学习JavaScript后,我了解了setTimeoutsetInterval,并知道可以利用它们来逐渐的改变元素的属性,以此达到动画效果。于是我开始着手借助setInterval来实现我自己的下拉上滑效果。让我们以下面的HTML代码为基础,开始聊聊我的第一次实现:

文件名:Silder.html

<!doctype html>
<html>
<head><meta charset="utf-8"/><title>Slider</title>
</head>
<body><button id="btn">Button</button><div id="panel" style="width:600px;height:400px;background:red;"></div><script src="Slider.js"></script>
</body>
</html>

上面代码布局很简单,会显示一个idbtn的按钮;和一个宽为600px,高为400px,背景为红色,idpanel的div。最后我引入了一个文件名为Slider.js的JavaScript文件,这个文件目前还未创建,我将在下面给出。

为了方便描述,我把idbtn的按钮简记为btn,同理也把红色div简记为panel

现在我想实现一个效果:当我点击btn时,如果panel当前可见,就执行一个上滑动画来将它慢慢收起,从而让它变成不可见;如果当前不可见,那就执行一个下拉动画将它慢慢展开,从而让它变成可见。整个流程如下:

Created with Raphaël 2.1.2 开始 点击btn panel是否可见? 上滑动画 结束 下拉动画 yes no

整个流程清楚了,现在就开始写JS代码了,以下是Slider.js中的内容:

window.onload = function() {// 获取btn和panelvar btn   = document.getElementById("btn"),panel = document.getElementById("panel");// 为btn绑定onclick事件btn.onclick = function() {// 通过panel的offsetHeight来判断元素是否可见if (panel.offsetHeight === 0) {// 不可见,调用下拉函数:在300毫秒内下拉slideDown(panel, 300);} else {// 可见,调用上滑函数:在300毫秒内上滑slideUp(panel, 300);}};
};function slideDown(element, time) {// 等待实现
}function slideUp(element, time) {// 等待实现
}

以上代码注释地很清楚,这里不再赘述。我们要注意的是其中有两个等待实现的函数slideDown(element, time)slideUp(element, time) 。这两个函数分别用于执行下滑上拉动画,他们都接受一个Element类型的element和一个Number类型的time为参数,其中element表示要执行动画的元素,time表示执行动画一个需要多少时间(毫秒)。

我们先来实现slideUp(element, time)。我的思路是这样的:用一个setInterval定时器,每隔10毫秒执行一个函数,每次执行这个函数时就把elementheight属性(element.style.height)减去一部分。举个例子,如果我们传入的time参数为500的话,这个函数就会执行(500/10) = 50次。也就是说我们要分50次把element.style.height减为0,那么每次要减去的值就是 element.offsetHeight/50 了(其中element.offsetHeight为元素的高度)。来看看slidUp的实现:

function slideUp(element, time) {// 获取元素总高度var totalHeight = element.offsetHeight;// 定义一个变量保存元素当前高度var currentHeight = totalHeight;// 计算每次减去的值var decrement = totalHeight/ (time/10);// 开始循环定时器var timer = setInterval(function() {// 减去当前高度的一部分currentHeight = currentHeight - decrement;// 把当前高度赋值给height属性element.style.height = currentHeight + "px";// 如果当前高度小于等于0,就关闭定时器if (currentHeight <= 0) {// 关闭定时器clearInterval(timer);// 把元素display设置为noneelement.style.display = "none";// 把元素高度值还原element.style.height = totalHeight + "px";}}, 10);
}
// ...

现在当我们点击btn时,panel就会慢慢被收起。而当我们再次点击btn时,却没有任何反应,因为我们还没有实现slideDown。有了slideUp为基础,slideDown就不算什么难事了,直接上代码:

//...
function slideDown(element, time) {// 获取元素总高度element.style.display = "block";            // 1.显示元素,元素变为可见var totalHeight = element.offsetHeight;     // 2.保存总高度element.style.height = "0px";               // 3.再将元素高度设置为0,元素又变为不可见// 定义一个变量保存元素当前高度var currentHeight = 0;                      // 当前元素高度为0// 计算每次增加的值var increment = totalHeight / (time/10);// 开始循环定时器var timer = setInterval(function () {// 增加一部分高度currentHeight = currentHeight + increment;// 把当前高度赋值给height属性element.style.height = currentHeight + "px";// 如果当前高度大于或等于总高度则关闭定时器if (currentHeight >= totalHeight) {// 关闭定时器clearInterval(timer);// 把高度设置为总高度element.style.height = totalHeight + "px";}}, 10);
}
//...

OK,大功告成。现在来看看效果如何。首先点击btn,我们会看到panel慢慢被收起。等待收起动画完成后,再次点击btnpanel又会被慢慢展开。看起来很不错,达到了我们预期的效果。可是一切真的这么简单吗?请注意,我方才刻意强调了等待收起动画完成后再点击btn,但是如果我们快速连续点击btn又会出现什么情况呢?答案是我们的动画会崩溃!你可以亲自试一试,看看是不是如我所说。

综上所述,我的第一次尝试是失败的,或者说是不完美的。因为当用户快速点击按钮时我们的动画会崩溃,这显然不是用户想要的。

第一次失败的思考

以上动画为什么会失败呢?那是因为我想当然的认为:JavaScript中的定时器会一个接一个的按着顺序执行。因为JavaScript是单线程,所以也这个认为看起来似乎并不是错的。但是,事实证明这是错的,比如从下面的列子中就可以看出:

// test.js
window.onload = function() {timer1();timer2();timer3();
};// 依次弹出 1 2 3
function timer1() {var num = 0;var timer = setInterval(function() {num ++;alert(num);if (num == 3) {clearInterval(timer);}}, 10);
}// 依次弹出 4 5 6
function timer2() {var num = 3;var timer = setInterval(function() {num ++;alert(num);if (num == 6) {clearInterval(timer);}}, 10);
}// 依次弹出 7 8 9
function timer3() {var num = 6;var timer = setInterval(function() {num ++;alert(num);if (num == 9) {clearInterval(timer);}}, 10);
}

从上面代码可以看出,我们使用三个定时器,希望浏览器依次按顺序弹出1-9这9个数字(每隔10毫秒弹出一个)。把Slider.html 中的Slider.js改为test.js,执行以下看看结果。结果我们会发现,弹出的顺序根本毫无规律可言。这也再次说明了,JavaScript中定时器并不会按代码顺序依次执行。至于为什么,我在这里不做深入研究,在此,我们只需记住这个结论即可。

其实,根本不用做上面的实验,我们也能轻易得出这个结论。因为很多网页上不只有一个动画,如果所有的动画都按顺序一个接一个的执行的话,那岂不是说在该网页上同时至多只能有一个动画在执行,与此同时其余动画都是静止的(因为还未轮到它们执行),这显然和我们看到的不一致。

让定时器乖乖就范

既然定时器如此顽皮地不按顺序执行,所以我们必需得想个法子让它乖乖就范。

要让定时器按顺序执行,那就必需使用回调。也就是说,在一个函数执行函数完成后去调用另一个函数。具体到刚刚那个test.js,要让timer1()timer2()timer3()三个定时器依次执行。我们可以在timer1()执行完成后主动去调用timer2()timer2()完成后又主动去调用timer3()。这样一来,我们只需执行timer1(),三个定时器就都会被依次执行,就像下面一样:

// test.js
window.onload = function() {timer1();
};// 依次弹出 1 2 3
function timer1() {var num = 0;var timer = setInterval(function() {num ++;alert(num);if (num == 3) {clearInterval(timer);// 主动调用timer2();timer2();}}, 10);
}// 依次弹出 4 5 6
function timer2() {var num = 3;var timer = setInterval(function() {num ++;alert(num);if (num == 6) {clearInterval(timer);// 主动调用 timer3();timer3();}}, 10);
}// 依次弹出 7 8 9
function timer3() {var num = 6;var timer = setInterval(function() {num ++;alert(num);if (num == 9) {clearInterval(timer);}}, 10);
}

运行上面的代码,我们发现,浏览器会像我们期待的那样依次按顺序弹出9个数字。

更灵活的管理方案

虽然上面代码会让定时器依次执行,但这种方式并不灵活。想象一下,如果我们要添加一个新的定时器timer4(),我们就必须在timer3()中去调用它。如果要添加1000个呢?那工作量会相当可观。

现在我们用一个更为灵活的方案来管理定时器的执行。我们可以把所有要按一定顺序执行的定时器都保存在一个数组中,然后把这个数组当成一个队列使用,最后按顺序一个接一个的执行队列里面的定时器。

提示:JavaScript中的数组本身就可以当作队列使用(参见数组的shift()方法和push()方法),所以我们不要实现自己的队列数据结构。

在此,我们创建一个TimerManager对象来管理动画队列。其代码如下:

// test.js
// ...
// 声明TimerManager
var TimerManager = {};TimerManager.timers = [];       // 用于保存定时器的数组(队列)
TimerManager.isFiring = false;  // 用于记录当前是否有定时器在执行// 一个用于添加定时器的方法
TimerManager.add = function(timer) {// 把定时器存入队列this.timers.push(timer);// 调用fire()执行队列中第一个定时器this.fire();
};// 一个用于执行队列中第一个定时器的方法
TimerManager.fire = function() {if ( !this.isFiring ) { // 如果当前没有定时器在执行var firstTimer = this.timers.shift();  // 取出队列中的第一项if (firstTimer) { // 如果第一个定时器存在, 就执行// 设置isFiring为true表明当前有定时器在执行this.isFiring = true; firstTimer();}}
};// 一个用于执行下一个定时器的方法
TimerManager.next = function() {// 先把isFiring设置为false,表明当前没有定时器在执行this.isFiring = false;// 调用fire()执行第一个定时器this.fire();
};// ...

TimerManager一共有两个属性,timersisFiring;还有三个方法分别是add(timer)fire()next()。其中我们常用的是add(timer)和next()。

fire()是一个用来执行队列中第一个定时器的内部方法,执行的时候,它会先判断当前是否有定时器在执行,如果没有的话,它便会把第一个定时器从队列中取出来并立即执行;如果当前有定时器正在执行,它就什么都不做。

next()是一个用来执行队列中下一个动画的方法,它应该在定时器结束的时候被调用,以执行队列中下一个定时器。这也表明了,我们在写定时器时,必须在定时器结束时主动调用这个方法

add(timer)用于向队列中添加定时器。timer即要添加的函数名。

前面说过,必须在定时器结束时调用next()方法。所以我们把之前的定时器(timer1,timer2,timer3)都修改一下,改成下面这样:

// test.js
// ...
// 依次弹出 1 2 3
function timer1() {var num = 0;var timer = setInterval(function() {num ++;alert(num);if (num == 3) {clearInterval(timer);// 调用next();TimerManager.next();}}, 10);
}// 依次弹出 4 5 6
function timer2() {var num = 3;var timer = setInterval(function() {num ++;alert(num);if (num == 6) {clearInterval(timer);// 调用next();TimerManager.next();}}, 10);
}// 依次弹出 7 8 9
function timer3() {var num = 6;var timer = setInterval(function() {num ++;alert(num);if (num == 9) {clearInterval(timer);// 调用next();TimerManager.next();}}, 10);
}
// ... 

完成以上修改后,我就可以调用add(timer)方法把所有定时器添加进去。因为在add(timer)内部会主动调用fire()来执行队列中的第一个定时器,所以我们不用手动调用fire()。我们要做的只是把定时器添加进去,其他什么也不用做,定时器就会乖乖地排着队去会执行。以下便是test.js修改后全部代码:

// test.js// 声明TimerManager
var TimerManager = {};TimerManager.timers = [];       // 用于保存定时器的数组(队列)
TimerManager.isFiring = false;  // 用于记录当前是否有定时器在执行// 一个用于添加定时器的方法
TimerManager.add = function(timer) {// 把定时器存入队列this.timers.push(timer);// 调用fire()执行队列中第一个定时器this.fire();
};// 一个用于执行队列中第一个定时器的方法
TimerManager.fire = function() {if ( !this.isFiring ) { // 如果当前没有定时器在执行var firstTimer = this.timers.shift();  // 取出队列中的第一项if (firstTimer) { // 如果第一个定时器存在, 就执行// 设置isFiring为true表明当前有定时器在执行this.isFiring = true; firstTimer();}}
};// 一个用于执行下一个定时器的方法
TimerManager.next = function() {// 先把isFiring设置为false,表明当前没有定时器在执行this.isFiring = false;// 调用fire()执行第一个定时器this.fire();
};window.onload = function() {// 调用add(timer)添加定时器TimerManager.add(timer1);TimerManager.add(timer2);TimerManager.add(timer3);
};// 依次弹出 1 2 3
function timer1() {var num = 0;var timer = setInterval(function() {num ++;alert(num);if (num == 3) {clearInterval(timer);// 调用next();TimerManager.next();}}, 10);
}// 依次弹出 4 5 6
function timer2() {var num = 3;var timer = setInterval(function() {num ++;alert(num);if (num == 6) {clearInterval(timer);// 调用next();TimerManager.next();}}, 10);
}// 依次弹出 7 8 9
function timer3() {var num = 6;var timer = setInterval(function() {num ++;alert(num);if (num == 9) {clearInterval(timer);// 调用next();TimerManager.next();}}, 10);
}

运行上述代码后我们会得到一样的运行结果,不同的是我们采用了更为灵活的管理方式。

最终实现

上面说了这么多,好像偏题了。但是,实现定时器的有序执行对实现我们的下拉上滑动画来说的确十分重要。现在,我们就利用上面的成果,来完成我们的下拉上滑。为了方便描述,我们新建一个JS文件Slider2.js。我们会在这个文件中实现一个Slider对象,它包含一个slideUp(element, time)和一个slideDown(element, time)方法。以下是这个文件的结构:

window.Slider = (function() {var Slider = {};// 等待实现return Slider;
})();

这里创建了一个匿名函数,创建并返回一个对象(Slider),我们所有的代码都将在这个闭包中完成,最后只提供两个接口 —— slideUp(element, time)slideDown(element, time)

在给出最终实现代码前,先来说说我的思路:

I. 首先,由于一个网页中往往有多个绑定动画的元素,我们要求每个元素的动画单独连续执行,各个元素的动画的执行相互独立。所以我们不能用一个TimerManger来管理所有元素的动画队列,因此我们需要做的是为每个动画元素分配一个唯一的TimerManger。于是我们先要定义一个TimerManager类。

II. 我们要修改之前的动画函数:在动画结束的时候,获取动画元素的TimerManager,并调用它的next()方法

III. 用Slider对象把TimerManger与动画函数整合起来,并提供外部访问接口

以下是Slider2.js中所有代码:

window.Slider = (function() {// 定义Slider对象var Slider = {};// I.定义一个TimerManager类// 1)构造函数function TimerManager() {this.timers = [];       // 保存定时器this.args = [];         // 保存定时器的参数this.isFiring = false;}// 2)静态方法:为element添加一个TimerManager实例TimerManager.makeInstance = function(element) {// 如果element.__TimerManager__上没有TimerManager,就为其添加一个if (!element.__TimerManager__ || element.__TimerManager__.constructor != TimerManager) {element.__TimerManager__ = new TimerManager();}};// 3)扩展TimerManager原型,分别实现add,fire,next方法TimerManager.prototype.add = function(timer, args) {this.timers.push(timer);this.args.push(args);this.fire();};TimerManager.prototype.fire = function() {if ( !this.isFiring ) {var timer = this.timers.shift(),        // 取出定时器args  = this.args.shift();          // 取出定时器参数if (timer && args) {this.isFiring = true;// 传入参数,执行定时器函数timer(args[0], args[1]);}}};TimerManager.prototype.next = function() {this.isFiring = false;this.fire();};// II. 修改动画函数并在定时器结束后调用element.__TimerManager__.next()// 1)下滑函数function fnSlideDown(element, time) {if (element.offsetHeight == 0) {  //如果当前高度为0,则执行下拉动画// 获取元素总高度element.style.display = "block";            // 1.显示元素,元素变为可见var totalHeight = element.offsetHeight;     // 2.保存总高度element.style.height = "0px";               // 3.再将元素高度设置为0,元素又变为不可见// 定义一个变量保存元素当前高度var currentHeight = 0;                      // 当前元素高度为0// 计算每次增加的值var increment = totalHeight / (time/10);// 开始循环定时器var timer = setInterval(function () {// 增加一部分高度currentHeight = currentHeight + increment;// 把当前高度赋值给height属性element.style.height = currentHeight + "px";// 如果当前高度大于或等于总高度则关闭定时器if (currentHeight >= totalHeight) {// 关闭定时器clearInterval(timer);// 把高度设置为总高度element.style.height = totalHeight + "px";if (element.__TimerManager__ && element.__TimerManager__.constructor == TimerManager) {element.__TimerManager__.next();}}}, 10);} else {  // 如果当前高度不为0,则直接执行队列里的下一个函数if (element.__TimerManager__ && element.__TimerManager__.constructor == TimerManager) {element.__TimerManager__.next();}}}// 2)上拉函数function fnSlideUp(element, time) {if (element.offsetHeight > 0) {  // 如果当前高度不为0,则执行上滑动画// 获取元素总高度var totalHeight = element.offsetHeight;// 定义一个变量保存元素当前高度var currentHeight = totalHeight;// 计算每次减去的值var decrement = totalHeight / (time/10);// 开始循环定时器var timer = setInterval(function() {// 减去当前高度的一部分currentHeight = currentHeight - decrement;// 把当前高度赋值给height属性element.style.height = currentHeight + "px";// 如果当前高度小于等于0,就关闭定时器if (currentHeight <= 0) {// 关闭定时器clearInterval(timer);// 把元素display设置为noneelement.style.display = "none";// 把元素高度值还原element.style.height = totalHeight + "px";if (element.__TimerManager__ && element.__TimerManager__.constructor == TimerManager) {element.__TimerManager__.next();}}}, 10);} else {  // 如果当前高度为0, 则直接执行队列里的下一个函数if (element.__TimerManager__ && element.__TimerManager__.constructor == TimerManager) {element.__TimerManager__.next();}}}// III.定义外部访问接口// 1)下拉接口Slider.slideDown = function(element, time) {TimerManager.makeInstance(element);element.__TimerManager__.add(fnSlideDown, arguments);return this;};// 2)上滑接口Slider.slideUp = function(element, time) {TimerManager.makeInstance(element);element.__TimerManager__.add(fnSlideUp, arguments);return this;};// 返回Slider对象return Slider;
})();

以上代码注释相当清楚(之前描述过的实现在此不再注释),这里不再赘述。

Slider对象完成了,现在新建一个test2.js来调用Slider的方法。以下为test2.js的代码:

window.onload = function() {// 获取btn和panelvar btn   = document.getElementById("btn"),panel = document.getElementById("panel");// 为btn绑定onclick事件btn.onclick = function() {// 通过panel的offsetHeight来判断元素是否可见if (panel.offsetHeight === 0) {// 不可见,调用Slider.slideDown函数:在300毫秒内下拉Slider.slideDown(panel, 300);} else {// 可见,调用Slider.slideUp函数:在300毫秒内上滑Slider.slideUp(panel, 300);}};
};

现在有了Slider2.jstest2.js,我们只要修改一下Slider.html引入这两个JS文件就行了。以下为Slider.html的代码:

<!doctype html>
<html>
<head><meta charset="utf-8"/><title>Slider</title>
</head>
<body><button id="btn">Button</button><div id="panel" style="width:600px;height:400px;background:red;"></div><script src="Slider2.js"></script><script src="test2.js"></script>
</body>
</html>

大功告成,运行以上代码,现在无论我们怎样疯狂的点击btn,我们的动画始终会正确执行。

至此我们已经完成了我们的目标,只不过我们的动画函数还不够优秀,因为它们都是一些简单的匀速运动。不过限于篇幅,这里就不再深究,以后有机会再来实现一些复杂的变速运动,当然,如果读者有兴趣的话也可以自行实现。

还有就是当我们在一个周期内(也就是分别执行一次上拉和下滑所用的全部时间内)多次点击btn时,动画最多执行两次,而不会执行一共点击的次数。当然,这也不能算是一个bug,因为这是笔者刻意为之。如果需要响应多次点击的话,也可以通过简单的修改来实现,不过限于篇幅,笔者也不再深究,留给有心的读者实现。

用原生JavaScript写出类似jQuery中slideUp和slideDown效果相关推荐

  1. 【C 语言】文件操作 ( 学生管理系统 | 命令行接收数据填充结构体 | 结构体写出到文件中 | 查询文件中的结构体数据 )

    文章目录 一.学生管理系统 二.代码示例 一.学生管理系统 前两篇博客 [C 语言]文件操作 ( 将结构体写出到文件中并读取结构体数据 | 将结构体数组写出到文件中并读取结构体数组数据 ) [C 语言 ...

  2. 将JSON对象带有格式的写出到文件中

    需求:将一个JSON对象写出到文件中,要求文件中的JSON数据带有简单的格式.代码的实现参考了Java算法中的栈处理括号匹配问题.好了,不多说了,下面是代码的实现. 代码: 1 package gem ...

  3. 自己用JavaScript写出吉他和弦图生成器

    前言:因为自己有个设计衣服的想法,但网络搜到的和弦图都太模糊,也对市场上的和弦图生成器不太清楚,于是,用自己所学,使用JavaScript写出和弦图 和弦图画起来也是比较简单的,分析一下,就是横竖线, ...

  4. 用JavaScript写出100以内与7有关的数

    用JavaScript写出100以内与7有关的数,代码如下: a%7==0:表示的是7的倍数: a%10==7:表示的是十位是7的数: parseInt(a/10)==7:表示的是个位是7数: 与7有 ...

  5. 微信小程序手把手教你实现类似Android中ViewPager控件效果

    微信小程序手把手教你实现类似Android中ViewPager控件效果 前言 需求分析 头部TAB 滑动的内容部分 最终版本 尾巴 前言 在做Android开发的时候,ViewPager是开发者使用频 ...

  6. jQuery中的渐变动画效果

    jQuery中的渐变动画效果jQuery中的渐变动画效果 转载于:https://www.cnblogs.com/DreamDrive/p/5780292.html

  7. JavaScript事件 以及和jQuery中事件使用对比

    1.即时反应的input和propertychange方法  :https://www.cnblogs.com/LHYwin/p/6136256.html 2.js自定义一个事件    :   htt ...

  8. Flink 使用Table Api 读取文件数据并写出到文件中

    前言 在上一篇我们演示了如何使用Flink 的Table Api 读取文件数据,并过滤特定字段的数据,本篇在上一篇的基础上,将从CSV文件中读取的数据重新输出到一个新的CSV文件中: 在实际业务场景下 ...

  9. 原生JS写仿淘宝搜索框(代码+效果),可实现3级搜索哦!

    闲来无事,用原生JS写了一个淘宝搜索框,用的淘宝的接口,可直接进行商品搜索. 写在前面: 1.记得引用jquery啊! 2.有人私信我说css样式不能用,那是因为复制代码的时候,有空格,只需要自己把c ...

最新文章

  1. 浅谈 CTR 预估模型发展史
  2. Oracle杀死Java EE:名正言顺转到.NET Core
  3. Java SE 8新功能介绍:使用新的DateTime API计算时间跨度
  4. opencv 边缘平滑_基于OpenCV的车道检测实现(一)
  5. query.exec报QSqlQuery::exec: database not open
  6. 互联网 性能 开源_开源的互联网25年及未来
  7. SBI旗下交易所SBI VC Trade推出比特币借贷服务
  8. win10 安装db2 10.1 并使用DBserver连接db2数据库
  9. 用户态处理arp、ndisc neighbour solication 报文
  10. DevOps使用教程 华为云(15)git如何将本地项目初始化为远程仓库
  11. 传智播客 C/C++学习笔记 二级指针作为输入 3 以及三种不同模型的内存模型示意图
  12. [PKUSC2018游记]
  13. PMP课程学习第五天
  14. python编写摇骰子游戏_Python使用tkinter实现摇骰子小游戏功能的代码
  15. 金蝶云·星空——采购入库单生成凭证取不到价税合计
  16. php图书借阅管理系统前台,php图书馆图书借阅管理系统
  17. 微信小程序应用百度地图API
  18. Flashback 技术
  19. HTML5声音引擎Howler.js简介
  20. 北大数学天才“韦神”上热搜,随手帮6个博士解决困扰4个月的难题

热门文章

  1. 演化计算(蚁群算法、粒子群算法、遗传算法、演化规则......)
  2. 江西理工大学计算机考研资料汇总
  3. history of program
  4. 明哥复习MyBatis(1)
  5. Java实现月工资个人所得税及各保险计算问题(2022年版)
  6. 912 计算机考研专业课,2019清华大学计算机考研912考试教材如何选择?
  7. 爬虫第八式:破解百度翻译案例(特别详细) - JS逆向
  8. docker安装elasticsearch内存修改
  9. 华文行楷字帖欣赏_千字文华文行楷字帖.pdf
  10. 华软云计算机软件,广州大学华软软件学院