three完全开源扩展案例02-跳动的音乐

在这里插入图片描述
更多案例尽在https://threelab.cn/

演示地址

import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";

let mediaElement;
let analyser;
let scene;
let camera;
let renderer;
let controls;
let mesh;

const fftSize = 4096;
const clock = new THREE.Clock();
const uniform = {
  uTime: { value: 0 },
  tAudioData: { value: 0 },
  uStrength: { value: 0 },
};
const box = document.getElementById("box");

const init = () => {
  // Scene
  scene = new THREE.Scene();

  const material = new THREE.MeshStandardMaterial();
  material.roughness = 0.7;
  const depthMaterial = new THREE.MeshDepthMaterial({
    depthPacking: THREE.RGBADepthPacking,
  });

  material.onBeforeCompile = (shader) => {
    shader.uniforms.uTime = uniform.uTime;
    shader.uniforms.uStrength = uniform.uStrength;

    shader.vertexShader = shader.vertexShader.replace(
      "#include <common>",
      `
              #include <common>
              attribute float aOffset;
              varying vec2 vUv;
              uniform float uTime;
              uniform float uStrength;

              //	Simplex 4D Noise
  //	by Ian McEwan, Ashima Arts
  //
  vec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}
  float permute(float x){return floor(mod(((x*34.0)+1.0)*x, 289.0));}
  vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}
  float taylorInvSqrt(float r){return 1.79284291400159 - 0.85373472095314 * r;}

  vec4 grad4(float j, vec4 ip)
  {
    const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0);
    vec4 p,s;

    p.xyz = floor( fract (vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0;
    p.w = 1.5 - dot(abs(p.xyz), ones.xyz);
    s = vec4(lessThan(p, vec4(0.0)));
    p.xyz = p.xyz + (s.xyz*2.0 - 1.0) * s.www;

    return p;
  }

  float simplexNoise4d(vec4 v)
  {
    const vec2  C = vec2( 0.138196601125010504,  // (5 - sqrt(5))/20  G4
                          0.309016994374947451); // (sqrt(5) - 1)/4   F4
    // First corner
    vec4 i  = floor(v + dot(v, C.yyyy) );
    vec4 x0 = v -   i + dot(i, C.xxxx);

    // Other corners

    // Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI)
    vec4 i0;

    vec3 isX = step( x0.yzw, x0.xxx );
    vec3 isYZ = step( x0.zww, x0.yyz );
    //  i0.x = dot( isX, vec3( 1.0 ) );
    i0.x = isX.x + isX.y + isX.z;
    i0.yzw = 1.0 - isX;

    //  i0.y += dot( isYZ.xy, vec2( 1.0 ) );
    i0.y += isYZ.x + isYZ.y;
    i0.zw += 1.0 - isYZ.xy;

    i0.z += isYZ.z;
    i0.w += 1.0 - isYZ.z;

    // i0 now contains the unique values 0,1,2,3 in each channel
    vec4 i3 = clamp( i0, 0.0, 1.0 );
    vec4 i2 = clamp( i0-1.0, 0.0, 1.0 );
    vec4 i1 = clamp( i0-2.0, 0.0, 1.0 );

    //  x0 = x0 - 0.0 + 0.0 * C
    vec4 x1 = x0 - i1 + 1.0 * C.xxxx;
    vec4 x2 = x0 - i2 + 2.0 * C.xxxx;
    vec4 x3 = x0 - i3 + 3.0 * C.xxxx;
    vec4 x4 = x0 - 1.0 + 4.0 * C.xxxx;

    // Permutations
    i = mod(i, 289.0);
    float j0 = permute( permute( permute( permute(i.w) + i.z) + i.y) + i.x);
    vec4 j1 = permute( permute( permute( permute (
               i.w + vec4(i1.w, i2.w, i3.w, 1.0 ))
             + i.z + vec4(i1.z, i2.z, i3.z, 1.0 ))
             + i.y + vec4(i1.y, i2.y, i3.y, 1.0 ))
             + i.x + vec4(i1.x, i2.x, i3.x, 1.0 ));
    // Gradients
    // ( 7*7*6 points uniformly over a cube, mapped onto a 4-octahedron.)
    // 7*7*6 = 294, which is close to the ring size 17*17 = 289.

    vec4 ip = vec4(1.0/294.0, 1.0/49.0, 1.0/7.0, 0.0) ;

    vec4 p0 = grad4(j0,   ip);
    vec4 p1 = grad4(j1.x, ip);
    vec4 p2 = grad4(j1.y, ip);
    vec4 p3 = grad4(j1.z, ip);
    vec4 p4 = grad4(j1.w, ip);

    // Normalise gradients
    vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
    p0 *= norm.x;
    p1 *= norm.y;
    p2 *= norm.z;
    p3 *= norm.w;
    p4 *= taylorInvSqrt(dot(p4,p4));

    // Mix contributions from the five corners
    vec3 m0 = max(0.6 - vec3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0);
    vec2 m1 = max(0.6 - vec2(dot(x3,x3), dot(x4,x4)            ), 0.0);
    m0 = m0 * m0;
    m1 = m1 * m1;
    return 49.0 * ( dot(m0*m0, vec3( dot( p0, x0 ), dot( p1, x1 ), dot( p2, x2 )))
                 + dot(m1*m1, vec2( dot( p3, x3 ), dot( p4, x4 ) ) ) ) ;

  }

          `
    );

    shader.vertexShader = shader.vertexShader.replace(
      "#include <fog_vertex>",
      `#include <fog_vertex>
            vec3 newPos = position;
            float strength = uStrength;
            newPos += normal * simplexNoise4d(vec4(position, uTime)) * strength;
            gl_Position = projectionMatrix * modelViewMatrix * vec4(newPos, 1.0);

          `
    );
    shader.fragmentShader = shader.fragmentShader.replace(
      "#include <common>",
      `#include <common>
          uniform float uTime;
          uniform float uStrength;
        `
    );

    shader.fragmentShader = shader.fragmentShader.replace(
      "#include <opaque_fragment>",
      `#include <opaque_fragment>
          gl_FragColor = vec4(vNormal, 1.);
        `
    );
  };
  depthMaterial.onBeforeCompile = (shader) => {
    shader.uniforms.uTime = uniform.uTime;
    shader.uniforms.uStrength = uniform.uStrength;

    shader.vertexShader = shader.vertexShader.replace(
      "#include <common>",
      `
              #include <common>
              attribute float aOffset;
              varying vec2 vUv;
              uniform float uTime;
              uniform float uStrength;

              //	Simplex 4D Noise
  //	by Ian McEwan, Ashima Arts
  //
  vec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}
  float permute(float x){return floor(mod(((x*34.0)+1.0)*x, 289.0));}
  vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}
  float taylorInvSqrt(float r){return 1.79284291400159 - 0.85373472095314 * r;}

  vec4 grad4(float j, vec4 ip)
  {
    const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0);
    vec4 p,s;

    p.xyz = floor( fract (vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0;
    p.w = 1.5 - dot(abs(p.xyz), ones.xyz);
    s = vec4(lessThan(p, vec4(0.0)));
    p.xyz = p.xyz + (s.xyz*2.0 - 1.0) * s.www;

    return p;
  }

  float simplexNoise4d(vec4 v)
  {
    const vec2  C = vec2( 0.138196601125010504,  // (5 - sqrt(5))/20  G4
                          0.309016994374947451); // (sqrt(5) - 1)/4   F4
    // First corner
    vec4 i  = floor(v + dot(v, C.yyyy) );
    vec4 x0 = v -   i + dot(i, C.xxxx);

    // Other corners

    // Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI)
    vec4 i0;

    vec3 isX = step( x0.yzw, x0.xxx );
    vec3 isYZ = step( x0.zww, x0.yyz );
    //  i0.x = dot( isX, vec3( 1.0 ) );
    i0.x = isX.x + isX.y + isX.z;
    i0.yzw = 1.0 - isX;

    //  i0.y += dot( isYZ.xy, vec2( 1.0 ) );
    i0.y += isYZ.x + isYZ.y;
    i0.zw += 1.0 - isYZ.xy;

    i0.z += isYZ.z;
    i0.w += 1.0 - isYZ.z;

    // i0 now contains the unique values 0,1,2,3 in each channel
    vec4 i3 = clamp( i0, 0.0, 1.0 );
    vec4 i2 = clamp( i0-1.0, 0.0, 1.0 );
    vec4 i1 = clamp( i0-2.0, 0.0, 1.0 );

    //  x0 = x0 - 0.0 + 0.0 * C
    vec4 x1 = x0 - i1 + 1.0 * C.xxxx;
    vec4 x2 = x0 - i2 + 2.0 * C.xxxx;
    vec4 x3 = x0 - i3 + 3.0 * C.xxxx;
    vec4 x4 = x0 - 1.0 + 4.0 * C.xxxx;

    // Permutations
    i = mod(i, 289.0);
    float j0 = permute( permute( permute( permute(i.w) + i.z) + i.y) + i.x);
    vec4 j1 = permute( permute( permute( permute (
               i.w + vec4(i1.w, i2.w, i3.w, 1.0 ))
             + i.z + vec4(i1.z, i2.z, i3.z, 1.0 ))
             + i.y + vec4(i1.y, i2.y, i3.y, 1.0 ))
             + i.x + vec4(i1.x, i2.x, i3.x, 1.0 ));
    // Gradients
    // ( 7*7*6 points uniformly over a cube, mapped onto a 4-octahedron.)
    // 7*7*6 = 294, which is close to the ring size 17*17 = 289.

    vec4 ip = vec4(1.0/294.0, 1.0/49.0, 1.0/7.0, 0.0) ;

    vec4 p0 = grad4(j0,   ip);
    vec4 p1 = grad4(j1.x, ip);
    vec4 p2 = grad4(j1.y, ip);
    vec4 p3 = grad4(j1.z, ip);
    vec4 p4 = grad4(j1.w, ip);

    // Normalise gradients
    vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
    p0 *= norm.x;
    p1 *= norm.y;
    p2 *= norm.z;
    p3 *= norm.w;
    p4 *= taylorInvSqrt(dot(p4,p4));

    // Mix contributions from the five corners
    vec3 m0 = max(0.6 - vec3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0);
    vec2 m1 = max(0.6 - vec2(dot(x3,x3), dot(x4,x4)            ), 0.0);
    m0 = m0 * m0;
    m1 = m1 * m1;
    return 49.0 * ( dot(m0*m0, vec3( dot( p0, x0 ), dot( p1, x1 ), dot( p2, x2 )))
                 + dot(m1*m1, vec2( dot( p3, x3 ), dot( p4, x4 ) ) ) ) ;

  }

          `
    );

    shader.vertexShader = shader.vertexShader.replace(
      "#include <clipping_planes_vertex>",
      `#include <clipping_planes_vertex>
            vec3 newPos = position;
            float strength = uStrength;
            newPos += normal * simplexNoise4d(vec4(position, uTime)) * strength;
            gl_Position = projectionMatrix * modelViewMatrix * vec4(newPos, 1.0);
          `
    );
  };
  // const geometry = new THREE.SphereGeometry(0.5, 256, 256);
  const geometry = new THREE.IcosahedronGeometry(2.5, 50);
  mesh = new THREE.Mesh(geometry, material);
  mesh.customDepthMaterial = depthMaterial;
  mesh.castShadow = true;

  scene.add(mesh);
  const plane = new THREE.Mesh(
    new THREE.PlaneGeometry(25, 25),
    new THREE.MeshStandardMaterial()
  );
  plane.rotation.x = -Math.PI * 0.5;
  plane.position.y = -5;
  plane.receiveShadow = true;

  scene.add(plane);

  /**
   * Lights
   */
  const ambientLight = new THREE.AmbientLight(0xffffff, 0.3);
  scene.add(ambientLight);

  const directionalLight = new THREE.DirectionalLight("#ffffff", 0.3);
  directionalLight.castShadow = true;
  directionalLight.shadow.mapSize.set(1024, 1024);
  directionalLight.shadow.camera.far = 40;
  directionalLight.castShadow = true;
  directionalLight.position.set(2, 2, -2);
  scene.add(directionalLight);

  const directionalLightCameraHelper = new THREE.CameraHelper(
    directionalLight.shadow.camera
  );
  // 关闭助手
  scene.add(directionalLightCameraHelper);
  /**
   * Sizes
   */
  const sizes = {
    width: box.clientWidth,
    height: box.clientHeight,
  };
  window.addEventListener("resize", () => {
    // Update sizes
    sizes.width = box.clientWidth;
    sizes.height = box.clientHeight;
    // Update camera
    camera.aspect = sizes.width / sizes.height;

    camera.updateProjectionMatrix();
    // Update renderer
    renderer.setSize(sizes.width, sizes.height);
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
  });

  /**
   * Camera
   */
  // Base camera
  camera = new THREE.PerspectiveCamera(
    75,
    sizes.width / sizes.height,
    0.1,
    100
  );
  camera.position.set(4, 4, 6);
  scene.add(camera);

  /**
   * Renderer
   */
  renderer = new THREE.WebGLRenderer();
  renderer.setSize(sizes.width, sizes.height);
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
  renderer.setClearColor("#111");
  renderer.shadowMap.enabled = true;
  box.appendChild(renderer.domElement);
  // Controls
  controls = new OrbitControls(camera, renderer.domElement);
  // controls.enableDamping = true;
};

