原生JS+canvas实现炫酷背景

news2024/11/16 5:40:48

原生JS+canvas实现炫酷背景 可以在需要的背景页使用

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>HTML5 Canvas矩阵粒子波浪背景动画特效</title>

<style>
html,body {
    height:100%;
}
body {
    margin:0;
    background:#000;
}
canvas {
    display:block;
}
.waves {
    position:absolute;
    left:0;
    top:0;
    right:0;
    bottom:0;
}
</style>

</head>
<body>

<div class="waves"></div>

<script>
class ShaderProgram {

  constructor( holder, options = {} ) {

    options = Object.assign( {
      antialias: false,
      depthTest: false,
      mousemove: false,
      autosize: true,
      side: 'front',
      vertex: `
        precision highp float;

        attribute vec4 a_position;
        attribute vec4 a_color;

        uniform float u_time;
        uniform vec2 u_resolution;
        uniform vec2 u_mousemove;
        uniform mat4 u_projection;

        varying vec4 v_color;

        void main() {

          gl_Position = u_projection * a_position;
          gl_PointSize = (10.0 / gl_Position.w) * 100.0;

          v_color = a_color;

        }`,
      fragment: `
        precision highp float;

        uniform sampler2D u_texture;
        uniform int u_hasTexture;

        varying vec4 v_color;

        void main() {

          if ( u_hasTexture == 1 ) {

            gl_FragColor = v_color * texture2D(u_texture, gl_PointCoord);

          } else {

            gl_FragColor = v_color;

          }

        }`,
      uniforms: {},
      buffers: {},
      camera: {},
      texture: null,
      onUpdate: ( () => {} ),
      onResize: ( () => {} ),
    }, options )

    const uniforms = Object.assign( {
      time: { type: 'float', value: 0 },
      hasTexture: { type: 'int', value: 0 },
      resolution: { type: 'vec2', value: [ 0, 0 ] },
      mousemove: { type: 'vec2', value: [ 0, 0 ] },
      projection: { type: 'mat4', value: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ] },
    }, options.uniforms )

    const buffers = Object.assign( {
      position: { size: 3, data: [] },
      color: { size: 4, data: [] },
    }, options.buffers )

    const camera = Object.assign( {
      fov: 60,
      near: 1,
      far: 10000,
      aspect: 1,
      z: 100,
      perspective: true,
    }, options.camera )

    const canvas = document.createElement( 'canvas' )
    const gl = canvas.getContext( 'webgl', { antialias: options.antialias } )

    if ( ! gl ) return false

    this.count = 0
    this.gl = gl
    this.canvas = canvas
    this.camera = camera
    this.holder = holder
    this.onUpdate = options.onUpdate
    this.onResize = options.onResize
    this.data = {}

    holder.appendChild( canvas )

    this.createProgram( options.vertex, options.fragment )

    this.createBuffers( buffers )
    this.createUniforms( uniforms )

    this.updateBuffers()
    this.updateUniforms()

    this.createTexture( options.texture )

    gl.enable( gl.BLEND )
    gl.enable( gl.CULL_FACE )
    gl.blendFunc( gl.SRC_ALPHA, gl.ONE )
    gl[ options.depthTest ? 'enable' : 'disable' ]( gl.DEPTH_TEST )

    if ( options.autosize )
      window.addEventListener( 'resize', e => this.resize( e ), false )
    if ( options.mousemove )
      window.addEventListener( 'mousemove', e => this.mousemove( e ), false )

    this.resize()

