WebGL利用FBO完成立方体贴图效果完整实例(附demo源码下载)


Posted in Javascript onJanuary 26, 2016

本文实例讲述了WebGL利用FBO完成立方体贴图效果的方法。分享给大家供大家参考,具体如下:

这篇主要记录WebGL的一些基本要点,顺便也学习下如何使用FBO与环境贴图。先看下效果图(需要支持WebGL,Chrome,火狐,IE11)。

WebGL利用FBO完成立方体贴图效果完整实例(附demo源码下载)

主要实现过程如下,先用FBO输出当前环境在立方体纹理中,再画出当前立方体,最后画球,并且把FBO关联的纹理贴在这个球面上。

开始WebGL时,最好有些OpenGL基础,在前面讲Obj完善与MD2时,大家可能已经发现了,因为着色器的添加使用,原来一些Opengl大部分API已经没有使用。WebGL就和这差不多,大部分功能是着色器完成主要功能,记录下主要过程,大家可以比较下前面的,看看是不是很像,为了熟悉WebGL基本功能,本文没有利用比较完善的框架,只是用到一个帮助计算矩阵的框架(gl-matrix.js).

和使用OpenGL一样,我们要初始化使用环境,提取一些全局使用量。相关代码如下:

初始化:

var gl;//WebGLRenderingContext
var cubeVBO;//Cube buffer ID
var sphereVBO;//球体VBO
var sphereEBO;//球体EBO
var cubeTexID;//立方体纹理ID
var fboBuffer;//桢缓存对象
var glCubeProgram;//立方体着色器应用
var glSphereProgram;//球体着色器应用
var fboWidth = 512;//桢缓存绑定纹理宽度
var fboHeight = 512;//桢缓存绑定纹理高度
var targets;//立方体贴图六个方向
var pMatrix = mat4.create();//透视矩阵
var vMatrix = mat4.create();//视图矩阵
var eyePos = vec3.fromValues(0.0, 1.0, 0.0);//眼睛位置
var eyeLookat = vec3.fromValues(0.0, -0.0, 0.0);//眼睛方向
var spherePos = vec3.fromValues(0.0, -0.0, 0.0);//球体位置
var canvanName;
function webGLStart(cName) {
  canvanName = cName;
  InitWebGL();
  InitCubeShader();
  InitSphereShader();
  InitCubeBuffer();
  InitSphereBuffer();
  InitFBOCube();
  //RenderFBO();
  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.enable(gl.DEPTH_TEST);
  tick();
}
function InitWebGL() {
  //var canvas = document.getElementById(canvanName);
  InitGL(canvanName);
}
function InitGL(canvas) {
  try {
    //WebGLRenderingContext 
    gl = canvas.getContext("experimental-webgl");
    gl.viewportWidth = canvas.width;
    gl.viewportHeight = canvas.height;
    targets = [gl.TEXTURE_CUBE_MAP_POSITIVE_X,
           gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
           gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
           gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
           gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
           gl.TEXTURE_CUBE_MAP_NEGATIVE_Z];
  } catch (e) { }
  if (!gl) { alert("你的浏览器不支持WebGL"); }
}

在这里,我们初始化在网页中WebGL的上下方环境,并给出一系列初始化过程。下面先给出房间,也就是其中立方体的相关代码。

立方体:

