html5中文学习网

您的位置: 首页 > html5教程 > 入门教程 » 正文

HTML5之WebGL 3D概述(上)―WebGL原生开发开启网页3D渲染新时代_html5教程技巧

[ ] 已经帮助:人解决问题
点评:WebGL开启了网页3D渲染的新时代,它允许在canvas中直接渲染3D的内容,而不借助任何插件,看到此处是不是感觉特别惊讶啊,WebGL有一个很好的中文教程,就是下面使用参考中的第一个链接,所以这里不再班门弄斧,后面的内容只是简单的总结一下学习的内容,感兴趣的朋友可以了

WebGL开启了网页3D渲染的新时代,它允许在canvas中直接渲染3D的内容,而不借助任何插件。WebGL同canvas 2D的API一样,都是通过脚本操纵对象,所以步骤也是基本相似:准备工作上下文,准备数据,在canvas中绘制对象并渲染。与2D不同的就是3D涉及的知识更多了,例如世界、光线、纹理、相机、矩阵等专业知识。WebGL有一个很好的中文教程,就是下面使用参考中的第一个链接,所以这里不再班门弄斧,后面的内容只是简单的总结一下学习的内容。6EzHTML5中文学习网 - HTML5先行者学习网

浏览器的支持6EzHTML5中文学习网 - HTML5先行者学习网
由于微软有自己的图形发展计划,一直不支持WebGL,所以IE目前除了安装插件外,是无法运行WebGL的。其他的主流浏览器如Chrome、FireFox、Safari、Opera等,都装上最新的版本就可以了。除了浏览器要装最新的外,还要保证显卡的驱动也是最新的。6EzHTML5中文学习网 - HTML5先行者学习网
装上这些以后,可以打开浏览器,输入下面的网址验证一下浏览器对WebGL的支持情况:http://webglreport.sourceforge.net/。6EzHTML5中文学习网 - HTML5先行者学习网

在正常安装以上浏览器之后还是不能运行WebGL,那你可以强制开启WebGL支持试一试。开启方法如下:6EzHTML5中文学习网 - HTML5先行者学习网
Chrome浏览器6EzHTML5中文学习网 - HTML5先行者学习网
我们需要为Chrome加入一些启动参数,以下具体操作步骤以Windows操作系统为例:找到Chrome浏览器的快捷方式,右键点击快捷方式,选择属性;在目标框内,chrome.exe后面的引号后面,加入以下内容:6EzHTML5中文学习网 - HTML5先行者学习网

--enable-webgl--ignore-gpu-blacklist--allow-file-access-from-files

点击确定后关闭Chrome,然后用此快捷方式启动Chrome浏览器。6EzHTML5中文学习网 - HTML5先行者学习网
几个参数的含义如下:6EzHTML5中文学习网 - HTML5先行者学习网
--enable-webgl的意思是开启WebGL支持;6EzHTML5中文学习网 - HTML5先行者学习网
--ignore-gpu-blacklist的意思是忽略GPU黑名单,也就是说有一些显卡GPU因为过于陈旧等原因,不建议运行WebGL,这个参数可以让浏览器忽略这个黑名单,强制运行WebGL;6EzHTML5中文学习网 - HTML5先行者学习网
--allow-file-access-from-files的意思是允许从本地载入资源,如果你不是WebGL的开发者,不需要开发调试WebGL,只是想要看一下WebGL的Demo,那你可以不添加这个参数。6EzHTML5中文学习网 - HTML5先行者学习网

Firefox浏览器6EzHTML5中文学习网 - HTML5先行者学习网
Firefox的用户请在浏览器的地址栏输入“about:config”,回车,然后在过滤器(filter)中搜索“webgl”,将webgl.force-enabled设置为true;将webgl.disabled设置为false;在过滤器(filter)中搜索“security.fileuri.strict_origin_policy”,将security.fileuri.strict_origin_policy设置为false;然后关闭目前开启的所有Firefox窗口,重新启动Firefox。6EzHTML5中文学习网 - HTML5先行者学习网
前两个设置是强制开启WebGL支持,最后一个security.fileuri.strict_origin_policy的设置是允许从本地载入资源,如果你不是WebGL的开发者,不需要开发调试WebGL,只是想要看一下WebGL的Demo,那你可以不设置此项。6EzHTML5中文学习网 - HTML5先行者学习网