    this.update = this.update.bind( this )
    this.time = { start: performance.now(), old: performance.now() }
    this.update()

  }

  mousemove( e ) {

    let x = e.pageX / this.width * 2 - 1
    let y = e.pageY / this.height * 2 - 1

    this.uniforms.mousemove = [ x, y ]

  }

  resize( e ) {

    const holder = this.holder
    const canvas = this.canvas
    const gl = this.gl

    const width = this.width = holder.offsetWidth
    const height = this.height = holder.offsetHeight
    const aspect = this.aspect = width / height
    const dpi = this.dpi = devicePixelRatio

    canvas.width = width * dpi
    canvas.height = height * dpi
    canvas.style.width = width + 'px'
    canvas.style.height = height + 'px'

    gl.viewport( 0, 0, width * dpi, height * dpi )
    gl.clearColor( 0, 0, 0, 0 )

    this.uniforms.resolution = [ width, height ]
    this.uniforms.projection = this.setProjection( aspect )

    this.onResize( width, height, dpi )

  }

  setProjection( aspect ) {

    const camera = this.camera

    if ( camera.perspective ) {

      camera.aspect = aspect

      const fovRad = camera.fov * ( Math.PI / 180 )
      const f = Math.tan( Math.PI * 0.5 - 0.5 * fovRad )
      const rangeInv = 1.0 / ( camera.near - camera.far )

      const matrix = [
        f / camera.aspect, 0, 0, 0,
        0, f, 0, 0,
        0, 0, (camera.near + camera.far) * rangeInv, -1,
        0, 0, camera.near * camera.far * rangeInv * 2, 0
      ]

      matrix[ 14 ] += camera.z
      matrix[ 15 ] += camera.z

      return matrix

    } else {

      return [
         2 / this.width, 0, 0, 0,
         0, -2 / this.height, 0, 0,
         0, 0, 1, 0,
        -1, 1, 0, 1,
      ]

    }

  }

  createShader( type, source ) {

    const gl = this.gl
    const shader = gl.createShader( type )

    gl.shaderSource( shader, source )
    gl.compileShader( shader )

    if ( gl.getShaderParameter (shader, gl.COMPILE_STATUS ) ) {

      return shader

    } else {

      console.log( gl.getShaderInfoLog( shader ) )
      gl.deleteShader( shader )

    }

  }

  createProgram( vertex, fragment ) {

    const gl = this.gl

    const vertexShader = this.createShader( gl.VERTEX_SHADER, vertex )
    const fragmentShader = this.createShader( gl.FRAGMENT_SHADER, fragment )

    const program = gl.createProgram()

    gl.attachShader( program, vertexShader )
    gl.attachShader( program, fragmentShader )
    gl.linkProgram( program )

    if ( gl.getProgramParameter( program, gl.LINK_STATUS ) ) {

      gl.useProgram( program )
      this.program = program

    } else {

      console.log( gl.getProgramInfoLog( program ) )
      gl.deleteProgram( program )

    }

  }

  createUniforms( data ) {

    const gl = this.gl
    const uniforms = this.data.uniforms = data
    const values = this.uniforms = {}

    Object.keys( uniforms ).forEach( name => {

      const uniform = uniforms[ name ]

      uniform.location = gl.getUniformLocation( this.program, 'u_' + name )

      Object.defineProperty( values, name, {
        set: value => {

          uniforms[ name ].value = value
          this.setUniform( name, value )

        },
        get: () => uniforms[ name ].value
      } )

    } )

  }

  setUniform( name, value ) {

    const gl = this.gl
    const uniform = this.data.uniforms[ name ]

    uniform.value = value

    switch ( uniform.type ) {
      case 'int': {
        gl.uniform1i( uniform.location, value )
        break
      }
      case 'float': {
        gl.uniform1f( uniform.location, value )
        break
      }
      case 'vec2': {
        gl.uniform2f( uniform.location, ...value )
        break
      }
      case 'vec3': {
        gl.uniform3f( uniform.location, ...value )
        break
      }
      case 'vec4': {
        gl.uniform4f( uniform.location, ...value )
        break
      }
      case 'mat2': {
        gl.uniformMatrix2fv( uniform.location, false, value )
        break
      }
      case 'mat3': {
        gl.uniformMatrix3fv( uniform.location, false, value )
        break
      }
      case 'mat4': {
        gl.uniformMatrix4fv( uniform.location, false, value )
        break
      }
    }

    // ivec2       : uniform2i,
    // ivec3       : uniform3i,
    // ivec4       : uniform4i,
    // sampler2D   : uniform1i,
    // samplerCube : uniform1i,
    // bool        : uniform1i,
    // bvec2       : uniform2i,
    // bvec3       : uniform3i,
    // bvec4       : uniform4i,

  }

  updateUniforms() {

    const gl = this.gl
    const uniforms = this.data.uniforms

    Object.keys( uniforms ).forEach( name => {

      const uniform = uniforms[ name ]

      this.uniforms[ name ] = uniform.value

    } )

  }

  createBuffers( data ) {

    const gl = this.gl
    const buffers = this.data.buffers = data
    const values = this.buffers = {}

    Object.keys( buffers ).forEach( name => {

      const buffer = buffers[ name ]

      buffer.buffer = this.createBuffer( 'a_' + name, buffer.size )

      Object.defineProperty( values, name, {
        set: data => {

          buffers[ name ].data = data
          this.setBuffer( name, data )

          if ( name == 'position' )
            this.count = buffers.position.data.length / 3

        },
        get: () => buffers[ name ].data
      } )

    } )

  }

  createBuffer( name, size ) {

    const gl = this.gl
    const program = this.program

    const index = gl.getAttribLocation( program, name )
    const buffer = gl.createBuffer()

    gl.bindBuffer( gl.ARRAY_BUFFER, buffer )
    gl.enableVertexAttribArray( index )
    gl.vertexAttribPointer( index, size, gl.FLOAT, false, 0, 0 )

    return buffer

  }

  setBuffer( name, data ) {

    const gl = this.gl
    const buffers = this.data.buffers

    if ( name == null && ! gl.bindBuffer( gl.ARRAY_BUFFER, null ) ) return

    gl.bindBuffer( gl.ARRAY_BUFFER, buffers[ name ].buffer )
    gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( data ), gl.STATIC_DRAW )

  }

  updateBuffers() {

    const gl = this.gl
    const buffers = this.buffers

    Object.keys( buffers ).forEach( name =>
      buffers[ name ] = buffer.data
    )

    this.setBuffer( null )

  }

  createTexture( src ) {

    const gl = this.gl
    const texture = gl.createTexture()

    gl.bindTexture( gl.TEXTURE_2D, texture )
    gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array( [ 0, 0, 0, 0 ] ) )

    this.texture = texture

    if ( src ) {

      this.uniforms.hasTexture = 1
      this.loadTexture( src )

    }

  }

  loadTexture( src ) {

    const gl = this.gl
    const texture = this.texture

    const textureImage = new Image()

    textureImage.onload = () => {

      gl.bindTexture( gl.TEXTURE_2D, texture )

      gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textureImage )

      gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR )
      gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR )

      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)

      // gl.generateMipmap( gl.TEXTURE_2D )

    }

    textureImage.src = src

  }

  update() {

    const gl = this.gl

    const now = performance.now()
    const elapsed = ( now - this.time.start ) / 5000
    const delta = now - this.time.old
    this.time.old = now

    this.uniforms.time = elapsed

    if ( this.count > 0 ) {
      gl.clear( gl.COLORBUFFERBIT )
      gl.drawArrays( gl.POINTS, 0, this.count )
    }

    this.onUpdate( delta )

    requestAnimationFrame( this.update )

  }

}