function InitCubeShader() {
  //WebGLShader
  var shader_vertex = GetShader("cubeshader-vs");
  var shader_fragment = GetShader("cubeshader-fs");
  //WebglCubeProgram
  glCubeProgram = gl.createProgram();
  gl.attachShader(glCubeProgram, shader_vertex);
  gl.attachShader(glCubeProgram, shader_fragment);
  gl.linkProgram(glCubeProgram);
  if (!gl.getProgramParameter(glCubeProgram, gl.LINK_STATUS)) {
    alert("Shader hava error.");
  }
  gl.useProgram(glCubeProgram);
  glCubeProgram.positionAttribute = gl.getAttribLocation(glCubeProgram, "a_position");
  glCubeProgram.normalAttribute = gl.getAttribLocation(glCubeProgram, "a_normal");
  glCubeProgram.texCoordAttribute = gl.getAttribLocation(glCubeProgram, "a_texCoord");
  glCubeProgram.view = gl.getUniformLocation(glCubeProgram, "view");
  glCubeProgram.perspective = gl.getUniformLocation(glCubeProgram, "perspective");
}
function InitCubeBuffer() {
  var cubeData = [
      -10.0, -10.0, -10.0, 0.0, 0.0, -10.0, 1.0, 0.0,
      -10.0, 10.0, -10.0, 0.0, 0.0, -10.0, 1.0, 1.0,
      10.0, 10.0, -10.0, 0.0, 0.0, -10.0, 0.0, 1.0,
      10.0, 10.0, -10.0, 0.0, 0.0, -10.0, 0.0, 1.0,
      10.0, -10.0, -10.0, 0.0, 0.0, -10.0, 0.0, 0.0,
      -10.0, -10.0, -10.0, 0.0, 0.0, -10.0, 1.0, 0.0,
      -10.0, -10.0, 10.0, 0.0, 0.0, 10.0, 0.0, 0.0,
      10.0, -10.0, 10.0, 0.0, 0.0, 10.0, 1.0, 0.0,
      10.0, 10.0, 10.0, 0.0, 0.0, 10.0, 1.0, 1.0,
      10.0, 10.0, 10.0, 0.0, 0.0, 10.0, 1.0, 1.0,
      -10.0, 10.0, 10.0, 0.0, 0.0, 10.0, 0.0, 1.0,
      -10.0, -10.0, 10.0, 0.0, 0.0, 10.0, 0.0, 0.0,
      -10.0, -10.0, -10.0, 0.0, -10.0, 0.0, 0.0, 0.0,
      10.0, -10.0, -10.0, 0.0, -10.0, 0.0, 1.0, 0.0,
      10.0, -10.0, 10.0, 0.0, -10.0, 0.0, 1.0, 1.0,
      10.0, -10.0, 10.0, 0.0, -10.0, 0.0, 1.0, 1.0,
      -10.0, -10.0, 10.0, 0.0, -10.0, 0.0, 0.0, 1.0,
      -10.0, -10.0, -10.0, 0.0, -10.0, 0.0, 0.0, 0.0,
      10.0, -10.0, -10.0, 10.0, 0.0, 0.0, 0.0, 0.0,
      10.0, 10.0, -10.0, 10.0, 0.0, 0.0, 1.0, 0.0,
      10.0, 10.0, 10.0, 10.0, 0.0, 0.0, 1.0, 1.0,
      10.0, 10.0, 10.0, 10.0, 0.0, 0.0, 1.0, 1.0,
      10.0, -10.0, 10.0, 10.0, 0.0, 0.0, 0.0, 1.0,
      10.0, -10.0, -10.0, 10.0, 0.0, 0.0, 0.0, 0.0,
      10.0, 10.0, -10.0, 0.0, 10.0, 0.0, 0.0, 0.0,
      -10.0, 10.0, -10.0, 0.0, 10.0, 0.0, 1.0, 0.0,
      -10.0, 10.0, 10.0, 0.0, 10.0, 0.0, 1.0, 1.0,
      -10.0, 10.0, 10.0, 0.0, 10.0, 0.0, 1.0, 1.0,
      10.0, 10.0, 10.0, 0.0, 10.0, 0.0, 0.0, 1.0,
      10.0, 10.0, -10.0, 0.0, 10.0, 0.0, 0.0, 0.0,
      -10.0, 10.0, -10.0, -10.0, 0.0, 0.0, 0.0, 0.0,
      -10.0, -10.0, -10.0, -10.0, 0.0, 0.0, 1.0, 0.0,
      -10.0, -10.0, 10.0, -10.0, 0.0, 0.0, 1.0, 1.0,
      -10.0, -10.0, 10.0, -10.0, 0.0, 0.0, 1.0, 1.0,
      -10.0, 10.0, 10.0, -10.0, 0.0, 0.0, 0.0, 1.0,
      -10.0, 10.0, -10.0, -10.0, 0.0, 0.0, 0.0, 0.0,
  ];
  cubeVBO = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, cubeVBO);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(cubeData), gl.STATIC_DRAW);
}
function RenderCube() {
  gl.useProgram(glCubeProgram);
  gl.bindBuffer(gl.ARRAY_BUFFER, cubeVBO);
  gl.vertexAttribPointer(glCubeProgram.positionAttribute, 3, gl.FLOAT, false, 32, 0);
  gl.enableVertexAttribArray(glCubeProgram.positionAttribute);
  gl.vertexAttribPointer(glCubeProgram.normalAttribute, 3, gl.FLOAT, false, 32, 12);
  gl.enableVertexAttribArray(glCubeProgram.normalAttribute);
  gl.vertexAttribPointer(glCubeProgram.texCoordAttribute, 2, gl.FLOAT, false, 32, 24);
  gl.enableVertexAttribArray(glCubeProgram.texCoordAttribute);
  gl.uniformMatrix4fv(glCubeProgram.view, false, vMatrix);
  gl.uniformMatrix4fv(glCubeProgram.perspective, false, pMatrix);
  gl.drawArrays(gl.TRIANGLES, 0, 36);
}