Safari浏览器6EzHTML5中文学习网 - HTML5先行者学习网
在菜单中找到“属性”→“高级”,选中“显示开发菜单”,然后到“开发”菜单,选中“开启WebGL”。6EzHTML5中文学习网 - HTML5先行者学习网

开发步骤6EzHTML5中文学习网 - HTML5先行者学习网

下面的代码只是简单总结一下相关的概念,它来源于参考中的中文教程,涉及较多的3D方面的知识。感兴趣的同学直接可以跳到实用参考中的中文教程中学习,比我这里讲解的要详细和准确的多。凑热闹的同学简单看看就可以了,不用深究每一行代码的含义。6EzHTML5中文学习网 - HTML5先行者学习网

6EzHTML5中文学习网 - HTML5先行者学习网
准备工作6EzHTML5中文学习网 - HTML5先行者学习网
这个不用说了,就是在页面上添加一个canvas元素作为渲染的容器。例如:6EzHTML5中文学习网 - HTML5先行者学习网

复制代码
代码如下:
6EzHTML5中文学习网 - HTML5先行者学习网
<bodyonload="start()">6EzHTML5中文学习网 - HTML5先行者学习网
<canvasid="glcanvas"width="640"height="480">6EzHTML5中文学习网 - HTML5先行者学习网
Yourbrowserdoesn'tappeartosupporttheHTML5canvaselement.6EzHTML5中文学习网 - HTML5先行者学习网
</canvas>6EzHTML5中文学习网 - HTML5先行者学习网
</body> 6EzHTML5中文学习网 - HTML5先行者学习网
6EzHTML5中文学习网 - HTML5先行者学习网
下面就是正式开始写脚本的时候了,首先看一下程序入口以及整体结构:6EzHTML5中文学习网 - HTML5先行者学习网

复制代码
代码如下:
6EzHTML5中文学习网 - HTML5先行者学习网
functionstart(){6EzHTML5中文学习网 - HTML5先行者学习网
varcanvas=document.getElementById("glcanvas");6EzHTML5中文学习网 - HTML5先行者学习网
initGL(canvas);6EzHTML5中文学习网 - HTML5先行者学习网
initShaders();6EzHTML5中文学习网 - HTML5先行者学习网
initBuffers();6EzHTML5中文学习网 - HTML5先行者学习网
gl.clearColor(0.0,0.0,0.0,1.0);6EzHTML5中文学习网 - HTML5先行者学习网
gl.enable(gl.DEPTH_TEST);6EzHTML5中文学习网 - HTML5先行者学习网
drawScene();6EzHTML5中文学习网 - HTML5先行者学习网
} 6EzHTML5中文学习网 - HTML5先行者学习网
6EzHTML5中文学习网 - HTML5先行者学习网
这里的几个方法代表了典型的WebGL的绘制步骤:6EzHTML5中文学习网 - HTML5先行者学习网
6EzHTML5中文学习网 - HTML5先行者学习网
步骤一:初始化WebGL工作环境 - initGL6EzHTML5中文学习网 - HTML5先行者学习网
这个方法的代码如下:6EzHTML5中文学习网 - HTML5先行者学习网

复制代码
代码如下:
6EzHTML5中文学习网 - HTML5先行者学习网
vargl;6EzHTML5中文学习网 - HTML5先行者学习网
functioninitGL(canvas){6EzHTML5中文学习网 - HTML5先行者学习网
gl=null;6EzHTML5中文学习网 - HTML5先行者学习网
try{6EzHTML5中文学习网 - HTML5先行者学习网
//Trytograbthestandardcontext.Ifitfails,fallbacktoexperimental.6EzHTML5中文学习网 - HTML5先行者学习网
gl=canvas.getContext("webgl")||canvas.getContext("experimental-webgl");6EzHTML5中文学习网 - HTML5先行者学习网
}6EzHTML5中文学习网 - HTML5先行者学习网
catch(e){} //Ifwedon'thaveaGLcontext,giveupnow6EzHTML5中文学习网 - HTML5先行者学习网
if(!gl){6EzHTML5中文学习网 - HTML5先行者学习网
alert("UnabletoinitializeWebGL.Yourbrowsermaynotsupportit.");6EzHTML5中文学习网 - HTML5先行者学习网
}6EzHTML5中文学习网 - HTML5先行者学习网
} 6EzHTML5中文学习网 - HTML5先行者学习网
6EzHTML5中文学习网 - HTML5先行者学习网
这个方法很简单,就是获取WebGL的绘制环境,需要把参数"webgl"传给canvas.getContext方法就行了,但是由于目前WebGL的标准没有最终定型,所以实验阶段用的参数都是"experimental-webgl"。当然你直接去调用canvas.getContext("experimental-webgl")也是可以的,等标准定下以后,你再修改一个代码。6EzHTML5中文学习网 - HTML5先行者学习网

