three - 2 - IT机房案例

代码git地址:https://github.com/buglas/threejs-lesson

知识点

  • 场景 Scene
  • 透视相机 PerspectiveCamera
  • 基础材质 MeshBasicMaterial
  • 几何体 BufferGeometry
  • 网格对象 Mesh
  • 渲染对象 WebGLRenderer
  • 轨道控制器 OrbitControls

项目概述

按理说,学习一门新技术的时候,没有一入门就实战的。

但是,有个三维机房的案例,确实很适合一入门就实战,因为它是很简单,很经典,也很适合我们统揽全局,看一下threejs 是如何实战的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zdCK57bk-1653464897384)(images/1-1648868935945.gif)]

等说完这个案例,我们会从基础慢慢说threejs。

1-IT机房简介

在一些安全要求比较高的企业,比如电信、网通、移动等,都有自己的独立服务器,而不是使用阿里云、腾讯云之类的第三方服务器。

这种大企业的服务器可能很多,需要装进IT 机柜里。

一般小点的企业的服务器需要二三十个机柜,而大的则需要上千个机柜。

IT 机房就是用于放置这些机柜的房间。

2-三维IT机房

随着科技的进步和信息化进程的推进,IT机房的重要性越来越高,企业需要对IT机房进行更加妥善的管理和监控,比如实时监控机房的温度和湿度。

现在市面上好点的IT 机柜都可以将其内部数据同步到服务端,这个时候,虚拟现实的三维IT机房便有了用武之地。

三维IT机房可以将机房数据可视化,让企业更好的监控和管理IT 机柜。

3-项目需求

当前这个项目先不整太复杂了,毕竟现在还是入门阶段,以后再逐步深入。

咱们先说几个IT 机房的几个常见功能:

  1. 在前端页面对IT 机房进行三维展示。
  2. 当鼠标划入IT 机柜的时候,提示当前机柜的详细信息。
  3. 一键显示机房中过热的机柜。

react+ts+threejs 开发三维IT机房

react+ts 是我当前所知的大部分企业开发三维项目的标配,所以我着这里就选择了react+ts。

1-1-建模思路

  • 简化模型,能用贴图表现的细节,就用贴图。这样可提高渲染速度。
  • 将光效融入贴图中,即模型贴图后便具备光效和体感。这样在three 中就无需打灯,即可提高开发速度,亦可提高渲染效率。

1-2-建模软件

现在市面上可以3d建模的软件有很多,3dsMax、ZRender、C4D 都可以。

我当前用的3dsMax 版本是2018,无法导出gltf 文件,所以还需要安装一个gltf 文件导出插件。

一般公司都是有专门的建模师。

1-3-模型文件

GLTF 模型文件包含了整个场景的数据,比如几何体、材质、动画、相机等。

GLTF 模型在使用起来,要比传统的obj 模型方便很多。

在导出GLTF模型后,一般会包含以下文件:

  • gltf 模型文件
  • bin文件
  • 贴图文件

1-4-规范模型的结构和命名

在建模软件中,同一类型的模型文件可以放入一个数组里,数组可以多层嵌套。

当前的机房模型比较简单,我就没有使用数组,所有的Mesh对象都是平展开的。

为了便于访问和区分模型,需要对模型进行规范命名,如机房中的IT机柜都是按照cabinet-001、cabinet-002、cabinet-003 命名的。

假设IT机柜的名称都是唯一的,那我们便可以基于这个名称从后端获取相应机柜的详细信息。

1.5 构建项目

npx create-react-app 02-machineroom --template typescript
npm install three @types/three --save

npm run start

调整一下ts的配置文件,取消strict 模式。因为如果strict为true,用threejs 写程序时,会比较麻烦。

tsconfig.json

