使用JavaScript和Canvas开发游戏(五)
2011年08月17日 Web开发, 编程技术, 翻译
原文作者:Matthew Casperson • 编辑:Michele McDonough
原文链接: Game Development with JavaScript and the Canvas element
1、认识一下Canvas
2、在Canvas上绘图
3、通过Canvas元素实现高级图像操作
4、写一个游戏框架(一)
5、写一个游戏框架(二)
6、通过Canvas实现视差滚动
7、动画
8、JavaScript键盘输入
9、综合运用
10、定义级别
11、跳跃与坠落
12、添加道具
13、加载资源
14、添加主菜单
在Canvas元素上实现视差滚动
http://www.brighthub.com/internet/web-development/articles/40511.aspx
视差滚动是在2D应用中创造立体纵深感的一种技术。这篇文章就来看一看在我们刚刚创建的游戏框架基础上实现视差滚动有多容易。
视差滚动
有了游戏框架,就可以通过画布元素来做一些好玩的东西了。
视差滚动指的是屏幕上的几个图层发生相对位移的效果,换句话说,背景图层滚动得比它前面的那些图层要慢一些。这种创造视觉纵深感的技术在2D游戏中的应用极为普遍。
RepeatingGameObject.js
/**
这个类可以重复显示纹理图像,支持纹理图像在x轴或y轴偏移
@class
*/
function RepeatingGameObject()
{
/** 最终图像占据的宽度
@type Number
*/
this.width = 0;
/** 最终图像占据的高度
@type Number
*/
this.height = 0;
/** 绘制时应用多少scrollX和scrollY
@type Number
*/
this.scrollFactor = 1;
/**
初始化对象
@return 对初始化对象的引用
*/
this.startupRepeatingGameObject = function(image, x, y, z, width, height, scrollFactor)
{
this.startupVisualGameObject(image, x, y, z);
this.width = width;
this.height = height;
this.scrollFactor = scrollFactor;
return this;
}
/**
清理对象
*/
this.shutdownstartupRepeatingGameObject = function()
{
this.shutdownVisualGameObject();
}
/**
把当前元素绘制到后台缓冲
@param dt 自上一帧绘制起经过的秒数
@param context 绘制上下文
@param xScroll x轴的全局滚动值
@param yScroll y轴的全局滚动值
*/
this.draw = function(dt, canvas, xScroll, yScroll)
{
var areaDrawn = [0, 0];
for (var y = 0; y < this.height; y += areaDrawn[1])
{
for (var x = 0; x < this.width; x += areaDrawn[0])
{
// 绘制下一张贴片左上角的点
var newPosition = [this.x + x, this.y + y];
// 剩余的绘制空间
var newFillArea = [this.width - x, this.height - y];
// 第一次必须从图像的中央开始绘制
// 后续贴片从上方或左侧绘制
var newScrollPosition = [0, 0];
if (x==0) newScrollPosition[0] = xScroll * this.scrollFactor;
if (y==0) newScrollPosition[1] = yScroll * this.scrollFactor;
areaDrawn = this.drawRepeat(canvas, newPosition, newFillArea, newScrollPosition);
}
}
}
this.drawRepeat = function(canvas, newPosition, newFillArea, newScrollPosition)
{
// 找到重复绘制纹理图像的起点(左上角)
var xOffset = Math.abs(newScrollPosition[0]) % this.image.width;
var yOffset = Math.abs(newScrollPosition[1]) % this.image.height;
var left = newScrollPosition[0]<0?this.image.width-xOffset:xOffset;
var top = newScrollPosition[1]<0?this.image.height-yOffset:yOffset;
var width = newFillArea[0] < this.image.width-left?newFillArea[0]:this.image.width-left;
var height = newFillArea[1] < this.image.height-top?newFillArea[1]:this.image.height-top;
// 绘制图像
canvas.drawImage(this.image, left, top, width, height, newPosition[0], newPosition[1], width, height);
return [width, height];
}
}
RepeatingGameObject.prototype = new VisualGameObject();
这个RepeatingGameObject类可以让图像在一个确定的区域内重复和滚动。此前,我们已经实现了绘制整幅图像。而RepeatingGameObject的不同之处在于,它拿到一幅图像并用它来填充一块范围既定的区域(其尺寸与绘制的图像无关)。我们通过这个类每次显示一幅大图像(如一座山的全景图)的一小部分,从而创建出一个背景。
也许你已经注意到了GameObjectManager的xScroll和yScroll属性,它们被传递给了GameObject的draw和updata函数。这两个值定义的是摄像机沿x轴或y轴移动了多远。RepeatingGameObject使用这两个值来它们显示的纹理,以创造移动的假象。
首先,定义RepeatingGameObject要绘制的区域。底层的GameObject类的x和y属性定义了左上角位置,而新的width和height属性定义的是绘制区域。
/** 最终图像占据的宽度
@type Number
*/
this.width = 0;
/** 最终图像占据的高度
@type Number
*/
this.height = 0;
/** 绘制时应用多少scrollX和scrollY
@type Number
*/
而scrollFactor属性用于改变RepeatingGameObject滚动的量,该变化是通过传递到draw函数的xScroll和yScroll来控制的。将scrollFactor设置为小于1的值,会导致滚动变慢,从而造就画面中的远景。
/** 绘制时应用多少scrollX和scrollY
@type Number
*/
this.scrollFactor = 1;
最后两个draw和drawRepeat函数具体负责渲染贴片及偏移的纹理。
/**
把当前元素绘制到后台缓冲
@param dt 自上一帧绘制起经过的秒数
@param context 绘制上下文
@param xScroll x轴的全局滚动值
@param yScroll y轴的全局滚动值
*/
this.draw = function(dt, canvas, xScroll, yScroll)
{
var areaDrawn = [0, 0];
for (var y = 0; y < this.height; y += areaDrawn[1])
{
for (var x = 0; x < this.width; x += areaDrawn[0])
{
// 绘制下一张贴片左上角的点
var newPosition = [this.x + x, this.y + y];
// 剩余的绘制空间
var newFillArea = [this.width - x, this.height - y];
// 第一次必须从图像的中央开始绘制
// 后续贴片从上方或左侧绘制
var newScrollPosition = [0, 0];
if (x==0) newScrollPosition[0] = xScroll * this.scrollFactor;
if (y==0) newScrollPosition[1] = yScroll * this.scrollFactor;
areaDrawn = this.drawRepeat(canvas, newPosition, newFillArea, newScrollPosition);
}
}
}
this.drawRepeat = function(canvas, newPosition, newFillArea, newScrollPosition)
{
// 找到重复绘制纹理图像的起点(左上角)
var xOffset = Math.abs(newScrollPosition[0]) % this.image.width;
var yOffset = Math.abs(newScrollPosition[1]) % this.image.height;
var left = newScrollPosition[0]<0?this.image.width-xOffset:xOffset;
var top = newScrollPosition[1]<0?this.image.height-yOffset:yOffset;
var width = newFillArea[0] < this.image.width-left?newFillArea[0]:this.image.width-left;
var height = newFillArea[1] < this.image.height-top?newFillArea[1]:this.image.height-top;
// 绘制图像
canvas.drawImage(this.image, left, top, width, height, newPosition[0], newPosition[1], width, height);
return [width, height];
}
ApplicationManager.js
/**
ApplicationManager用于管理应用
@class
*/
function ApplicationManager()
{
/**
初始化对象
@return A 对初始化对象的引用
*/
this.startupApplicationManager = function()
{
this.startupGameObject();
this.background3 = new RepeatingGameObject().startupRepeatingGameObject(g_back2, 0, 100, 3, 600, 320, 1);
this.background2 = new RepeatingGameObject().startupRepeatingGameObject(g_back1, 0, 100, 2, 600, 320, 0.75);
this.background = new RepeatingGameObject().startupRepeatingGameObject(g_back0, 0, 0, 1, 600, 320, 0.5);
return this;
}
/**
更新当前对象
@param dt 自上一帧绘制起经过的秒数
@param context 绘制上下文
@param xScroll x轴的全局滚动值
@param yScroll y轴的全局滚动值
*/
this.update = function(/**Number*/ dt, /**CanvasRenderingContext2D*/ context, /**Number*/ xScroll, /**Number*/ yScroll)
{
g_GameObjectManager.xScroll += 50 * dt;
}
}
ApplicationManager.prototype = new GameObject
在这里,我们通过ApplicationManager创建了三个RepeatingGameObject类的实例,每个实例分别显示为一个图层,使用z(深度)和scrollFactor值来创造RepeatingGameObject 渐远和渐慢的效果。
最终结果很不错。视差滚动为画布赋予了完美的纵深感,而整个效果只多编写了一个类就实现了。
看一看视差滚动的Demo吧。http://webdemos.sourceforge.net/jsplatformer4/jsplatformer4.html
使用JavaScript和Canvas开发游戏(四)
2011年08月15日 Web开发, 编程技术, 翻译
原文作者:Matthew Casperson • 编辑:Michele McDonough
原文链接: Game Development with JavaScript and the Canvas element
1、认识一下Canvas
2、在Canvas上绘图
3、通过Canvas元素实现高级图像操作
4、写一个游戏框架(一)
5、写一个游戏框架(二)
6、通过Canvas实现视差滚动
7、动画
8、JavaScript键盘输入
9、综合运用
10、定义级别
11、跳跃与坠落
12、添加道具
13、加载资源
14、添加主菜单
4、写一个游戏框架(二)
在这篇文章里,我们继续介绍构成这个基本JavaScript游戏框架的其他必要的类。
前一篇文章介绍了GameObjectManager类,该类负责画布的渲染并允许GameObject类更新和删除它们自己。下面就来看一看GameObject类。
GameObject.js
/**
游戏中出现的所有元素的基类
@class
*/
function GameObject()
{
/** 显示的深度次序。较小的zOrder值表示先渲染,因而会在背景中。
@type Number
*/
this.zOrder = 0;
/**
x轴的坐标
@type Number
*/
this.x = 0;
/**
y轴的坐标
@type Number
*/
this.y = 0;
/**
初始化游戏对象,并将其添加到GameObjectManager维护的对象列表中
@param x x轴的坐标
@param y y轴的坐标
@param z 元素的z次序(背景元素的z值较小)
*/
this.startupGameObject = function(/**Number*/ x, /**Number*/ y, /**Number*/ z)
{
this.zOrder = z;
this.x = x;
this.y = y;
g_GameObjectManager.addGameObject(this);
return this;
}
/**
清理当前对象,将其从GameObjectManager维护的对象列表中删除
*/
this.shutdownGameObject = function()
{
g_GameObjectManager.removeGameObject(this);
}
}
这个GameObject类(是一个引擎类)的目的,是为游戏中将会出现的所有对象定义一些共有的属性,包括它们的位置(x和y)和深度(z)。需要注意的是,我们不会直接创建GameObject类的实例,而是会再创建一个类来扩展它。
这个类的x和y坐标值没有什么好说的——就是相应对象左上角位置在画布上的坐标。关键是GameObject中的z值,这个值定义的是对象的深度。理解这个值很重要,这个值较小的GameObject会先绘制到画布上。换句话说,z值较大的GameObject将被绘制到z值较小的GameObject上面。
查看全文 »
使用JavaScript和Canvas开发游戏(三)
2011年08月14日 Web开发, 编程技术, 翻译
原文作者:Matthew Casperson • 编辑:Michele McDonough
原文链接: Game Development with JavaScript and the Canvas element
1、认识一下Canvas
2、在Canvas上绘图
3、通过Canvas元素实现高级图像操作
4、写一个游戏框架(一)
5、写一个游戏框架(二)
6、通过Canvas实现视差滚动
7、动画
8、JavaScript键盘输入
9、综合运用
10、定义级别
11、跳跃与坠落
12、添加道具
13、加载资源
14、添加主菜单
4、写一个游戏框架(一)
http://www.brighthub.com/internet/web-development/articles/40512.aspx
在知道了如何使用画布元素之后,接下来我教大家写一个框架,有了这个框架,我们就可以把它作为基础来创建游戏。在这第一部分,我们会介绍前两个文件/类。
编写代码之前,我们先来看一看随后几篇文章将致力于创建的示例Demo。表面上看起来,这个Demo跟第二篇文章里的那个没啥区别,但如果你看看后台(查看网页源代码)就会发现,为了更方便地创建这个最终效果,一个凝聚不少心血的基础框架已经写好了。