上面的代码主要分为初始化立方体的着色器对象,初始化相关缓存,然后绘制立方体,可以说在Opengl中,如果用着色器来画,过程也是差不多的,在Opengl里,已经没有固定管线的一些功能如InterleavedArrays来指定是顶点还是法线或是纹理了,统一用vertexAttribPointer来传递应用程序与着色器之间的数据。在前面 MD2桢动画实现里面后面的参数传递改进版也有相关应用。

相应着立方体着色器主要代码如下:

立方体着色器实现:

<script id="cubeshader-fs" type="x-shader/x-fragment">
    precision mediump float;
    varying vec3 normal;
    varying vec3 tex1;
    varying vec3 tex2;
    void main( void )
    {
    float x = tex1.x * 6.28 * 8.0; //2兀 * 8
    float y = tex1.y * 6.28 * 8.0; //2兀 * 8
    //cos(x)= 8个 (1 -1 1)
    gl_FragColor = vec4(tex2,1.0) * vec4(sign(cos(x)+cos(y))); //
    //gl_FragColor = vec4(normal*vec3(0.5)+vec3(0.5), 1);
    }
</script>
<script id="cubeshader-vs" type="x-shader/x-vertex">
    attribute vec3 a_position;
    attribute vec3 a_normal;
    attribute vec2 a_texCoord;
    uniform mat4 view;
    uniform mat4 perspective;
    varying vec3 normal;
    varying vec3 tex1;
    varying vec3 tex2;
    void main( void )
    {
    gl_Position = perspective * view * vec4(a_position,1.0);
    normal = a_normal;
    tex1 = vec3(a_texCoord,0.0);
    tex2 = normalize(a_position)*0.5+0.5;
    }
</script>

着色器中,已经没有ftransform()功能可供调用,要自己传递如模型,视图,透视矩阵,在这里,模型是以原点为中心来绘画,意思模型视图矩阵也就是视图矩阵,所以屏幕位置的计算只需要视图和透视矩阵。在片断着色器中,x,y是从顶点着色器中的纹理坐标传递过来,相应过程6.28*8.0,相当于8个360度,用于控制立方体上的方块显示,而tex2是着色器中的顶点映射[0,1]的值,分别给立方体的六面分别设置不同的意思,然后用二个矢量的乘积来混合这二种颜色显示,gl_FragColor = vec4(tex2,1.0) * vec4(sign(cos(x)+cos(y)))。

在显示球体之前,应该先生成当前环境的立方体绘图,在这里使用FBO,先生成桢缓存和立方体绘理,并关联,然后以原点为中心,分别向上下左右前右绘图,然后利用桢缓冲分别输出到立方体上的六个面,主要代码如下:

FBO与立方体纹理:

function InitFBOCube() {
  // WebGLFramebuffer
  fboBuffer = gl.createFramebuffer();
  gl.bindFramebuffer(gl.FRAMEBUFFER, fboBuffer);
  fboBuffer.width = 512;
  fboBuffer.height = 512;
  cubeTexID = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_CUBE_MAP, cubeTexID);
  gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
  for (var i = 0; i < targets.length; i++) {
    gl.texImage2D(targets[i], 0, gl.RGBA, fboBuffer.width, fboBuffer.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
  }
  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}