{"compilerOptions": {……// "strict": true,……},……
}

如果当前的vscode编辑器还无法对tsx 文件做格式化,可以安装一个Prettier - Code formatter 插件。

  • react 18 对react 做了更新,index.tsx需要这样写:
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App";const container = document.getElementById("root");
const root = createRoot(container);
root.render(<App />);

创建models 文件夹,将之前的模型文件放进去。同理,在public文件夹里放一份。

在App.tsx中创建canvas画布

  • App.tsx
import React from 'react';
import './App.css';class App extends React.Component {// 建立canvas 画布render() {return <div className="App"><canvas id='canvas'></canvas></div>}
}export default App;

设置css样式,让App组件充满窗口。

  • index.css
html{height: 100%;
}
body{height: 100%;margin: 0;
}
#root{height: 100%;
}
  • App.css
.App {height: 100%;overflow: hidden;
}

建立机房对象-MachineRoom.ts

机房对象会把所有图形相关的对象都封装进去,对模型进行统一管理和渲染。

src/component/MachineRoom.ts
机房对象会把所有图形相关的对象都封装进去,对模型进行统一管理和渲染。

import {MeshBasicMaterial,MeshStandardMaterial,Mesh, PerspectiveCamera,Raycaster,Scene,Texture,TextureLoader,WebGLRenderer, Vector2
} from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'// GLTF 模型加载器
const gltfLoader=new GLTFLoader()export default class MachineRoom{// 渲染器renderer: WebGLRenderer// 场景scene: Scene// 相机camera: PerspectiveCamera// 轨道控制器controls: OrbitControls// 存放模型文件的目录modelPath: string// 初始化场景constructor(canvas: HTMLCanvasElement,modelPath: string = './models/') {this.renderer = new WebGLRenderer({ canvas })this.scene=new Scene()/*** 实例化透视相机* 45:45度* 相机宽高比例* 0.1 近裁剪距离* 1000 远裁剪距离*/this.camera = new PerspectiveCamera(45, canvas.width / canvas.height, 0.1, 1000)// 设置相机视点位置this.camera.position.set(0, 10, 15)// 相机看向0,0,0点this.camera.lookAt(0, 0, 0)/**** 实例化相机轨道控制器* 参数* camera: 相机* canvas*/this.controls = new OrbitControls(this.camera,this.renderer.domElement);this.modelPath=modelPath}// 加载GLTF模型loadGLTF(modelName: string = '') {gltfLoader.load(this.modelPath + modelName, ({ scene: { children } }) => {this.scene.add(...children);})}// 连续渲染animate() {this.renderer.render(this.scene, this.camera)requestAnimationFrame(() => {this.animate()})}
}

实例化机房对象

  • App.tsx