下面我们要介绍的JavaScript代码使用面向对象的方式来编写。对于没有编写过多少JavaScript代码的人来说,恐怕第一眼看到它们会觉得有点奇怪。如果你真的不太熟悉JavaScript的面向对象编程,建议通过Mozilla Developer Network的这个教程https://developer.mozilla.org/en/Introduction_to_Object-Oriented_JavaScript来补补课。这篇教程里解释了我们稍后会用到的一些编程技术。
从设计思想上来看,这个框架可以分成两部分:与底层的2D引擎交互的类(用于操作画布、控制渲染循环、处理输入等的代码)和用来创建对象以便构成游戏的类。前者可以归为引擎类,后者可以归为应用类。由于应用类要构建于引擎类之上,所以我们需要先来创建引擎类。
查看全文 »
使用JavaScript和Canvas开发游戏(二)
2011年08月11日 Web开发, 编程技术, 翻译
原文作者:Matthew Casperson • 编辑:Michele McDonough
原文链接: Game Development with JavaScript and the Canvas element
1、认识一下Canvas
2、在Canvas上绘图
3、通过Canvas元素实现高级图像操作
4、通过Canvas实现视差滚动
5、写一个游戏框架(一)
6、写一个游戏框架(二)
7、动画
8、JavaScript键盘输入
9、综合运用
10、定义级别
11、跳跃与坠落
12、添加道具
13、加载资源
14、添加主菜单
3、通过Canvas元素实现高级图像操作
http://www.brighthub.com/internet/web-development/articles/39509.aspx
这篇文章将带领大家学习使用JavaScript和Canvas元素操作图像了几种不同的方式,这些方式在Canvas元素出现之前是不可能的事儿。
上一篇文章演示了如何利用Canvas实现一个基本的图像动画。那个例子很简单,同样的效果通过修改IMG或DIV等标准HTML元素的一些属性,照样也可以轻易实现。下面我们就来演示一下画布元素的高级应用,展示一下它的真正威力。
首先,还是准备一个HTML页面。
< !DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
<title>JavaScript Platformer 2</title>
<script type="text/javascript" src="jsplatformer2.js"></script>
<style type="text/css">
body { font-family: Arial,Helvetica,sans-serif;}
</style>
</head>
<body>
<p>
<a href="http://www.brighthub.com/internet/web-development/articles/38364.aspx">
Game Development with Javascript and the canvas element
</a>
</p>
<canvas id="canvas" width="600" height="400">
<p>Your browser does not support the canvas element.</p>
</canvas>
<br />
<button onclick="currentFunction=alpha;">Change Alpha</button>
<button onclick="currentFunction=shear;">Shear</button>
<button onclick="currentFunction=scale;">Scale</button>
<button onclick="currentFunction=rotate;">Rotate</button>
</body>
</html>
与上个一例子的HTML页面相比,唯一的区别就是添加了一些按钮。单击这些按钮,就会设置currentFunction变量(稍后介绍)的值,用以改变在渲染循环中运行的函数。
查看全文 »
使用JavaScript和Canvas开发游戏(一)
2011年08月10日 Web开发, 编程技术, 翻译
原文作者:Matthew Casperson • 编辑:Michele McDonough
原文链接: Game Development with JavaScript and the Canvas element
1、认识一下Canvas
2、在Canvas上绘图
3、通过Canvas元素实现高级图像操作
4、通过Canvas实现视差滚动
5、写一个游戏框架(一)
6、写一个游戏框架(二)
7、动画
8、JavaScript键盘输入
9、综合运用
10、定义级别
11、跳跃与坠落
12、添加道具
13、加载资源
14、添加主菜单
1、认识一下Canvas
http://www.brighthub.com/internet/web-development/articles/38364.aspx
Canvas元素以及JavaScript引擎中新增的一些特性,让Web开发人员不必借助第三方插件,即可设计开发出精细且具有交互性的2D网页。这篇文章就向大家介绍一下Canvas元素,以及它的一些可能的用途。
JavaScript与Canvas元素
HTML是为创建静态页面而生的。HTML所能实现的动态效果,也仅限于显示GIF动画和闪烁的文本。JavaScript改变了这一切,通过它能够动态修改网页。今天,很多Web服务都利用AJAX来创建网页,为用户提供更加流畅的体验,也超越了标准HTML页面中常见的“点击-重新加载-点击”式的交互模式。
然而,JavaScript的某些功能会受到其宿主浏览器的制约。尽管可以在网页中创建和修改任何元素,但JavaScript不能(轻易地)让浏览器显示一种新对象。通过JavaScript修改文本、插入图像或者缩放表格都很容易,因为这些对象本来就是HTML所支持的。如果你想再玩得刺激一点,比如写一个网页游戏,怎么办?那恐怕就得苦心积虑地改变标准HTML元素的用途,克服种种不测才能达到目的。要么,你就得求助于Flash或Silverlight这样的插件。
Canvas元素登场了。这个新HTML元素为JavaScript开发者提供了一种无需插件即可在网页中直接绘图的机制。Canvas元素最早是由苹果公司在其WebKit框架中引入的,Safari浏览器和Dashboard微件都在使用。Canvas元素现在也被建议加入了HTML5规范,得到了最新的Chrome、Firefox、Opera以及Konqueror等浏览器的支持。Internet Explorer(至少在IE8之前)还不支持Canvas,但ExplorerCanvas项目倒是为IE提供了与Canvas元素类似的功能。
Canvas元素对做过2D图形编程的人是小菜一碟。可以在这个元素上画线、画各种形状、画渐变,甚至可以利用与其他2D API中类似的函数来修改其中的每一个像素。得益于Chrome的V8、Firefox的SpiderMonkey以及Safari的Nitro等最新JavaScript引擎的性能,创建精细且具有交互性的Web应用已经完全没有问题。
我们这一系列文章将教会大家使用JavaScript和Canvas元素创建一个简单的平台游戏。将要涉及的内容包括动画、加载资源、分层渲染、滚动和交互。通过一步一步地展示示例代码和实际效果,你可以很快学会如何驾驭强大的Canvas元素。
2、在Canvas上绘图
http://www.brighthub.com/internet/web-development/articles/38744.aspx
下面,我们就通过一个循序渐进的示例及实时演示,来介绍如何使用JavaScript在Canvas元素上绘图及实现动画。
准备工作
知道了什么是Canvas元素之后,该学习在屏幕上绘图了。首先,需要一个HTML页面来放置和显示Canvas元素。
查看全文 »
JavaScript是Web的汇编语言(二):疯狂,亦或只是精神错乱?
2011年07月21日 Web开发, 编程技术, 翻译
原文地址:JavaScript is Assembly Language for the Web: Part 2 – Madness or just Insanity?
有些人认为“JavaScript是Web的汇编语言”完全是精神病说的话。为此,我询问了几位JavaScript权威,比如Brendan Eich(JavaScript之父)、Douglas Crockford(JSON之父),还有Mike Shaver(Mozilla技术副总裁)。以下都是从个人邮件里摘过来的,得到了以上几位的许可。
几年前,我曾说过“JS是Web的x86”(好像是在一次JSConf上),不过我不敢说我是第一个这么说的。(Nick Thompson今年也在Hacker News中这么说过。)
关键在于,JS确实在按照我们想的,越来越往低级方向发展了。但它也具备高级的特性。
Shaver说得没错,汇编缺少可靠的宏处理器,因此不适合程序员,也不够安全。但JS可不是这样。所以,这个比喻需要加点限制条件,不然就要闹出笑话来了。
无论从高级函数式编程还是内存安全角这个角度看,还是从低级特性,像类型化数组以及即将成为现实的ES中类型化数组的扩展、二进制数据,等等来说,JS都是一个比汇编更加强大的编程语言。当然了,内存安全是首要的区别。
JavaScript是Web的汇编语言(一):语义Web已死!
2011年07月20日 Web开发, 编程技术, 翻译
原文地址:JavaScript is Assembly Language for the Web: Sematic Markup is Dead! Clean vs. Machine-coded HTML
(更新)有些人认为“JavaScript是Web的汇编语言”完全是精神病说的话。为此,我询问了几位JavaScript权威,比如Brendan Eich(JavaScript之父)、Douglas Crockford(JSON之父),还有Mike Shaver(Mozilla技术副总裁)。他们的评论发表在下一篇文章里。
昨天我跟Erik Meijer聊天,他说:
——Erik Meijer
怎么会说起这件事儿呢?当时我正在试用Google+,就跟上大多数让我印象深刻的网站一样,我立即就查看它的源代码。不看不要紧,一看吓一跳:

咱就将就说吧,我看到了1300行代码,密密麻麻的,大约90KB。上面图片显示的只是最前面的一小部分,基本上都是“瘦身后”的JavaScript代码。再往下看,页面中间呢,全都是像下面这样的span、div以及生成的类和id: 查看全文 »
JavaScript,只有你想不到
2011年06月22日 Web开发, 编程技术, 翻译
原文地址:http://radar.oreilly.com/2011/06/time-to-learn-javascript.html
作者简介:Mike Loukides
很长时间以来,JavaScript在我眼里都是编程语言中的二等公民。早先,它经常是很多安全问题的发源地,就像是胶水一样,它能把HTML应用与样式粘到一块,可没有人拿它来正正规规地写程序;这样的情形太普遍了。而Java、Ruby、Python,这些才是真正能用来写程序的语言。
过去几年间,我对JavaScript的态度有了彻底的改变。JavaScript已经“长大成人”了。我敢保证很多JavaScript开发人员都不会认同我前面的说法,他们会说JavaScript一直都是一个十分强大、成熟、深得人心的语言。或许他们说得没错,事实上只要是一门完整的编程语言,就能拿来写程序,也包括BASIC这种滥东西。而一门语言真正有用,必须一方面自身具备很强的表达能力,另一方面还要有众多的库和开发工具。显然,JavaScript的表达能力早就没有问题了,即便是创建对象的方式有点不好让人接受,其实问题也不大。直到最近,一些极其重要的扭转局面的技术出现了:jQuery、JSON、Node.js和HTML5。或许JavaScript以前就是一门完善的语言了,但却是这些重要的相关技术(以及其他一些没有在这里提及的),让JavaScript成为了每一个开发人员都知道的语言。如果明年你要学一门新语言的话,那一定就是JavaScript。
潜力无限的Node.js
说Node.js潜力无限的意思,就是它有可能引发Web开发的革命。Node.js是一个框架,用于构建高性能Web应用——即使是巨量的请求也能应对如流。虽然Node本身作为一个底层框架,能够用于构建任何应用,但它还是最适合构建Web服务器。它的异步事件驱动模式与传统的请求-响应模式相比,无疑更适合Web应用。 查看全文 »
http://radar.oreilly.com/2011/02/2010-book-market-1.html
Mike Hendrickson
2011-2-10
自从上一次计算机图书市场报告发表之后的两年来,技术图书市场经历了一些重大的变化。恐怕不少读者根据图书市场日益疲软的征兆,已经看到未来发展的某些趋势了。实际上,我们早就断言过计算机图书销售情况预示着技术发展的趋势,大家也可以搜索一下有关计算机图书市场的其他文章。
本报告的数据来自Bookscan每周监控到的Top 3000图书的销售情况。Bookscan计量的是书店收银机的实际销售数据。换句话说,只要你在美国的书店里买过一本技术书,那么你购买这本书的信息十有八九已经进入了Bookscan的数据库里。Borders、Barnes & Noble以及Amazon等零售商实现了绝大多数技术图书的销售。
图书市场业绩综述
在讨论细分的计算机图书市场之前,我们先来了解一下截止到2011年1月2日这个周末,整个图书市场的表现情况。下面这张表涵盖了从The Girl with the Dragon Tatoo and Eat, Pray, Love to Decision Points到The Ugly Truth的所有图书,只要是印刷、装订并以书的形式销售的,都包含在里面。
整体图书市场表现(所有图书,数据截止日期2011年1月2日)
| 所有图书,所有题材 |
| 青少年非虚构 | -0.44% |
| 青少年虚构 | -3.46% |
| 青少图书整体 | -2.88% |
| 成人非虚构 | -1.91% |
| 计算机及互联网 | -3.99% |
| 成人虚构 | -7.20% |
| 其他 | -13.12% | 整体市场 | -4.54% |
从表中可以看出,计算机图书市场较上年下滑了4%。需要注意的是,计算机图书的销售量只占实体和在线书店所有图书总销量的1%。下面这张图展示的整体图书市场各门类的业绩增长情况。其中,幽默(Humor)类图书的增长在普遍低迷的市场背景下显得独树一帜(14.45%)。另一个增长的领域是青少年非虚构类中的教材/教辅类(21.55%)。我很奇怪,为什么教材/教辅类图书居然有这么高的增长率。
查看全文 »


为之漫笔(李松峰),本博客专注于Web前后端技术、移动平台开发技术、交互设计和技术翻译。声明一下,因为时常需要外出审稿,而且基本不带笔记本,所以有时可能会迟一点回复大家的留言。