const tick = () => {
  const elapsedTime = clock.getElapsedTime();
  controls?.update();

  // Update material

  updateOffsetData();

  if (uniform?.uTime) {
    uniform.uTime.value = elapsedTime;
  }

  // Render
  renderer.render(scene, camera);
  // Call tick again on the next frame
  window.requestAnimationFrame(tick);
};

const updateOffsetData = () => {
  if (analyser?.getFrequencyData) {
    analyser.getFrequencyData();
    const analyserData = analyser?.data;
    let sum = 0;
    for (let i = 0; i < analyserData.length; i++) {
      sum += analyserData[i];
    }
    sum /= analyserData.length * 25.5;
    uniform.uStrength.value = sum;
  }
};

const play = () => {
  const listener = new THREE.AudioListener();
  const audio = new THREE.Audio(listener);
  const file = "/three-cesium-examples/public/files/audio/Avicii-WeBurn.mp3";
  mediaElement = new Audio(file);
  mediaElement.play();
  audio.setMediaElementSource(mediaElement);
  analyser = new THREE.AudioAnalyser(audio, fftSize);
};

const pause = () => {
  mediaElement.pause();
};

const createButton = () => {
  const playButton = document.createElement("button");
  playButton.textContent = "Play";
  playButton.style.position = "absolute";
  playButton.style.right = "140px";
  playButton.style.top = "30px";
  playButton.style.padding = "10px 20px";
  box.appendChild(playButton);
  playButton.addEventListener("click", play);

  const pauseButton = document.createElement("button");
  pauseButton.textContent = "Pause";
  pauseButton.style.position = "absolute";
  pauseButton.style.right = "30px";
  pauseButton.style.top = "30px";
  pauseButton.style.padding = "10px 20px";
  box.appendChild(pauseButton);
  pauseButton.addEventListener("click", pause);
};