function RenderFBO() {
  gl.disable(gl.DEPTH_TEST);
  gl.viewport(0, 0, fboBuffer.width, fboBuffer.height);
  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.bindFramebuffer(gl.FRAMEBUFFER, fboBuffer);
  for (var i = 0; i < targets.length; i++) {
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, targets[i], cubeTexID, null);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  }
  mat4.perspective(pMatrix, 45, fboBuffer.width / fboBuffer.height, 0.1, 100.0);
  for (var i = 0; i < targets.length; i++) {
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, targets[i], cubeTexID, null);
    var lookat = vec3.create();
    var up = vec3.create();
    up[1] = 1.0;
    if (i == 0) {
      lookat[0] = -1.0;
    } else if (i == 1) {
      lookat[0] = 1.0;      
    } else if (i == 2) {
      lookat[1] = -1.0;
      up[0] = 1.0;
    } else if (i == 3) {
      lookat[1] = 1.0;
      up[0] = 1.0;
    } else if (i == 4) {
      lookat[2] == -1.0;      
    } else if (i == 5) {
      lookat[2] = 1.0;      
    } else {     
    }
    //vec3.fromValues(0.0, 0.0, 0.0)
    vMatrix = mat4.create();
    mat4.lookAt(vMatrix, vec3.fromValues(0.0, 0.0, 0.0), lookat, up);
    //mat4.scale(vMatrix, vMatrix, vec3.fromValues(-1.0, -1.0, -1.0));
    //mat4.translate(vMatrix, vMatrix, spherePos);    
    RenderCube();
  }
  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
  gl.enable(gl.DEPTH_TEST);
}

在上面不知是gl-matrix提供的矩阵算法有问题,还是本来应该这样,在上下面的时候生成的纹理图不对,需要偏转摄像机的向上矢量。因为这是摄像机位置与目标的生成的Z轴和设定的UP轴平行了,这样导致不能正确计算X轴,然后对应的UP轴也计算不出来,相应视图矩阵出现错误。

最后是球体的绘画,代码主要和立方体的差不多,注意球体的顶点算法。

球体:

function InitSphereShader() {
  //WebGLShader
  var shader_vertex = GetShader("sphereshader-vs");
  var shader_fragment = GetShader("sphereshader-fs");
  //WebglCubeProgram
  glSphereProgram = gl.createProgram();
  gl.attachShader(glSphereProgram, shader_vertex);
  gl.attachShader(glSphereProgram, shader_fragment);
  gl.linkProgram(glSphereProgram);
  if (!gl.getProgramParameter(glSphereProgram, gl.LINK_STATUS)) {
    alert("Shader hava error.");
  }
  glSphereProgram.positionAttribute = gl.getAttribLocation(glSphereProgram, "a_position");
  glSphereProgram.normalAttribute = gl.getAttribLocation(glSphereProgram, "a_normal");
  glSphereProgram.eye = gl.getUniformLocation(glSphereProgram, "eye");
  glSphereProgram.mapCube = gl.getUniformLocation(glSphereProgram, "mapCube");
  glSphereProgram.model = gl.getUniformLocation(glSphereProgram, "model");
  glSphereProgram.view = gl.getUniformLocation(glSphereProgram, "view");
  glSphereProgram.perspective = gl.getUniformLocation(glSphereProgram, "perspective");
}
function InitSphereBuffer() {
  var radius = 1;
  var segments = 16;
  var rings = 16;
  var length = segments * rings * 6;
  var sphereData = new Array();
  var sphereIndex = new Array();
  for (var y = 0; y < rings; y++) {
    var phi = (y / (rings - 1)) * Math.PI;
    for (var x = 0; x < segments; x++) {
      var theta = (x / (segments - 1)) * 2 * Math.PI;
      sphereData.push(radius * Math.sin(phi) * Math.cos(theta));
      sphereData.push(radius * Math.cos(phi));
      sphereData.push(radius * Math.sin(phi) * Math.sin(theta));
      sphereData.push(Math.sin(phi) * Math.cos(theta));
      sphereData.push(radius * Math.cos(phi))
      sphereData.push(Math.sin(phi) * Math.sin(theta));
    }
  }
  for (var y = 0; y < rings - 1; y++) {
    for (var x = 0; x < segments - 1; x++) {
      sphereIndex.push((y + 0) * segments + x);
      sphereIndex.push((y + 1) * segments + x);
      sphereIndex.push((y + 1) * segments + x + 1);
      sphereIndex.push((y + 1) * segments + x + 1);
      sphereIndex.push((y + 0) * segments + x + 1)
      sphereIndex.push((y + 0) * segments + x);
    }
  }
  sphereVBO = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, sphereVBO);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(sphereData), gl.STATIC_DRAW);
  sphereVBO.numItems = segments * rings;
  sphereEBO = gl.createBuffer();
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, sphereEBO);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(sphereIndex), gl.STATIC_DRAW);
  sphereEBO.numItems = sphereIndex.length;
}
function RenderSphere() {
  gl.useProgram(glSphereProgram);
  gl.bindBuffer(gl.ARRAY_BUFFER, sphereVBO);
  gl.vertexAttribPointer(glSphereProgram.positionAttribute, 3, gl.FLOAT, false, 24, 0);
  gl.enableVertexAttribArray(glSphereProgram.positionAttribute);
  gl.vertexAttribPointer(glSphereProgram.normalAttribute, 3, gl.FLOAT, false, 24, 12);
  gl.enableVertexAttribArray(glSphereProgram.normalAttribute);
  var mMatrix = mat4.create();
  mat4.translate(mMatrix, mMatrix, spherePos);
  gl.uniform3f(glSphereProgram.eye, eyePos[0],eyePos[1],eyePos[2]);
  gl.uniformMatrix4fv(glSphereProgram.model, false, mMatrix);
  gl.uniformMatrix4fv(glSphereProgram.view, false, vMatrix);
  gl.uniformMatrix4fv(glSphereProgram.perspective, false, pMatrix);
  gl.activeTexture(gl.TEXTURE0);
  gl.bindTexture(gl.TEXTURE_CUBE_MAP, cubeTexID);
  //gl.uniformMatrix4fv(glSphereProgram.mapCube, 0);
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, sphereEBO);
  gl.drawElements(gl.TRIANGLES, sphereEBO.numItems, gl.UNSIGNED_SHORT, 0);
  gl.bindTexture(gl.TEXTURE_2D, null);
}