步骤二:初始化着色器Shaders - initShaders6EzHTML5中文学习网 - HTML5先行者学习网
着色器Shader概念比较简单,说白了就是显卡运算指令。构造3D场景需要进行大量的颜色、位置等等信息的计算,如果这些计算由软件执行的话,速度会很慢。所以把这些运算让显卡去计算,速度就很快;如何去执行这些计算,就是由着色器指定的。着色器代码是用一种叫做GLSL的着色器语言编写的,这个我们不去讲述这个语言了。6EzHTML5中文学习网 - HTML5先行者学习网
着色器可以在html中定义,在代码中使用。当然了你在程序中用一个字符串去定义着色器也是一样的。6EzHTML5中文学习网 - HTML5先行者学习网
下面先看定义的部分:6EzHTML5中文学习网 - HTML5先行者学习网

复制代码
代码如下:
6EzHTML5中文学习网 - HTML5先行者学习网
<scriptid="shader-fs"type="x-shader/x-fragment">6EzHTML5中文学习网 - HTML5先行者学习网
precisionmediumpfloat;6EzHTML5中文学习网 - HTML5先行者学习网
varyingvec4vColor;6EzHTML5中文学习网 - HTML5先行者学习网
voidmain(void){6EzHTML5中文学习网 - HTML5先行者学习网
gl_FragColor=vColor;6EzHTML5中文学习网 - HTML5先行者学习网
}6EzHTML5中文学习网 - HTML5先行者学习网
</script>6EzHTML5中文学习网 - HTML5先行者学习网
<scriptid="shader-vs"type="x-shader/x-vertex">6EzHTML5中文学习网 - HTML5先行者学习网
attributevec3aVertexPosition;6EzHTML5中文学习网 - HTML5先行者学习网
attributevec4aVertexColor;6EzHTML5中文学习网 - HTML5先行者学习网
uniformmat4uMVMatrix;6EzHTML5中文学习网 - HTML5先行者学习网
uniformmat4uPMatrix;6EzHTML5中文学习网 - HTML5先行者学习网
varyingvec4vColor;6EzHTML5中文学习网 - HTML5先行者学习网
voidmain(void){6EzHTML5中文学习网 - HTML5先行者学习网
gl_Position=uPMatrix*uMVMatrix*vec4(aVertexPosition,1.0);6EzHTML5中文学习网 - HTML5先行者学习网
vColor=aVertexColor;6EzHTML5中文学习网 - HTML5先行者学习网
}6EzHTML5中文学习网 - HTML5先行者学习网
</script> 6EzHTML5中文学习网 - HTML5先行者学习网
6EzHTML5中文学习网 - HTML5先行者学习网
这里有两个着色器:面着色器和顶点着色器。6EzHTML5中文学习网 - HTML5先行者学习网
关于这两个着色器,这里有必要说明一下,计算机中的3D模型基本都是由点结合三角面片去描述的,顶点着色器就是去处理这些点的数据,而面着色器就是通过插值的方式,去处理三角面片上点的数据。6EzHTML5中文学习网 - HTML5先行者学习网
上面定义的顶点着色器就定义了顶点的位置和颜色计算方式;而面着色器定义了插值点的颜色计算方式。实际的应用场景中,还会涉及到在着色器中处理光线等效果。6EzHTML5中文学习网 - HTML5先行者学习网
定义了着色器,在程序中就可以查找到它们并可以去使用:6EzHTML5中文学习网 - HTML5先行者学习网