init();
createButton();
tick();


技术交流群
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

应力 (Stress) 是指单位面积上所承受的力

应力 (Stress) 是指单位面积上所承受的力 flyfish 轴向力 轴向力 (Axial Force) 是指沿着物体的纵轴施加的力。对于一根杆或柱子&#xff0c;轴向力可以是拉力或压力&#xff0c;具体取决于力的方向。 拉力 (Tensile Force)&#xff1a;使物体拉长的力。 压力 (Compressive…

上班摸鱼吗?一文详解代码生成神器-Velocity

引言 “我不是在教你学坏,而是教你如何提高生产效率。” ----------- 牛顿 人类社会能够一直进步发展出现在的文明世界,最大的一个原因就是这个世界上懒人居多,懒人为了偷懒就需要提高生产效率,效率提高节省下来的时间才能创造出艺术、娱乐以及更高效率的科学技术。程序员…

机器学习(五) -- 监督学习(7) --SVM2

系列文章目录及链接 上篇&#xff1a;机器学习&#xff08;五&#xff09; -- 监督学习&#xff08;7&#xff09; --SVM1 下篇&#xff1a; 前言 tips&#xff1a;标题前有“***”的内容为补充内容&#xff0c;是给好奇心重的宝宝看的&#xff0c;可自行跳过。文章内容被“文…