可以看到,也是和立方体一样的三步,初始化着色器,初始化顶点与法线,绘画。下面给出着色器代码:

球体着色器:

<script id="sphereshader-fs" type="x-shader/x-fragment">
    precision mediump float;
    varying vec3 normal;
    varying vec3 eyevec;
    uniform samplerCube mapCube;
    void main( void )
    {
    gl_FragColor = textureCube(mapCube, reflect(normalize(-eyevec), normalize(normal)));
    }
</script>
<script id="sphereshader-vs" type="x-shader/x-vertex">
    attribute vec3 a_position;
    attribute vec3 a_normal;
    uniform mat4 model;
    uniform mat4 view;
    uniform mat4 perspective;
    uniform vec3 eye;
    varying vec3 normal;
    varying vec3 eyevec;
    void main( void )
    {
    gl_Position = perspective * view * model * vec4(a_position,1.0);
    eyevec = -eye;// a_position.xyz;
    normal = a_normal;
    }
</script>

和前面立方体有点不同的是,球体有自己的模型矩阵,这也是一般正常的用法,然后传递眼睛对应球体顶点矢量与法线传递在片断着色器中,在片断着色器中,就有用到前面所生成的立方体纹理,我们根据眼睛经过顶点通过对应法向量反射到立体体纹理上的点来获取当前球体所对应的环境颜色,在这里,我们可以直接调用textureCube来完成上面所说的过程,不需要我们手动来计算。

其中GetShader函数的使用,参照了http://msdn.microsoft.com/zh-TW/library/ie/dn302360(v=vs.85) 这里的讲解。

可以说,上面主要的绘制函数已经完成,但是我们这个是能动的,所以需要模拟如客户端环境每隔多久绘制一次,主要代码如下:

动画:

function tick() {
  Update();
  OnDraw();
  setTimeout(function () { tick() }, 15);
}
function OnDraw() {
  //fbo rander CUBE_MAP
  RenderFBO();
  //element rander
  gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  mat4.perspective(pMatrix, 45, gl.viewportWidth / gl.viewportHeight, 0.1, 200.0);
  mat4.lookAt(vMatrix, eyePos, eyeLookat, vec3.fromValues(0.0, 1.0, 0.0));
  RenderCube();
  RenderSphere();
}
var lastTime = new Date().getTime();
function Update() {
  var timeNow = new Date().getTime();
  if (lastTime != 0) {
    var elapsed = timeNow - lastTime;
    //3000控制人眼的旋转速度。8控制人眼的远近
    eyePos[0] = Math.cos(elapsed / 3000) * 8;
    eyePos[2] = Math.sin(elapsed / 2000) * 8;
    spherePos[0] = Math.cos(elapsed / 4000) * 3;
    spherePos[2] = Math.cos(elapsed / 4000) * 3;
  }
}

在上面,每隔15毫秒调用一次Update与Draw函数,其中Update用于更新眼睛与球体位置,Draw绘画。