const pointSize = 3

const waves = new ShaderProgram( document.querySelector( '.waves' ), {
  texture: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAb1BMVEUAAAD///8v0wLRAAAAJHRSTlMAC/goGvDhmwcExrVjWzrm29TRqqSKenRXVklANSIUE8mRkGpv+HOfAAABCElEQVQ4y4VT13LDMAwLrUHteO+R9f/fWMfO6dLaPeKVEECRxOULWsEGpS9nULDwia2Y+ALqUNbAWeg775zv+sA4/FFRMxt8U2FZFCVWjR/YrH4/H9sarclSKdPMWKzb8VsEeHB3m0shkhVCyNzeXeAQ9Xl4opEieX2QCGnwGbj6GMyjw9t1K0fK9YZunPXeAGsfJtYjwzxaBnozGGorYz0ypK2HzQSYx1y8DgSRo2ewOiyh2QWOEk1Y9OrQV0a8TiBM1a8eMHWYnRMy7CZ4t1CmyRkhSUvP3gRXyHOCLBxNoC3IJv//ZrJ/kxxUHPUB+6jJZZHrpg6GOjnqaOmzp4NDR48OLxn/H27SRQ08S0ZJAAAAAElFTkSuQmCC',
  uniforms: {
    size: { type: 'float', value: pointSize },
    field: { type: 'vec3', value: [ 0, 0, 0 ] },
    speed: { type: 'float', value: 5 },
  },
  vertex: `
    #define M_PI 3.1415926535897932384626433832795

    precision highp float;

    attribute vec4 a_position;
    attribute vec4 a_color;

    uniform float u_time;
    uniform float u_size;
    uniform float u_speed;
    uniform vec3 u_field;
    uniform mat4 u_projection;

    varying vec4 v_color;

    void main() {

      vec3 pos = a_position.xyz;

      pos.y += (
        cos(pos.x / u_field.x * M_PI * 8.0 + u_time * u_speed) +
        sin(pos.z / u_field.z * M_PI * 8.0 + u_time * u_speed)
      ) * u_field.y;

      gl_Position = u_projection * vec4( pos.xyz, a_position.w );
      gl_PointSize = ( u_size / gl_Position.w ) * 100.0;

      v_color = a_color;

    }`,
  fragment: `
    precision highp float;

    uniform sampler2D u_texture;

    varying vec4 v_color;

    void main() {

      gl_FragColor = v_color * texture2D(u_texture, gl_PointCoord);

    }`,
  onResize( w, h, dpi ) {

    const position = [], color = []

    const width = 400 * ( w / h )
    const depth = 400
    const height = 3
    const distance = 5

    for ( let x = 0; x < width; x += distance ) {
      for ( let z = 0; z < depth; z+= distance ) {

        position.push( - width / 2 + x, -30, -depth / 2 + z )
        color.push( 0, 1 - ( x / width ) * 1, 0.5 + x / width * 0.5, z / depth )

      }
    }

    this.uniforms.field = [ width, height, depth ]

    this.buffers.position = position
    this.buffers.color = color

    this.uniforms.size = ( h / 400) * pointSize * dpi

  },
} )
</script>

