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>

总结

时刻 保持一颗学习进步的心态,新技术,会越来越有意思

  • 2
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以使用ReactDataV来构建可视化大屏编辑器。React是一个用于构建用户界面的JavaScript库,而DataV是一个由阿里巴巴开发的数据可视化工具,可以帮助你快速创建交互式的大屏展示。 下面是一些步骤,可以帮助你开始构建这样的编辑器: 1. 安装React:使用npm或yarn安装React库。可以通过以下命令进行安装: ``` npm install react ``` 2. 创建React应用:使用create-react-app等工具创建一个新的React应用。运行以下命令: ``` npx create-react-app datav-editor ``` 3. 安装DataV:使用npm或yarn安装DataV库。可以通过以下命令进行安装: ``` npm install @antv/data-set npm install @antv/g2 ``` 4. 创建可视化大屏编辑器组件:在React应用中创建一个新的组件,用于展示和编辑可视化大屏。你可以使用DataV提供的图表组件和数据集处理功能来实现可视化效果。 5. 编辑器功能实现:为编辑器组件添加一些功能,比如增加、删除、移动和调整可视化组件的位置和大小等。你可以使用React的状态管理来保存和更新编辑器状态。 6. 保存和导出:实现保存和导出编辑器中的可视化大屏配置。你可以使用浏览器的本地存储或将配置保存到后端服务器。 7. 预览和发布:添加预览功能,让用户可以在编辑器中实时查看可视化大屏效果。并实现发布功能,将编辑好的可视化大屏展示给其他用户。 以上是一个简单的步骤,可以帮助你开始构建ReactDataV的可视化大屏编辑器。当然,具体的实现还需要根据你的需求进行调整和扩展。希望对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值