复制代码
代码如下:
6EzHTML5中文学习网 - HTML5先行者学习网
varshaderProgram;6EzHTML5中文学习网 - HTML5先行者学习网
functioninitShaders(){6EzHTML5中文学习网 - HTML5先行者学习网
varfragmentShader=getShader(gl,"shader-fs");6EzHTML5中文学习网 - HTML5先行者学习网
varvertexShader=getShader(gl,"shader-vs");6EzHTML5中文学习网 - HTML5先行者学习网
shaderProgram=gl.createProgram();6EzHTML5中文学习网 - HTML5先行者学习网
gl.attachShader(shaderProgram,vertexShader);6EzHTML5中文学习网 - HTML5先行者学习网
gl.attachShader(shaderProgram,fragmentShader);6EzHTML5中文学习网 - HTML5先行者学习网
gl.linkProgram(shaderProgram);6EzHTML5中文学习网 - HTML5先行者学习网
if(!gl.getProgramParameter(shaderProgram,gl.LINK_STATUS)){6EzHTML5中文学习网 - HTML5先行者学习网
alert("Couldnotinitialiseshaders");6EzHTML5中文学习网 - HTML5先行者学习网
}6EzHTML5中文学习网 - HTML5先行者学习网
gl.useProgram(shaderProgram);6EzHTML5中文学习网 - HTML5先行者学习网
shaderProgram.vertexPositionAttribute=gl.getAttribLocation(shaderProgram,"aVertexPosition");6EzHTML5中文学习网 - HTML5先行者学习网
gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);6EzHTML5中文学习网 - HTML5先行者学习网
shaderProgram.vertexColorAttribute=gl.getAttribLocation(shaderProgram,"aVertexColor");6EzHTML5中文学习网 - HTML5先行者学习网
gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute);6EzHTML5中文学习网 - HTML5先行者学习网
shaderProgram.pMatrixUniform=gl.getUniformLocation(shaderProgram,"uPMatrix");6EzHTML5中文学习网 - HTML5先行者学习网
shaderProgram.mvMatrixUniform=gl.getUniformLocation(shaderProgram,"uMVMatrix");6EzHTML5中文学习网 - HTML5先行者学习网
} 6EzHTML5中文学习网 - HTML5先行者学习网
6EzHTML5中文学习网 - HTML5先行者学习网
着色器是有了,但是怎么让显卡去执行,Program就是这种桥梁,它是WebGL原生的二进制码,它的作用基本上就是让显卡运行着色器代码去渲染指定的模型数据。6EzHTML5中文学习网 - HTML5先行者学习网
这里还用到一个辅助方法getShader,这个方法就是遍历html文档,查找着色器的定义,拿到定义后创建着色器,这里就不细说了:6EzHTML5中文学习网 - HTML5先行者学习网