import React from 'react';
import './App.css';
import MachineRoom from './component/MachineRoom'//机房对象
let room: MachineRoom//canvas画布
let canvas:HTMLCanvasElementclass App extends React.Component {componentDidMount() {// 组件挂载完成,实例化机房对象渲染机房if (!canvas) { return }canvas.width = window.innerWidthcanvas.height = window.innerHeightroom=new MachineRoom(canvas)// 加载模型room.loadGLTF('machineRoom.gltf')room.animate()}// 建立canvas 画布,并通过ref 获取其HTMLCanvasElement对象render() {return <div className="App"><canvasid='canvas'ref={ele => canvas = ele}></canvas></div>}
}export default App;

效果如下:
请添加图片描述

真实的贴图如下:
请添加图片描述

对于以上的结果,我们发现模型渲染出来的太黑了,下面解决:色差问题。

修改模型材质

1. 排查色差问题:

在机房对象里打印模型

loadGLTF(modelName: string = '') {gltfLoader.load(this.modelPath + modelName, ({ scene: { children } }) => {console.log(...children);:this.scene.add(...children);})
}

请添加图片描述

分析一下children里的Mesh 对象,可以发现:所有Mesh对象的material材质都是MeshStandardMaterial 类型。

再分析一下material 中的map 贴图,可以发现其map贴图为 Texture 对象,其具备以下重要信息:

  • name 是贴图图片的名称。
  • flipY为false,即不对图像的y轴做翻转。
  • image图像源是ImageBitmap 类型。
  • wrapS 纹理横向重复,即THREE.RepeatWrapping。
  • wrapT 纹理纵向重复,即THREE.RepeatWrapping。

注:THREE.RepeatWrapping=1000

在此,我们要知道以下threejs 知识:

  • MeshStandardMaterial 材质会感光,这不是我们所需要的,我们不需要打光,需要将其材质换成MeshBasicMaterial。
  • ImageBitmap 的图像类型是渲染效果变黑的关键原因,因此需要将其换成Image() 对象。

接下来咱们就给模型换一个材质和图像源。

2. 修改模型的材质和图像源

1.为机房对象添加maps属性,用来存储纹理对象,以避免贴图的重复加载。

MachineRoom.ts


// 存储纹理对象
maps: Map<string, Texture>=new Map()

2.在加载GLTF 的时候,用changeMat()方法修改Mesh 对象的材质

MachineRoom.ts

loadGLTF(modelName: string = '') {gltfLoader.load(this.modelPath+modelName, ({ scene: { children } }) => {children.forEach((obj:Mesh) => {//断言 obj.material是 MeshStandardMaterialconst { map,color} = obj.material as MeshStandardMaterial// 修改材质this.changeMat(obj,map,color)})})
}

3.为机房对象中添加一个修改材质的方法changeMat()
MachineRoom.ts

// 修改材质
// obj: 贴图
// Texture:纹理材质
// color:颜色
changeMat(obj: Mesh, map: Texture, color: Color) {// 如果有贴图,就换一个贴图,如果没有就显示原来的颜色。if (map) {obj.material = new MeshBasicMaterial({// 添加建立纹理对象的方法map: this.crtTexture(map.name)})} else {obj.material = new MeshBasicMaterial({color})}
}

changeMat() 方法的参数:

  • obj:需要修改材质的Mesh 对象
  • map:GLTF 模型里的贴图对象
  • color:GLTF 模型的颜色

其中的if 逻辑是:若Mesh模型有贴图,就为其换一个材质和贴图;否则,就换一个材质,并继承原GLTF 模型的颜色。

4.为机房对象添加建立纹理对象的方法crtTexture() 。
MachineRoom.ts


// 创建纹理对象
crtTexture(imgName: string) {// 获取maps的纹理对象let curTexture=this.maps.get(imgName)// 如果没有纹理对象,则创建纹理对象if (!curTexture) {// new TextureLoader().load:加载纹理对象curTexture=new TextureLoader().load(this.modelPath+imgName)// 配置纹理对象的属性// flipY:纹理对象是否反转 false:不做反转curTexture.flipY = false// 在s方向重复curTexture.wrapS = 1000//在t方向重复curTexture.wrapT = 1000this.maps.set(imgName,curTexture)}// 如果有纹理对象,则直接返回,避免纹理对象的重复建立return curTexture
}

crtTexture() 会根据贴图名称建立Texture 纹理对象。

  • TextureLoader().load() 可以根据贴图路径,加载贴图,返回一个Texture 对象。
  • curTexture 的flipY、wrapS、wrapT是对原始GLTF 贴图的相应属性的继承。

效果如下:
请添加图片描述

为IT机柜添加鼠标事件

首先为机房添加2个属性,3个事件。
这2个属性是:1.机柜的集合 2.当前鼠标滑过的机柜

MachineRoom.ts

//机柜集合
cabinets: Mesh[] = []// 鼠标划入的机柜
curCabinet:Mesh// 鼠标划入机柜事件,参数为机柜对象
onMouseOverCabinet = (cabinet:Mesh) => { }// 鼠标在机柜上移动的事件,参数为鼠标在canvas画布上的坐标位
onMouseMoveCabinet = (x:number,y:number) => { }// 鼠标划出机柜的事件
onMouseOutCabinet = () => { }

2.在构造函数中,为maps 添加一个机柜的高亮贴图。之后鼠标划入机柜时,会将其贴图更换为高亮贴图。

MachineRoom.ts

  constructor(canvas: HTMLCanvasElement,modelPath: string = './models/') {……// 为maps 添加一个机柜的高亮贴图。之后鼠标划入机柜时,会将其贴图更换为高亮贴图。this.crtTexture("cabinet-hover.jpg")}

3.在加载GLTF 模型时,若模型名称包含’cabinet’,便将其存入cabinets。

loadGLTF(modelName: string = '') {this.gltfLoader.load(this.modelPath+modelName, ({ scene: { children } }) => {children.forEach((obj:Mesh) => {const {color, map} = obj.material as MeshStandardMaterialthis.changeMat(obj,map,color)// 在加载GLTF 模型时,若模型名称包含'cabinet',便将其存入cabinets。if (obj.name.includes('cabinet')) {this.cabinets.push(obj)}})this.scene.add(...children);})
}

4.在机房对象外面建立一个射线投射器,一个二维点,以避免在鼠标选择时机柜时重复实例化。

//射线投射器,可基于鼠标点和相机,在世界坐标系内建立一条射线,用于选中模型
const raycaster = new Raycaster()
//鼠标在裁剪空间中的点位
const pointer = new Vector2()

注:对于基于鼠标点和相机,用射线选择模型的原理,在WebGL的“进入三维世界”的选择立方体里有详细讲解,此处不再赘述。

5.为机房对象添加选择模型的方法selectCabinet(x,y),其参数为鼠标的canvas坐标位

// 选择机柜
selectCabinet(x:number, y:number) {const {cabinets,renderer,camera,maps,curCabinet}=thisconst { width, height } = renderer.domElement// 鼠标的canvas坐标转裁剪坐标pointer.set((x / width) * 2 - 1,-(y / height) * 2 + 1,)// 基于鼠标点的裁剪坐标位和相机设置射线投射器raycaster.setFromCamera(pointer, camera)// 选择机柜const intersect = raycaster.intersectObjects(cabinets)[0]// 当前选择的模型let intersectObj=intersect?intersect.object as Mesh:null// 若之前已有机柜被选择,且不等于当前所选择的机柜,取消之前选择的机柜的高亮if (curCabinet&&curCabinet!==intersectObj) {const material =curCabinet.material as MeshBasicMaterial// 设置贴图material.setValues({map: maps.get('cabinet.jpg')})}/* 若当前所选对象不为空:触发鼠标在机柜上移动的事件。若当前所选对象不等于上一次所选对象:更新curCabinet。将模型高亮。触发鼠标划入机柜事件。否则若上一次所选对象存在:置空curCabinet。触发鼠标划出机柜事件。*/if (intersectObj) {this.onMouseMoveCabinet(x,y)if (intersectObj !== curCabinet) {this.curCabinet=intersectObjconst material = intersectObj.material as MeshBasicMaterialmaterial.setValues({map: maps.get('cabinet-hover.jpg')})this.onMouseOverCabinet(intersectObj)}} else if(curCabinet) {this.curCabinet = nullthis.onMouseOutCabinet()}
}

6.在App.tsx 文件中,为APP 组件添加鼠标移动事件。

class App extends React.Component {……// 鼠标移动事件mouseMove({clientX,clientY}) {room.selectCabinet(clientX, clientY)}// 建立canvas 画布,并通过ref 获取其HTMLCanvasElement对象render() {return <div className="App" onMouseMove={this.mouseMove}>……</div>}
}

鼠标滑过机柜出现提示信息

其最简单的做法就是用HTML 建立一个信息面板,当鼠标在IT机柜上移动的时候,就让其随鼠标移动。

1.建立信息提示板

  • app.tsx
class App extends React.Component {
+  state = {// 信息面板的位置planePos: {left: 0, //信息面板的左侧位置top:0 // 信息面板的顶部位置},//信息面板的可见性planeDisplay: 'none',//当前机柜信息curCabinet: {//名称name:'Loading……',//温度temperature: 0,//容量capacity: 0,//服务器数量count:0}}……render() {
+    const {planePos: { left, top },planeDisplay: display,curCabinet:{name,temperature,capacity,count}} = this.statereturn <div className="App" onMouseMove={this.mouseMove}><canvasid='canvas'ref={ele => canvas = ele}></canvas>+     <divid='plane'style={{left,top,display}}><p>机柜名称:{name}</p><p>机柜温度:{temperature}°</p><p>使用情况:{count}/{capacity}</p></div></div>}
}
  • 在App.css 文件中设置面板样式
#plane{position: absolute;top: 0;left: 0;background-color: rgba(0,0,0,0.5);color: #fff;padding: 0 18px;transform: translate(12px,-100%);display: none;
}

2.根据绑定在机柜对象上的鼠标事件,设置信息面板的可见性和位置。

componentDidMount() {if (!canvas) { return }canvas.width = window.innerWidthcanvas.height = window.innerHeightroom=new MachineRoom(canvas)room.modelPath='./models/'room.loadGLTF('machineRoom.gltf')room.animate()//当鼠标划入机柜,显示信息面板
+  room.onMouseOverCabinet = () => {this.setState({planeDisplay: 'block'})}//当鼠标在机柜上移动,让信息面板随鼠标移动
+  room.onMouseMoveCabinet = (left,top) => {this.setState({planePos: {left,top}})}//当鼠标划出机柜,隐藏信息面板
+  room.onMouseOutCabinet = () => {this.setState({planeDisplay: 'none'})}
}

效果如下:
请添加图片描述

总结

通过三维机房的案例,我给大家分享一个我自己在实战中的经验:图形组件与前端页面的分离。

在这个案例里,我们可以看出,其最核心部分,就是三维机房对象MachineRoom。

MachineRoom 就是图形组件,它有两种职责:

  • 提供与图形相关的操作,比如场景搭建,模型加载,模型选择,场景渲染等。
  • 提供与前端交互的接口,比如鼠标在机柜上的划入、划出和移动事件。

图形组件尽量不要参与前端的业务逻辑,比如参与前端数据解析和存储,参与前端DOM元素的交互操作……

图形组件与前端页面的分离,会带来以下好处:

  • 可以明确WebGL工程师与react、vue等主流前端工程师的分工。

    由于图形学的水很深,当我们专心于图形学的学习时,短时间内,往往会忽略对react、vue等主流框架的研究。

    图形组件与前端页面的分离,可以让我们专精与我们擅长的领域。在项目开发的时候,我们只需要与主流前端工程师做好接口对接就好。

  • 降低图形组件与前端业务逻辑的耦合度,降低前端业务需求的频繁修改对图形组件的影响。

    有些大厂,涉及的业务逻辑复杂了,频繁改需求、改数据结构、改接口都是很正常的,若图形组件与前端业务逻辑混为一体,那WebGL工程师的工作就会很焦灼。

WebGL工程师与react、vue等主流前端工程师有了明确分工之后,便需要考虑团队协作。

现在在大厂里,一般主流的前端工程师比较好找,而有结实的图形学基础和实战经验的WebGL工程师却很难找。

所以,在一个以三维图形为主导的项目中,一个优秀的WebGL工程师会有很高的话语权。

不过,若WebGL工程师不精通项目工程化、不精通主流前端框架,建议不要主导整个项目的开发,也不要承担下自己不擅长的东西,只要一心一意的做好自己擅长的图形组件,与团队做好交流沟通即可。这样既省心,又省力。

关于图形组件与前端页面的分离,咱们就说到这。

后面,我们还可以一键显示机房中过热的机柜,这个原理和机柜的高亮是一样的,为其换一个偏红色调的贴图即可。大家可以自己在课后练习一下,我就不再赘述了,有问题可以在群里说。

当前这个案例主要是让大家对three实战项目有一个系统的认知。

根据不同的项目需求,我们可能还会对三维项目有不同的要求。

比如,若是我们用这种渲染三维机房的方式渲染室内设计,然后把效果图拿给业主看,那这单子肯定会搞砸了。

这是因为这种三维机房的渲染效果还是太假了。

因此,我们要不要让一个三维项目渲染得更加逼真,还是要看项目需求的。

比如这个三维机房,更多的是示意性的,若是将其整得太逼真,让它实时光线追踪,那交互起来就可能会卡。

若只是想渲染一张效果图给业主看,那咱们就得全心全意多花点时间,把一帧渲好了就行。

关于三维机房的实战,咱们就说到这。

接下来,我会系统讲解threejs 知识,告诉大家如何用three 应对不同的项目需求。

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

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

相关文章

【计算机毕业设计】租房网站

租房网站 在网络高速发展的时代&#xff0c;众多的软件被开发出来&#xff0c;给用户带来了很大的选择余地&#xff0c;而且人们越来越追求更个性的需求。在这种时代背景下&#xff0c;房东只能以用户为导向&#xff0c;所以开发租房网站是必须的。 系统采用了Java技术&#xf…

90分的机房长什么样?(三)

还记得之前雨笋君分享过的《90分的机房长什么样&#xff1f;&#xff08;一、二&#xff09;》系列内容吧&#xff0c;本篇继续为大家讲解另外四方面测评标准。 1、防静电 l 机房应铺设防静电地板或地面&#xff0c;并且地板进行安全接地处理。 常见的静电地板包括&#xff1…

机房建设方案设计

按企业规模大小参考《GB_50174-2017数据中心设计规范》&#xff0c;具体文件可以私信我获取

学校旧机房电脑如何改造升级成云教室客户端 旧电脑改造成x86云终端

过去十多年&#xff0c;随着国家信息化不断建设&#xff0c;学校已基本建设了多媒体教室和计算机实验室。但由于信息技术发展速度快&#xff0c;硬件软件更新换代周期变短&#xff0c;许多学校的机器已无法安装部署新的系统和软件&#xff0c;有些勉强能安装也是运行速度很慢。…

【计算机毕业设计】522租房网站

一、系统截图&#xff08;需要演示视频可以聊&#xff09; 目 录 摘 要 Abstract 第1章 前 言 2 1.1 研究背景 1.2 研究现状 1.3 系统开发目标 第2章 系统开发环境 2.1 java技术 2.2 Mysql数据库 2.3 B/S结构 2.4 springboot框架 2.5 ECLIPSE 开发…

【机房】机房配置三部曲

【前言】 终于开机房了&#xff01;OK&#xff0c;打开机房&#xff0c;这是个什么玩意儿&#xff01;看不懂&#xff0c;关上&#xff0c;再打开&#xff0c;还是看不懂&#xff0c;按照步骤就做吧&#xff01; 配置文件DSN 打开ODBC数据源&#xff0c;找到文件DSN&#xff…

【计算机毕业设计】022房屋租售网站

一、系统截图&#xff08;需要演示视频可以私聊&#xff09; 摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针…

【C语言】操作符大全(保姆级介绍)

&#x1f6a9;纸上得来终觉浅&#xff0c; 绝知此事要躬行。 &#x1f31f;主页&#xff1a;June-Frost &#x1f680;专栏&#xff1a;C语言 &#x1f525;该篇将详细介绍各种操作符的功能。 目录&#xff1a; &#x1f4d8; 前言① 算术操作符②移位操作符③位操作符④赋值操…

魔兽世界·与你同行,一起追忆魔兽年华吧

魔兽十周年纪录片《魔兽世界&#xff1a;寻求组队》 &#xff08;激动时间长达1小时&#xff0c;建议在WIFI下观看&#xff09; 2009年7月16日&#xff0c;一网友在WoW百度贴吧发表了一个名为“贾君鹏你妈妈喊你回家吃饭”的帖子&#xff0c;随后短短五六个小时内被390617名网友…

深度学习在自然语言处理中的十大应用领域

文章目录 1. 机器翻译2. 文本分类3. 命名实体识别4. 问答系统5. 文本生成6. 情感分析7. 语言生成与处理8. 信息检索与摘要9. 文本纠错与修复10. 智能对话系统总结 &#x1f389;欢迎来到AIGC人工智能专栏~深度学习在自然语言处理中的十大应用领域 ☆* o(≧▽≦)o *☆嗨~我是IT陈…

【前端】使用wow.js这个插件(实现页面动画效果),提高前端开发效率。

1.简介 有的页面在向下滚动的时候&#xff0c;有些元素会产生细小的动画效果。比如需要做到滚动条滑到某个位置时&#xff0c;才能显示动画。 wow.js 依赖 animate.css&#xff0c;所以它支持 animate.css 多达 60 多种的动画效果&#xff0c;能满足您的各种需求。 2.兼容性 …

vue中引入及使用wow.js

1. 安装 npm install wowjs --save (animate.css会被自动安装&#xff0c;但是这里有坑) 2. 在main.js中引入animate.css 引入时需要注意看是引入的哪个animate.css文件&#xff0c;在后面有详细讲解。 3. 引入wow.js并初始化 这里方法有二 方法1 在main.js中添加 impor…

http://bbs.duowan.com/forum.php,多玩论坛宣告关闭 国内游戏社区又少一个

2019年的今天还有人上论坛吗&#xff1f;有是肯定有的&#xff0c;但是论坛在国内的发展情况是注定的了&#xff0c;最近几年来大大小小的网站都关闭过旗下的论坛。今天没注意&#xff0c;很有名的多玩也宣布关闭论坛&#xff0c;今年12月31日后停止服务。 多玩之前、现在都是国…

Lottery抽奖项目第二章第二节:搭建DDD四层结构

搭建DDD四层结构 DDD&#xff1a;Domain Driven Design 描述&#xff1a;基于DDD架构构建&#xff0c;初始化搭建工程结构 本节是陆续搭建系统和编码的开始&#xff0c;我们会优先完成一个基础工程的创建。一般在互联网企业这部分工作可能不需要反复处理&#xff0c;只需要在…

企业如何通过CRM来提高销售业绩?

在当今市场环境中&#xff0c;客户的需求更偏向于个性化&#xff0c;企业面对的竞争更加激烈。如何有效地获取和维护客户&#xff0c;提高收入成为了企业的核心问题。作为一种强大的销售管理工具&#xff0c;CRM如何提高销售业绩&#xff1f; 提高客户转化率&#xff1a; 企业…

LSA

Type-7 LSA : NSSA External LSA NSSA&#xff08;非完全末梢区域Not-So-Stubby Area)我们可以理解为从Stub Area衍生而来&#xff0c;StubArea是不允许外部路由进入的&#xff0c;而NSSA可以。当NSSA的ASBR向该区域注入外部路由时&#xff0c;这些外部路由将使用Type-7 LSA来描…

SD 总线引脚介绍

参考 https://www.cnblogs.com/justin-y-lin/p/12259851.html SD卡与TF卡的引脚定义 - 360文档中心

PostgreSQL分区表

什么是分区表 数据库分区表将表数据分成更小的物理分片&#xff0c;以此提高性能、可用性、易管理性。分区表是关系型数据库中比较常见的对大表的优化方式&#xff0c;数据库管理系统一般都提供了分区管理&#xff0c;而业务可以直接访问分区表而不需要调整业务架构&#xff0c…

Qt5.15.2+VLC3.0.14学习笔记(六)Qt Player测试(官方示例 vlc-qt+mingw_64版)

前记&#xff1a; Qt Player是官方源码中的一个示例&#xff0c;好像已经很久没有更新了&#xff0c;今天运行测试了下&#xff0c;需要修改部分内容才能运行 vlc-3.0.14源码下载地址&#xff1a;https://code.videolan.org/videolan/vlc 已下载源码包 win下已编译的VLC下载…

UE4/5在蓝图细节面板中添加函数按钮(蓝图与c++的方法)

目录 在细节面板中添加按钮使用函数 蓝图的方法 事件 函数 效果 uec的方法 效果 在细节面板中添加按钮使用函数 很多时候&#xff0c;我们可以看到一些插件的actor类中&#xff0c;点击一下之后就可以实现如矩阵一样的效果。 实际上是因为其使用了函数来修改了蓝图中的数…