kubernetes集群如何更改所有节点IP

kubernetes集群如何更改所有节点IP 情景描述更换IP前的准备工作更换IP后的工作--master更换IP后的工作--node节点重新部署之前那些服务 情景描述 我有三台服务器&#xff0c;想要将其组成了一个kubernetes集群&#xff0c;在部署之前&#xff0c;我就对其进行了固定IP的操作&a…

Adobe Illustrator 2021 for mac/Win:专业矢量图形设计的巅峰之作

Adobe Illustrator 2021作为Adobe公司旗下的旗舰矢量图形设计软件&#xff0c;无论是对于Mac还是Windows用户&#xff0c;都提供了强大而灵活的设计工具&#xff0c;让设计师们能够轻松应对各种复杂的图形设计挑战。这款软件以其卓越的性能、丰富的功能和友好的用户界面&#x…

【正点原子K210连载】第二十章 machine.I2C类实验 摘自【正点原子】DNK210使用指南-CanMV版指南

1&#xff09;实验平台&#xff1a;正点原子ATK-DNK210开发板 2&#xff09;平台购买地址https://detail.tmall.com/item.htm?id731866264428 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/docs/boards/xiaoxitongban 第二十章 machine.I2…

java并发编程之美-第1章 并发编程线程基础-线程的创建与运行

