react + dataV + three.js + blender 实现3D可视化大屏效果
·
效果图
3D 可视化展示是一个很不错的方向,但是纯three.js 开发效率低,简单展示类的可视化,可以考虑使用blender 等3D 绘制工具,快速,高效。
文章目录
前言
关于如何使用react 等内容这里不再介绍了,我们使用的工具库:DataV 大屏,three.js 3D 可视化,简单的应用之前的博客也有介绍,blender 是制作3D的工具,自行学习,这里介绍下思路和实现。
一、DataV 大屏布局
网站:http://datav-react.jiaminghi.com/guide/
采用如此的布局:
实现:
<div id="data-view">
{/* <FullScreenContainer> */}
<TopHeader></TopHeader>
<div className="main-content">
<div className="left-content">
<LeftContent></LeftContent>
</div>
<div className="center-content">
<CenterContent></CenterContent>
</div>
<div className="right-content">
<RightContent></RightContent>
</div>
</div>
{/* </FullScreenContainer> */}
</div>
头部:
<div id="top-header">
<Decoration8 className="header-left-decoration"></Decoration8>
<Decoration5 className="header-center-decoration" />
<Decoration8 className="header-right-decoration" reverse={true} ></Decoration8>
<div className="center-title">3D 机床可视化大数据中心</div>
</div>
右侧其他的内容类似,也就是采用echarts 图形
二、blender 绘图
1.效果
使用工具生成一个fbx 格式的文件,后面three.js loader 加载使用,图形绘制的是3D齿轮效果,工具绘制还是要比代码实现的快很多。这里有动画的,本人比较懒,没有弄成动图
二、three.js 加载实现
1.初始化
我们需要用到 three(主要库) OrbitControls(轨道控制器OrbitControls.js,用它可以实现场景用鼠标交互,让场景动起来,控制场景的旋转、平移,缩放) FBXLoader(fbx 格式加载器)
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js';
import './threeDemo.less'
let rayCaster = new THREE.Raycaster();
let mouse = new THREE.Vector2();
const ThreeBSP = require('jthreebsp')(THREE)
var camera, scene, renderer, stats,docDom,width,height,marginLeft,marginTop;
export default function ThreeDemo() {
const [tagTop, setTagTop] = useState(0);
const [tagLeft, setTagLeft] = useState(0)
const [isShowTag, setIsShowTag] = useState(false);
useEffect(() => {
docDom = document.getElementById('container');
width = docDom.clientWidth;
height = docDom.clientHeight;
let rect = docDom.getBoundingClientRect();
marginTop = rect.top - docDom.clientTop;
marginLeft = rect.left - docDom.clientLeft;
fnFbxLoading();
// fnGear();
}, []);
2.加载相机,场景和灯光
let fnFbxLoading = ()=>{
const clock = new THREE.Clock();
let mixer;
fnInitCamera();
fnInitScene();
fnInitLight();
let fnInitCamera = ()=>{
camera = new THREE.PerspectiveCamera( 45, width / height, 1, 2000 );
camera.position.set( 0, 1200, 0 );
}
let fnInitScene = ()=>{
scene = new THREE.Scene();
}
let fnInitLight = ()=>{
// scene.background = new THREE.Color( 0xa0a0a0 );
// scene.fog = new THREE.Fog( 0xa0a0a0, 200, 1000 );
const hemiLight = new THREE.HemisphereLight( 0xffffff, 0x444444 );
hemiLight.position.set( 0, 200, 0 );
scene.add( hemiLight );
}
3、加载fbx
const loader = new FBXLoader();
loader.load( './models/gear.fbx', function ( object ) {
mixer = new THREE.AnimationMixer( object );
let animations = object.animations;
animations.map((animation)=>{
const action = mixer.clipAction( animation );
action.play();
});
object.traverse( function ( child ) {
if ( child.isMesh ) {
child.castShadow = true;
child.receiveShadow = true;
}
} );
scene.add( object );
let children = scene.children[1];
children.children.forEach(function(mesh){
if(mesh.type == "Mesh"){
let color = mesh.material.color;
let userData = mesh.userData;
if(!mesh.userData.color){
userData.color = {r:color.r,g:color.g,b:color.b};
}
}
}
);
} );
4、渲染,控制和动画
fnRenderer();
fnControls();
animate();
docDom.addEventListener( 'click', fnClickRayCaster, false );
function animate() {
requestAnimationFrame( animate );
const delta = clock.getDelta();
if ( mixer ) mixer.update( delta );
renderer.render( scene, camera );
// stats.update();
}
let fnRenderer = ()=>{
renderer = new THREE.WebGLRenderer( { antialias: true } );
// renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( width, height );
renderer.shadowMap.enabled = true;
docDom.appendChild( renderer.domElement );
}
let fnControls = ()=>{
const controls = new OrbitControls( camera, renderer.domElement );
controls.target.set( 0, 100, 0 );
controls.update();
window.addEventListener( 'resize', onWindowResize );
// stats
// stats = new Stats();
// docDom.appendChild( stats.dom );
}
5、加入点击事件(需要使用Raycaster 光线投影,对象坐标拾取)
docDom.addEventListener( 'click', fnClickRayCaster, false );
let fnClickRayCaster = (event) => {
console.log(marginTop,marginLeft);
console.log(event.clientY,event.clientX);
console.log(event.clientY - marginTop,event.clientX - marginLeft);
// mouse.x = ((event.clientX - marginLeft) / width) * 2 - 1;
// mouse.y = -((event.clientY - marginTop) / height) * 2 + 1;
mouse.x = ((event.clientX - marginLeft) / width) * 2 - 1;
mouse.y = -((event.clientY - marginTop) / height) * 2 + 1;
rayCaster.setFromCamera(mouse, camera);
let child = [];
let children = scene.children[1];
children.children.forEach(function(mesh){
if(mesh.type == "Mesh"){
let userData = mesh.userData;
let userColor = userData.color;
if(userColor){
mesh.material.color.setRGB(userColor.r,userColor.g,userColor.b);
}
child.push(mesh);
}
}
);
let intersects=rayCaster.intersectObjects(child);
if(!intersects.length){
setIsShowTag(false);
}else{
setTagLeft(event.clientX-marginLeft);
setTagTop(event.clientY-marginTop-145);
setIsShowTag(true);
}
for (var i = 0; i < intersects.length; i++) {
let intersect = intersects[i].object;
intersect.material.color.set(0x07a9e6);
}
}
let fnSetCamera = (face)=>{
let length = camera.position.length();
if(face == "up"){
var vec3 = new THREE.Vector3(1, 1, 1).normalize();
camera.position.set(vec3.x * length, vec3.y * length, vec3.z * length);
}else if(face == "left"){
camera.position.set(0, 0, length);
}else if(face == "right"){
camera.position.set(0, length, 0);
}else if(face == "front"){
camera.position.set(-length, 0, 0);
}
camera.lookAt(scene.position);
renderer.render( scene, camera );
}
<div className="change-camera">
<div onClick={()=>fnSetCamera("up")}>
<img src="./imgs/up.png"></img>
</div>
<div onClick={()=>fnSetCamera("left")}> <img src="./imgs/left.png"></img></div>
<div onClick={()=>fnSetCamera("right")}> <img src="./imgs/right.png"></img></div>
<div onClick={()=>fnSetCamera("front")}> <img src="./imgs/front.png"></img></div>
</div>
<div className="change-dev-left">
<div onClick={()=>fnSetCamera("up")}>
{/* <img src="./imgs/up.png"></img> */}
<LeftOutlined style={{fontSize:25}} />
</div>
</div>
<div className="change-dev-right">
<div onClick={()=>fnSetCamera("up")}>
{/* <img src="./imgs/up.png"></img> */}
<RightOutlined style={{fontSize:25}}/>
</div>
</div>
总结
时刻 保持一颗学习进步的心态,新技术,会越来越有意思
更多推荐
已为社区贡献1条内容
所有评论(0)