</body>
</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/963744.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信小程序新版隐私协议弹窗实现最新版

1. 微信小程序又双叒叕更新了 2023.08.22更新&#xff1a; 以下指南中涉及的 getPrivacySetting、onNeedPrivacyAuthorization、requirePrivacyAuthorize 等接口目前可以正常接入调试。调试说明&#xff1a; 在 2023年9月15号之前&#xff0c;在 app.json 中配置 __usePriva…

【iOS】属性关键字

文章目录 前言一、深拷贝与浅拷贝1、OC的拷贝方式有哪些2. OC对象实现的copy和mutableCopy分别为浅拷贝还是深拷贝&#xff1f;3. 自定义对象实现的copy和mutableCopy分别为浅拷贝还是深拷贝&#xff1f;4. 判断当前的深拷贝的类型&#xff1f;(区别是单层深拷贝还是完全深拷贝…

Mac移动硬盘怎么识别PC电脑?

如果你拥有一台Mac设备&#xff0c;总会遇到尴尬的那一刻——你在Mac上用得好好的移动硬盘怎么都不能被PC识别到。又或者你朋友在PC上用得好好的移动硬盘&#xff0c;连上你的Mac后&#xff0c;Mac里的文件死活就是拷贝不进移动硬盘里。这种坑&#xff0c;相信大多数使用Mac的小…

Windows无法删除分区怎么办?

我们知道Windows系统内置的磁盘管理工具是一个很实用的程序&#xff0c;可以帮助我们完成很多磁盘分区相关的基础操作&#xff0c;比如当我们想要删除硬盘上的某一个分区时&#xff0c;先想到的可能会是磁盘管理工具。但是当我们准备在磁盘管理工具中删除某个分区时&#xff0c…

Upload-labs 1~15 通关详细教程

文章目录 Upload-labs 1~15 通关详细教程Pass-01-前端js验证Pass-02-后端MIME验证Pass-03-黑名单验证Pass-04-黑名单验证.htaccessPass-05-文件后缀名大小写绕过Pass-06-文件后缀名空格绕过Pass-07-文件后缀名点绕过Pass-08-文件后缀名::$DATA绕过Pass-09-点空格点空格绕过Pass…

Python爬虫乱码问题之encoding和apparent_encoding的区别

encoding是从http中的header中的charset字段中提取的编码方式&#xff0c;若header中没有charset字段则默认为ISO-8859-1编码模式&#xff0c;则无法解析中文&#xff0c;这是乱码的原因 apparent_encoding会从网页的内容中分析网页编码的方式&#xff0c;所以apparent_encodi…

Windows10上使用llama-recipes(LoRA)来对llama-2-7b做fine-tune

刚刚在Windows10上搭建环境来对llama2做finetune&#xff0c;里面坑还是挺多的&#xff0c;这里把印象中的坑整理了一下以作备忘。 llama-recipes是meta的开源项目&#xff0c;Github地址为&#xff1a;GitHub - facebookresearch/llama-recipes: Examples and recipes for Ll…

【仿写spring之ioc篇】四、实现bean的初始化阶段

BeanPostProcessor 在Bean的初始化阶段有前置和后置方法&#xff0c;这个方法是通过BeanPostProcessor来管理的&#xff0c;下面我们对原有的项目结构做小小的更改。 对启动类作出修改&#xff0c;先检查有没有BeanPostProcessor的实现类&#xff0c;有的话就使用&#xff0c…

搜索二维矩阵 II

题目链接 搜索二维矩阵 II 题目描述 注意点 矩阵具有以下特性&#xff1a; 每行的元素从左到右升序排列。 每列的元素从上到下升序排列。 解答思路 最初想到使用深度优先遍历剪枝实现&#xff0c;但是运行后超出时间限制了可以直接遍历整个矩阵查找&#xff0c;虽然不超时…