复制代码
代码如下:
6EzHTML5中文学习网 - HTML5先行者学习网
functiongetShader(gl,id){6EzHTML5中文学习网 - HTML5先行者学习网
varshaderScript,theSource,currentChild,shader;6EzHTML5中文学习网 - HTML5先行者学习网
shaderScript=document.getElementById(id);6EzHTML5中文学习网 - HTML5先行者学习网
if(!shaderScript){6EzHTML5中文学习网 - HTML5先行者学习网
returnnull;6EzHTML5中文学习网 - HTML5先行者学习网
}6EzHTML5中文学习网 - HTML5先行者学习网
theSource="";6EzHTML5中文学习网 - HTML5先行者学习网
currentChild=shaderScript.firstChild;6EzHTML5中文学习网 - HTML5先行者学习网
while(currentChild){6EzHTML5中文学习网 - HTML5先行者学习网
if(currentChild.nodeType==currentChild.TEXT_NODE){6EzHTML5中文学习网 - HTML5先行者学习网
theSource+=currentChild.textContent;6EzHTML5中文学习网 - HTML5先行者学习网
}6EzHTML5中文学习网 - HTML5先行者学习网
currentChild=currentChild.nextSibling;6EzHTML5中文学习网 - HTML5先行者学习网
}6EzHTML5中文学习网 - HTML5先行者学习网
if(shaderScript.type=="x-shader/x-fragment"){6EzHTML5中文学习网 - HTML5先行者学习网
shader=gl.createShader(gl.FRAGMENT_SHADER);6EzHTML5中文学习网 - HTML5先行者学习网
}elseif(shaderScript.type=="x-shader/x-vertex"){6EzHTML5中文学习网 - HTML5先行者学习网
shader=gl.createShader(gl.VERTEX_SHADER);6EzHTML5中文学习网 - HTML5先行者学习网
}else{6EzHTML5中文学习网 - HTML5先行者学习网
//Unknownshadertype6EzHTML5中文学习网 - HTML5先行者学习网
returnnull;6EzHTML5中文学习网 - HTML5先行者学习网
}6EzHTML5中文学习网 - HTML5先行者学习网
gl.shaderSource(shader,theSource);6EzHTML5中文学习网 - HTML5先行者学习网
//Compiletheshaderprogram6EzHTML5中文学习网 - HTML5先行者学习网
gl.compileShader(shader);6EzHTML5中文学习网 - HTML5先行者学习网
//Seeifitcompiledsuccessfully6EzHTML5中文学习网 - HTML5先行者学习网
if(!gl.getShaderParameter(shader,gl.COMPILE_STATUS)){6EzHTML5中文学习网 - HTML5先行者学习网
alert("Anerroroccurredcompilingtheshaders:"+gl.getShaderInfoLog(shader));6EzHTML5中文学习网 - HTML5先行者学习网
returnnull;6EzHTML5中文学习网 - HTML5先行者学习网
}6EzHTML5中文学习网 - HTML5先行者学习网
returnshader;6EzHTML5中文学习网 - HTML5先行者学习网
} 6EzHTML5中文学习网 - HTML5先行者学习网
6EzHTML5中文学习网 - HTML5先行者学习网
步骤三:创建/加载模型数据 - initBuffers6EzHTML5中文学习网 - HTML5先行者学习网
这些小例子中,模型数据基本都是直接生成的,实际的程序中,这些数据应该都是从模型加载得到的:6EzHTML5中文学习网 - HTML5先行者学习网

复制代码
代码如下:
6EzHTML5中文学习网 - HTML5先行者学习网
vartriangleVertexPositionBuffer;6EzHTML5中文学习网 - HTML5先行者学习网
vartriangleVertexColorBuffer;6EzHTML5中文学习网 - HTML5先行者学习网
functioninitBuffers(){6EzHTML5中文学习网 - HTML5先行者学习网
triangleVertexPositionBuffer=gl.createBuffer();6EzHTML5中文学习网 - HTML5先行者学习网
gl.bindBuffer(gl.ARRAY_BUFFER,triangleVertexPositionBuffer);6EzHTML5中文学习网 - HTML5先行者学习网
varvertices=[6EzHTML5中文学习网 - HTML5先行者学习网
0.0,1.0,0.0,6EzHTML5中文学习网 - HTML5先行者学习网
-1.0,-1.0,0.0,6EzHTML5中文学习网 - HTML5先行者学习网
1.0,-1.0,0.06EzHTML5中文学习网 - HTML5先行者学习网
];6EzHTML5中文学习网 - HTML5先行者学习网
gl.bufferData(gl.ARRAY_BUFFER,newFloat32Array(vertices),gl.STATIC_DRAW);6EzHTML5中文学习网 - HTML5先行者学习网
triangleVertexPositionBuffer.itemSize=3;6EzHTML5中文学习网 - HTML5先行者学习网
triangleVertexPositionBuffer.numItems=3;6EzHTML5中文学习网 - HTML5先行者学习网
triangleVertexColorBuffer=gl.createBuffer();6EzHTML5中文学习网 - HTML5先行者学习网
gl.bindBuffer(gl.ARRAY_BUFFER,triangleVertexColorBuffer);6EzHTML5中文学习网 - HTML5先行者学习网
varcolors=[6EzHTML5中文学习网 - HTML5先行者学习网
1.0,0.0,0.0,1.0,6EzHTML5中文学习网 - HTML5先行者学习网
0.0,1.0,0.0,1.0,6EzHTML5中文学习网 - HTML5先行者学习网
0.0,0.0,1.0,1.06EzHTML5中文学习网 - HTML5先行者学习网
];6EzHTML5中文学习网 - HTML5先行者学习网
gl.bufferData(gl.ARRAY_BUFFER,newFloat32Array(colors),gl.STATIC_DRAW);6EzHTML5中文学习网 - HTML5先行者学习网
triangleVertexColorBuffer.itemSize=4;6EzHTML5中文学习网 - HTML5先行者学习网
triangleVertexColorBuffer.numItems=3;6EzHTML5中文学习网 - HTML5先行者学习网
} 6EzHTML5中文学习网 - HTML5先行者学习网
6EzHTML5中文学习网 - HTML5先行者学习网
上面这段代码创建了三角形的顶点和顶点的颜色数据并放在缓冲区中。6EzHTML5中文学习网 - HTML5先行者学习网
6EzHTML5中文学习网 - HTML5先行者学习网
步骤四:渲染 - drawScene6EzHTML5中文学习网 - HTML5先行者学习网
准备好了数据以后,交给WebGL去渲染就好了,这里调用的是gl.drawArrays方法。看代码:6EzHTML5中文学习网 - HTML5先行者学习网