文章目录 1.什么是线程2. 线程创建和运行 1.什么是线程 进程是操作系统进行资源分配和调度的基本单位&#xff0c;线程是 CPU 分配的基本单位。 程序计数器用来记录线程当前要执行的指令地址。CPU一般是使用时间片轮转方式让线程轮询占用的&#xff0c;程序计数器是记录线程…

MVC 返回集合方法,以及分页

返回一个数据集方法 返回多个数据集方法 》》定义一个Model public class IndexMoel {public List<UserGroup> UserGroup{get;set;}public List<User> User{get;set;}}》》》控制器 //db 是 EF 中的上下文 var listnew IndexModel(); list.UserGroupdb.UserGro…

Java--static详解

1.static静态的意义&#xff0c;加在属性面前就为静态属性&#xff1b;加在方法面前就为静态方法 2.如图&#xff0c;定义了一个静态属性age&#xff0c;一个非静态属性score&#xff1b; 输出语句一共四句&#xff0c;其中第三句报错&#xff0c;由于静态和非静态的区别&…

EXSI 实用指南 2024 -编译环境 Ubuntu 安装篇(二)

1. 引言 在当今的虚拟化领域&#xff0c;VMware ESXi 是备受推崇的虚拟化平台&#xff0c;广泛应用于企业和个人用户中。它以卓越的性能、稳定的运行环境和丰富的功能&#xff0c;为用户提供了高效的硬件资源管理和简化的 IT 基础设施维护。然而&#xff0c;如何在不同操作系统…

安卓逆向经典案例——XX优品(uniapp)

uni-app逆向 uniapp的目录结构 有一个io文件夹&#xff0c;下面有dcloud uniapp UniApp 可以用于开发 H5 应用&#xff0c;但它不仅仅局限于 H5 应用。UniApp 的特点包括&#xff1a; 1. 跨平台&#xff1a;可以一套代码同时生成适用于多个平台&#xff08;如 iOS、Android、…