完整实例代码点击此处本站下载。

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
js与jquery中获取当前鼠标的x、y坐标位置的代码
May 23 Javascript
js+html+css实现鼠标移动div实例
Jan 30 Javascript
form表单action提交的js部分与html部分
Jan 07 Javascript
node.js中的fs.lchmod方法使用说明
Dec 16 Javascript
JS实现简单的键盘打字的效果
Apr 24 Javascript
JQuery移动页面开发之屏幕方向改变与滚屏的实现
Dec 03 Javascript
怎么引入(调用)一个JS文件
May 26 Javascript
js对字符串进行编码的方法总结(推荐)
Nov 10 Javascript
vue构建单页面应用实战
Apr 10 Javascript
javaScript canvas实现(画笔大小 颜色 橡皮的实例)
Nov 28 Javascript
前后端如何实现登录token拦截校验详解
Sep 03 Javascript
JS检索下拉列表框中被选项目的索引号(selectedIndex)
Dec 17 Javascript
JS组件中bootstrap multiselect两大组件较量
Jan 26 #Javascript
JS组件Form表单验证神器BootstrapValidator
Jan 26 #Javascript
jQuery实现的指纹扫描效果实例(附演示与demo源码下载)
Jan 26 #Javascript
Bootstrap树形组件jqTree的简单封装
Jan 25 #Javascript
javascript实现2016新年版日历
Jan 25 #Javascript
基于javascript实现图片左右切换效果
Jan 25 #Javascript
JavaScript实现获取某个元素相邻兄弟节点的prev与next方法
Jan 25 #Javascript
You might like
完美解决dedecms中的[html][/html]和[code][/code]问题
2007/03/20 PHP
PHP number_format() 函数定义和用法
2012/06/01 PHP
PHP中功能强大却很少使用的函数实例小结
2016/11/10 PHP
Yii2――使用数据库操作汇总(增删查改、事务)
2016/12/19 PHP
详解PHP处理字符串类似indexof的方法函数
2017/06/11 PHP
CodeIgniter整合Smarty的方法详解
2017/08/25 PHP
不同浏览器对回车提交表单的处理办法
2010/02/13 Javascript
可以用来调试JavaScript错误的解决方案
2010/08/07 Javascript
JS 如果改变span标签的是否隐藏属性
2011/10/06 Javascript
jquery中插件实现自动添加用户的具体代码
2013/11/15 Javascript
JS实现同时搜索百度和必应的方法
2015/01/27 Javascript
JavaScript检查数字是否为整数或浮点数的方法
2015/06/09 Javascript
jQuery实现三级菜单的代码
2016/05/09 Javascript
jquery实现自定义图片裁剪功能【推荐】
2017/03/08 Javascript
AngularJS前端页面操作之用户修改密码功能示例
2017/03/27 Javascript
nodejs连接mysql数据库简单封装示例-mysql模块
2017/04/10 NodeJs
js实现数组和对象的深浅拷贝
2017/09/30 Javascript
基于vue cli重构多页面脚手架过程详解
2018/01/23 Javascript
基于webpack-hot-middleware热加载相关错误的解决方法
2018/02/22 Javascript
Bootstrap Table 双击、单击行获取该行及全表内容
2018/08/31 Javascript
js实现上下左右键盘控制div移动
2020/01/16 Javascript
使用python实现strcmp函数功能示例
2014/03/25 Python
Python基于回溯法子集树模板实现图的遍历功能示例
2017/09/05 Python
python中numpy的矩阵、多维数组的用法
2018/02/05 Python
python 定时任务去检测服务器端口是否通的实例
2019/01/26 Python
Python代码实现http/https代理服务器的脚本
2019/08/12 Python
DataFrame.to_excel多次写入不同Sheet的实例
2019/12/02 Python
利用 CSS3 实现的无缝轮播功能代码
2017/09/25 HTML / CSS
英国和世界各地鲜花速递专家:Arena Flowers
2018/02/10 全球购物
外贸主管求职简历的自我评价
2013/10/23 职场文书
社区健康教育实施方案
2014/03/18 职场文书
医疗器械售后服务承诺书
2014/05/21 职场文书
爱护公共设施的标语
2014/06/24 职场文书
简易离婚协议书范本2014
2014/10/15 职场文书
六一亲子活动感想
2015/08/07 职场文书
python爬取新闻门户网站的示例
2021/04/25 Python