【sgLazyTree】自定义组件:动态懒加载el-tree树节点数据,实现增删改、懒加载及局部数据刷新。

特性 可以自定义主键、配置选项支持预定义节点图标&#xff1a;folder文件夹|normal普通样式多个提示文本可以自定义支持动态接口增删改节点 sgLazyTree源码 <template><div :class"$options.name" v-loading"rootLoading"><div class&qu…

wireshark抓包分析

题目一&#xff1a;Cephalopod(图片提取) 打开下载好的数据包&#xff1a;CtrlF 按照如图选择分组字节流&#xff0c;选择字符串&#xff0c;输入‘flag’筛选出数据包&#xff1b; 点击筛选出来的一条数据包&#xff0c;右键选择追踪tcp流&#xff1b; 然后可以看到png的字样…

2.92-KFKG射频微波同轴连接器的电气特性

2.92mm连接器的名称是以其外导体内径命名的&#xff0c;采用空气介质工作频率高达40GHz,可与SMA和3.5mm连接器互换对插。优越的电性能、可靠的连接尤其适用于测试系统和武*装备&#xff0c;成为国际上应用最为广泛的毫米微波连接器之一。 电气特性&#xff1a; 特性阻抗&…

解读《生成式人工智能服务管理暂行办法》

《生成式人工智能服务管理暂行办法》 第一章 总 则第二章 技术发展与治理第三章 服务规范第四章 监督检查和法律责任第五章 附 则 以ChatGPT为代表的现象级互联网应用的出现&#xff0c;掀起了人工智能领域新一轮技术浪潮。作为新一代信息技术&#xff0c;生成式人工智能通过…

正点原子I.MX6ull应用编程 看门狗实验 /dev/watchdog: Device or resource busy

记录自己学习正点原子I.Mx6ull应用编程教程中遇到的坑点和问题 按着正点原子<<I.MX6U嵌入式Linux C应用编程指南>>学习看门狗应用编程&#xff0c;在运行程序的时候出现 open error: /dev/watchdog: Device or resource busy 可以看到watchdog忙碌&#xff0c;看起…

Python所有方向的学习路线图!!

学习路线图上面写的是某个方向建议学习和掌握的知识点汇总&#xff0c;举个例子&#xff0c;如果你要学习爬虫&#xff0c;那么你就去学Python爬虫学习路线图上面的知识点&#xff0c;这样学下来之后&#xff0c;你的知识体系是比较全面的&#xff0c;比起在网上找到什么就学什…

MyBatis——MyBatis数据源与连接池

摘要 博文主要介绍MyBatis数据源与连接池&#xff0c;更好的理解MyBatis数据源与连接池。 一、MyBatis数据源DataSource分类 MyBatis把数据源DataSource分为三种&#xff1a; UNPOOLED 不使用连接池的数据源POOLED 使用连接池的数据源JNDI 使用JNDI实现的数据源 MyBatis内…

ESP32C3 LuatOS RC522①写入数据并读取M1卡

LuatOS RC522官方示例 官方示例没有针对具体开发板&#xff0c;现以ESP32C3开发板为例。 选用的RC522模块 ESP32C3-CORE开发板 注意ESP32C3的 SPI引脚位置&#xff0c;SPI的id2 示例代码 -- LuaTools需要PROJECT和VERSION这两个信息 PROJECT "helloworld" VERSIO…

网络地址转换技术NAT(第九课)

一 什么是NAT&#xff1f; NAT是网络地址转换的缩写&#xff0c;是一种在计算机网络中使用的技术&#xff0c;可以将私有地址转换为公共地址&#xff0c;从而实现本地网络与公共网络的互联。NAT工作在网络层&#xff0c;可以隐藏内部网络中的IP地址和端口号&#xff0c;从而增…

在数字时代中,该怎么保留温暖的传统

在我们现代快节奏的数字时代&#xff0c;许多人会不禁思考&#xff0c;传统的壁炉是否还有存在的必要。毕竟&#xff0c;现代家庭通常配备了先进的暖气系统和电子设备&#xff0c;能够在寒冷的日子里提供温暖。然而&#xff0c;尽管科技的进步使得家庭取暖变得更加便捷&#xf…

SpringCloudAlibaba常用组件

SpringCloudAlibaba常用组件 微服务概念 1.1 单体、分布式、集群 单体 ⼀个系统业务量很⼩的时候所有的代码都放在⼀个项⽬中就好了&#xff0c;然后这个项⽬部署在⼀台服务器上就 好了。整个项⽬所有的服务都由这台服务器提供。这就是单机结构。 单体应⽤开发简单,部署测试…