【学习笔记】4、组合逻辑电路(上)

数字电路的分类&#xff1a;组合逻辑电路&#xff0c;时序逻辑电路。本章学习组合逻辑电路。 4.1 组合逻辑电路的分析 给定的逻辑电路&#xff0c;确定其逻辑表达式&#xff0c;列出真值表&#xff0c;得到简化后的逻辑表达式&#xff0c;分析得到其功能。 3位奇校验电路 &…

仕考网:公务员考试的录取流程

公务员考试的录取流程分很多个阶段&#xff0c;以下是流程介绍&#xff1a; 1. 报名阶段&#xff1a;考生需根据公布的招聘信息和岗位要求进行报名。 2. 笔试阶段&#xff1a;公务员笔试&#xff0c;包括申论和行测两部分。 3. 成绩公布与面试资格&#xff1a;笔试合格者将获…

什么是期权隐含波动率?期权隐含波动率和历史波动率的区别?

今天带你了解什么是期权隐含波动率&#xff1f;期权隐含波动率和历史波动率的区别&#xff1f;期权波动率反映的是价格波动程度&#xff0c;是对资产收益率不确定性的衡量&#xff0c;通常用标准差表示。 隐含波动率是根据期权市场价格反推出的波动率。它是市场参与者根据期权…

Agents 要点

一、Agents概念 人类是这个星球上最强大的 Agent。Agent是一个能感知并自主地采取行动的实体&#xff0c;这里的自主性极其关键&#xff0c;Agent要能够实现设定的目标&#xff0c;其中包括具备学习和获取知识的能力以提高自身性能。 关键点&#xff1a;感知环境、自主决策、具…

windows中超详细深度学习环境配置之安装显卡驱动、cuda、cudnn、pytorch、torchvision、pycharm

超详细介绍安装Gpu版本的pytorch深度学习环境 一、显卡驱动安装1.1 下载驱动1.2 安装驱动 二、cuda安装2.1 下载cuda2.2 安装cuda2.3 检查cuda是否安装成功 三、安装cudnn3.1 cudnn下载3.2 cudnn安装 四、安装miniconda4.1 miniconda下载4.2 miniconda安装4.3 添加环境变量 五、…

开源模型应用落地-Yi模型小试-Yi-1.5-9B-Chat-16K-入门篇(一)

一、前言 Yi 开源模型是由零一万物公司发布的一系列自然语言和多模态模型。2024年5月13日,零一万物发布了最新的 Yi-1.5系列开源模型,开源了6B/9B/34B的 base 版和 chat 版模型,并第一时间发布在ModelScope、huggingface、 wisemodel 等 AI 开源社区平台。 二、术语 2.1. 零…

无人机之机身保养

一、外观检查 1、检查机器表面整洁无划痕、无针孔凹陷擦伤、畸变等损坏情况&#xff1b; 2、晃动机身&#xff0c;仔细听机身内部有无松动零件或者螺丝在机身内部。 二、桨叶检查 1、有无裂痕、磨损、变形等缺陷&#xff0c;如有明显缺陷建议更换&#xff1b; 2、卡扣、紧…

Figma中文网?比Figma更懂你的神秘网站!

Figma奠定了在线UI设计工具的基本形式&#xff0c;许多国内设计师都在使用Figma。在本文中&#xff0c;我们将解密国内大型设计师使用的Figma灵魂合作伙伴&#xff0c;被称为Figma中文网络的即时设计资源社区。Figma中文网络UI设计工具的魅力是什么&#xff1f;让我们一起看看吧…

基于Java的校园交友网站系统

你好&#xff0c;我是专注于计算机技术研究的学姐码农小野。如果你对校园交友网站系统的构建或者相关技术感兴趣&#xff0c;欢迎私信交流。 开发语言 Java 数据库 MySQL 技术 Java语言、SpringBoot框架、B/S结构 工具 MyEclipse、Navicat、Maven 系统展示 首页 个人…