复制代码
代码如下:
6EzHTML5中文学习网 - HTML5先行者学习网
functiondrawScene(){6EzHTML5中文学习网 - HTML5先行者学习网
gl.viewport(0,0,gl.viewportWidth,gl.viewportHeight);6EzHTML5中文学习网 - HTML5先行者学习网
gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);6EzHTML5中文学习网 - HTML5先行者学习网
pMatrix=okMat4Proj(45.0,gl.viewportWidth/gl.viewportHeight,0.1,100.0);6EzHTML5中文学习网 - HTML5先行者学习网
mvMatrix=okMat4Trans(-1.5,0.0,-7.0);6EzHTML5中文学习网 - HTML5先行者学习网
gl.bindBuffer(gl.ARRAY_BUFFER,triangleVertexPositionBuffer);6EzHTML5中文学习网 - HTML5先行者学习网
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute,triangleVertexPositionBuffer.itemSize,gl.FLOAT,false,0,0);6EzHTML5中文学习网 - HTML5先行者学习网
gl.bindBuffer(gl.ARRAY_BUFFER,triangleVertexColorBuffer);6EzHTML5中文学习网 - HTML5先行者学习网
gl.vertexAttribPointer(shaderProgram.vertexColorAttribute,triangleVertexColorBuffer.itemSize,gl.FLOAT,false,0,0);6EzHTML5中文学习网 - HTML5先行者学习网
setMatrixUniforms();6EzHTML5中文学习网 - HTML5先行者学习网
gl.drawArrays(gl.TRIANGLES,0,triangleVertexPositionBuffer.numItems);6EzHTML5中文学习网 - HTML5先行者学习网
} 6EzHTML5中文学习网 - HTML5先行者学习网
6EzHTML5中文学习网 - HTML5先行者学习网
这个函数首先设置了3D世界的背景为黑色,然后设置投影矩阵,设置待绘制对象的位置,然后根据缓冲中的顶点和颜色数据,绘制对象。这里还有一些生成投影矩阵和模型视图矩形的辅助方法(使用了Oak3D图形库中的矩阵辅助方法)与主题关系不大,这里就不详细解释了。6EzHTML5中文学习网 - HTML5先行者学习网
基本上流程就是这么多了,更复杂的纹理,光线等都是在这些基础上加入一些WegGL的特性实现的,这个请参看后面的中文教程,里面有详细的例子。6EzHTML5中文学习网 - HTML5先行者学习网

怎么样?使用原生的WebGL开发是一种什么感受?不仅需要有深厚的3D知识,还需要知道各种实现细节。WebGL这样做是为了灵活的适应各种应用场景,但是对于大多数像我这样非专业人士来说,很多细节是不需要知道的。这样就催生了各种辅助开发的类库,例如这节用到的Oak3D库(为了演示WebGL开发,例子中只用到了矩阵辅助方法)。下一节会介绍一个用的比较多的Three.js图形库。6EzHTML5中文学习网 - HTML5先行者学习网

实用参考:6EzHTML5中文学习网 - HTML5先行者学习网
中文教程:http://www.hiwebgl.com/?p=426EzHTML5中文学习网 - HTML5先行者学习网

开发中心:https://developer.mozilla.org/en/WebGL6EzHTML5中文学习网 - HTML5先行者学习网

6EzHTML5中文学习网 - HTML5先行者学习网
(责任编辑:)
推荐书籍
推荐资讯
关于HTML5先行者 - 联系我们 - 广告服务 - 友情链接 - 网站地图 - 版权声明 - 人才招聘 - 帮助