threeJS 实现加载模型 + 页面按钮交互 + 显示css2Renderer标注

这篇具有很好参考价值的文章主要介绍了threeJS 实现加载模型 + 页面按钮交互 + 显示css2Renderer标注。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

  1. 创建好HTML标注的提示框
  2. 创建three的场景等系列
  3. three加载GLTF模型
  4. 点击按钮选中模型 + 交互
  5. 示例(大概意思就是我点击那个按钮显示那个提示框,模型变材质 + 线)css2drenderer,vue.js,经验分享
  6. 直接代码
<panel-group @handleSetLineChartData="handleSetLineChartData" />
        <div v-if="tagFlage == 'newVisitis'">
            <el-tag v-for="(item, index) of tagData" :key="index" :type="item.flag ? 'danger' : ''"
                @click="tagClick(item, index)">{{ item.name }}</el-tag>
        </div>

        <el-row style="background:#fff;padding:16px 16px 0; margin-bottom:32px;height: calc(100vh - 340px);">
            <canvas id="three"></canvas>
        </el-row>
        <div id="tag" style="display: none;"></div>
        <div :id="item.message" v-for="(item, index) of tagData" :key="index" style="
                visibility:hidden;
                width:230px;
                height:180px;
                position: absolute;
                color: #fff;
                z-index: 2;
                font-size: 16px;
                background: rgba(86, 183, 195, 0.47);
                box-shadow: rgba(86, 183, 195, 0.47) 0px 0px 15px 3px;"
            :style="{ left: item.x1 + 'px', top: item.y1 + 'px' }">
            <div style="position:relative;padding: 10px;">
                <div style="display: flex;">
                    <span>标识:</span>
                    <div id="granaryName" style="font-size:16px">平房仓 P_01</div>
                </div>

                <div style="display: flex;align-items: flex-end;margin-top: 5px;">
                    <span>温度:</span>
                    <span id="temperature">19</span></div>
                <div style="display: flex;align-items: flex-end;margin-top: 5px;">
                    <span>名字:</span>
                    <span id="weight">3600</span>
                </div>
                <div style="display: flex;align-items: flex-end;margin-top: 5px;">
                    <span>高度:</span>
                    <span id="granaryHeight">12</span>m
                </div>
                <div style="display: flex;align-items: flex-end;margin-top: 5px;">
                    <span>宽度:</span>
                    <span id="grainHeight">5</span> m
                </div>
            </div>
        </div>
<style lang="scss" scoped>
.dashboard-editor-container {
    padding: 32px;
    background-color: rgb(240, 242, 245);
    position: relative;

    .chart-wrapper {
        background: #fff;
        padding: 16px 16px 0;
        margin-bottom: 32px;
    }
}

@media (max-width:1024px) {
    .chart-wrapper {
        padding: 8px;
    }
}

#import-template {
    width: 100%;
    height: 100%;
}

#three {
    width: 100%;
    height: calc(591px - 32px);
}

.el-tag--medium {
    margin-right: 10px;
}
</style>
import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import {
    CSS2DObject,
    CSS2DRenderer
} from 'three/examples/jsm/renderers/CSS2DRenderer.js';
 data() {
        return {
            tagFlage: null,
            scene: null, // 场景
            camera: null, // 照相机
            renderer: null, // 渲染器
            // mesh: null, // 网格
            // textureLoader: null, // 纹理加载器
            // mixer: null,
            // groupBox: null,
            // stats: null, // 性能监测
            // control: null, // 相机控件
            // scene2: null,
            // render3D: null,
            publicPath: process.env.BASE_URL,
            // clearAnim: null,
            // clock: null,
            publicPath: process.env.BASE_URL,
            granaryArr: [],
            chooseMesh: null,
            messageTags: [],
            tagData: [
                {
                    name: '场地',
                    message: 'HuaZhuang',
                    x: 10,
                    y: 10,
                    z: 15,
                    x1: -90,
                    y1: -60,
                    flag: false,
                },
                {
                    name: '建筑墙面',
                    message: 'JianZhu_ZhuTi',
                    x: 15,
                    y: 5,
                    z: 1,
                    x1: -50,
                    y1: 50,
                    flag: false,
                },
                {
                    name: '条幅',
                    message: 'TeXiao_010101',
                    x: 15,
                    y: 15,
                    z: -10,
                    x1: 80,
                    y1: -60,
                    flag: false,
                },
            ],
            idArr: ["granaryName", "temperature",
                "weight", "granaryHeight", "grainHeight"
            ],
            messageData: {
                HuaZhuang: {
                    granaryName: "数据一(HuaZhu)",
                    temperature: 30,
                    weight: 'RC100',
                    granaryHeight: 6,
                    grainHeight: 25.1
                },
                JianZhu_ZhuTi: {
                    granaryName: "数据二(JianZhu)",
                    temperature: 30,
                    weight: 'SP500',
                    granaryHeight: 66,
                    grainHeight: 23.8
                },
                TeXiao_010101: {
                    granaryName: "数据三(TeXiao)",
                    temperature: 30,
                    weight: 'DataAPI',
                    granaryHeight: 666,
                    grainHeight: 9.2
                }
            },
        }
    },

mounted() {
        this.initThree();
},
methods: {
	initThree() {
            let this_ = this
            this_.scene = new THREE.Scene();
            this_.scene.background = new THREE.Color(0xeeeeee);
            this_.scene.fog = new THREE.Fog(0xa0a0a0, 200, 1000);

            const canvas = document.querySelector("#three");
            this_.renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true });
            this_.renderer.shadowMap.enabled = true;

            this.camera = new THREE.PerspectiveCamera(
                40,
                window.innerWidth / window.innerHeight,
                1,
                1000
            );
            const controls = new OrbitControls(this.camera, this.renderer.domElement);

            const gltfLoader = new GLTFLoader();
            gltfLoader.load(`${this.publicPath}factory/XXX.gltf`, (gltf) => {
                let model = gltf.scene;
                model.scale.set(1, 1, 1);
                controlsBox(model);
                gltf.scene.traverse(function (object) {
                    if (object.type === 'Mesh') {
                        // 批量更改所有Mesh的材质
                        object.material = new THREE.MeshLambertMaterial({
                            map: object.material.map, //获取原来材质的颜色贴图属性值
                            color: object.material.color, //读取原来材质的颜色
                        })
                    }
                })
                gltf.scene.children.forEach((obj, index) => {
                    if (obj.type === 'Mesh') {
                        this_.granaryArr.push(obj);
                    }
                })

                this_.scene.add(model);
            }, undefined, (error) => console.error(error));
            
            // 将模型的中心点设置到canvas坐标系的中心点,保证模型显示是居中的
            function controlsBox(model) {
                let box = new THREE.Box3().setFromObject(model); // 获取模型的包围盒
                let mdlen = box.max.x - box.min.x; // 模型长度
                let mdwid = box.max.z - box.min.z; // 模型宽度
                let mdhei = box.max.y - box.min.y; // 模型高度
                let xPoiition = box.min.x + mdlen / 2; // 模型中心点坐标X
                let yPoiition = box.min.y + mdhei / 2; // 模型中心点坐标Y
                let zPoiition = box.min.z + mdwid / 2; // 模型中心点坐标Z
                // 获取模型整体对角线长度,这里获取模型模型对角线的目的是为了保证模型可以完全的展示在视线范围内
                let diagonal = Math.sqrt(Math.pow(Math.sqrt(Math.pow(mdlen, 2) + Math.pow(mdwid, 2)), 2) + Math.pow(mdhei, 2));
                // 假设我们需要的进入视角为45度
                // 设置相机位置,向上偏移,确定可以包裹整个模型
                controls.object.position.set(box.max.x + diagonal / 2, (diagonal * 2) / Math.tan(Math.PI / 180 * 45) + Math.abs(box.max.y), box.max.z + diagonal / 2);
                // 设置相机的视角方向,看向模型的中心点
                controls.target.set(xPoiition, yPoiition, zPoiition);
                controls.update(); // 更新相机
            }

            const hemLight = new THREE.HemisphereLight(0xffffff, 0xffffff);
            hemLight.position.set(0, 20, 0);
            this_.scene.add(hemLight);
            const dirLight = new THREE.DirectionalLight(0xffffff, 0.6);
            //光源等位置
            dirLight.position.set(0, 20, 10);
            //可以产生阴影
            dirLight.castShadow = true;
            dirLight.shadow.mapSize = new THREE.Vector2(1024, 1024);
            this_.scene.add(dirLight);
            // 添加地板
            let floorGeometry = new THREE.PlaneGeometry(8000, 3000);
            let floorMaterial = new THREE.MeshPhongMaterial({
              color: 0x5cb3cc,
              shininess: 0,
            });
            let floor = new THREE.Mesh(floorGeometry, floorMaterial);
            floor.rotation.x = -0.5 * Math.PI;
            floor.receiveShadow = true;
            floor.position.y = -0.001;
            // this_.scene.add(floor);

            controls.enableDamping = true;
            this.tagData.forEach((item) => {
                this.labelRenderer = new CSS2DRenderer();
                let three = document.querySelector('#three')
                this.labelRenderer.setSize(three.getBoundingClientRect().width,three.getBoundingClientRect().height);
                this.labelRenderer.domElement.style.position = 'absolute';
                this.labelRenderer.domElement.style.top = '320px';
                this.labelRenderer.domElement.style.left = '250px';
                this.labelRenderer.domElement.style.pointerEvents = 'none';
                document.body.appendChild(this.labelRenderer.domElement);
                var messageTag = this.tag(item.message); //创建粮仓标注的标签
                this.scene.add(messageTag);
                this.messageTags.push(messageTag)
            })
            function animate() {
                controls.update();
                this_.renderer.render(this_.scene, this_.camera);
                this_.labelRenderer.render(this_.scene, this_.camera); //渲染HTML标签对象
                requestAnimationFrame(animate);

                if (resizeRendererToDisplaySize(this_.renderer)) {
                    const canvas = this_.renderer.domElement;
                    this_.camera.aspect = canvas.clientWidth / canvas.clientHeight;
                    this_.camera.updateProjectionMatrix();
                }
            }
            animate();
            function resizeRendererToDisplaySize(renderer) {
                const canvas = renderer.domElement;
                var width = window.innerWidth;
                var height = window.innerHeight;
                var canvasPixelWidth = canvas.width / window.devicePixelRatio;
                var canvasPixelHeight = canvas.height / window.devicePixelRatio;
                const needResize = canvasPixelWidth !== width || canvasPixelHeight !== height;
                if (needResize) {
                    renderer.setSize(width, height, false);
                }
                return needResize;
            }
        },

}
tagClick(item, index) {
            let this_ = this
            var messageTag = this.tag(item.message);
            let visibilitys = messageTag.element.style.visibility
            let chooseMeshData = null
            chooseMeshData = this_.choose(event, messageTag, 2, item.message); //执行射线拾取的代码
            // 选中不同粮仓,HTML标签信息跟着改变
            if (chooseMeshData) {
                //批量更新粮仓chooseMesh的标签信息
                this_.idArr.forEach(function (id) {
                    var dom = document.querySelector(`#${item.message} #${id}`)
                    dom.innerHTML = this_.messageData[chooseMeshData.name][id];
                })
                this.messageTags.forEach(function (messageTag) {
                    // 判断是否为当前需要更新的弹窗对象
                    if (document.querySelector(`#${item.message}`).id == messageTag.element.id) { // 替判断当前弹窗对象的条件
                        if (visibilitys == 'visible') {
                            messageTag.element.style.visibility = 'hidden';
                            this_.tagData[index].flag = false;
                        } else {
                            messageTag.element.style.visibility = 'visible';
                            this_.tagData[index].flag = true;
                        }
                        

                        // 创建线条材质
                        var lineMaterial = new THREE.LineBasicMaterial({ color: 0xff0000, linewidth: 100 });
                        // 获取点击模型和弹窗的位置坐标
                        var modelPosition = chooseMeshData.position.clone();
                        var newPositions = new THREE.Vector3().copy(messageTag.position); // 创建新的Vector3对象并复制点击对象的坐标
                        messageTag.position.copy(newPositions);
                        // var popupPosition = messageTag.position.clone();
                        var popupPosition = messageTag.position.copy({
                            x: item.x,
                            y: item.y,
                            z: item.z
                        });
                        // 创建线条的顶点数组
                        var positions = [];
                        positions.push(modelPosition.x, modelPosition.y, modelPosition.z);
                        positions.push(popupPosition.x, popupPosition.y, popupPosition.z);
                        // 创建线条的属性数组
                        var geometry = new THREE.BufferGeometry();
                        geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
                        // 创建线条对象
                        var line = new THREE.LineSegments(geometry, lineMaterial);
                        // 添加线条到场景中
                        line.name = item.message
                        this_.scene.add(line);
                        // 获取场景中的所有线段  
                        var allLineSegments = [];
                        this_.scene.traverse(function (child) {
                            if (child instanceof THREE.LineSegments) {
                                allLineSegments.push(child);
                            }
                        });
                        // 打印所有线段  
                        if (visibilitys == 'visible') {
                            allLineSegments.forEach((node) => {
                                if (node.name == item.message) {
                                    this_.scene.remove(node);
                                }
                            })
                        }
                    }
                });

            }
        },
        tag(domID) {
            var dom = document.getElementById(domID);
            var label = new CSS2DObject(dom);
            label.name = domID
            dom.style.pointerEvents = 'none';
            return label;
        },
        choose(event, messageTag, type, name) {
            let chooseMesh = null
            if (type == 1) {
            //这个逻辑里用不上,正常是点击模型里的 后来改成点击按钮了
                if (this.chooseMesh) {
                    this.chooseMesh.material.color.set(0xffffff); // 把上次选中的mesh设置为原来的颜色
                }
                var Sx = event.clientX;
                var Sy = event.clientY;
                let mainCanvas = document.querySelector('canvas')
                var x = ((event.clientX - mainCanvas.getBoundingClientRect().left) / mainCanvas.offsetWidth) * 2 - 1;
                var y = -((event.clientY - mainCanvas.getBoundingClientRect().top) / mainCanvas.offsetHeight) * 2 + 1;

                var raycaster = new THREE.Raycaster();
                raycaster.setFromCamera(new THREE.Vector2(x, y), this.camera);

                var intersects = raycaster.intersectObjects(this.granaryArr);
                console.log("射线器返回的对象", intersects);
                if (intersects.length > 0) {
                    this.chooseMesh = intersects[0].object;
                    this.chooseMesh.material.color.set(0x00ffff); //选中改变颜色,这样材质颜色贴图.map和color颜色会相乘
                    this.chooseMesh.point = intersects[0].point;
                } else {
                    this.chooseMesh = null;
                }
            } else {
                this.granaryArr.forEach((item) => {
                    if (item.name == name) {
                        console.log(item, 'd点击后的item', item.material.color.getHex())
                        chooseMesh = item;
                        if (item.material.color.getHex() == 16777215) {
                            chooseMesh.material.color.set(0xff0000); //选中改变颜色,这样材质颜色贴图.map和color颜色会相乘
                        } else {
                            chooseMesh.material.color.set(0xffffff); // 把上次选中的mesh设置为原来的颜色
                        }
                        chooseMesh.point = item.position;
                        // chooseMesh.point = item.point;
                        // this.chooseMesh.point = {
                        //     x: item.x,
                        //     y: item.y,
                        //     z: item.z
                        // }
                    }
                })
            }
            return chooseMesh

        },
        handleSetLineChartData(type) {
            console.log(type)
            this.tagFlage = type
        },


以上就是完整的,
弹窗的位置,和线的结束点是我写死的,因为我不知道创建的弹窗的位置该怎么设置
新手three 有大佬的话 可以给些建议

后续补充了一点新的 弹窗位置和线条的起始结束点
给我的当前弹窗点击拿到后设置
var popupPosition = messageTag.position.copy({
x: item.x,
y: item.y,
z: item.z
});
这组就是线条的结束点 push到一个存起点和结束点的数组里然后放到THREE.Float32BufferAttribute

数据的坐标点基本都这样的
{
name: ‘XXX’,
message: ‘XXX’,
x: -2,
y: 0.8,
z: 1,
x1: -43,
y1: -45,
flag: false,
},文章来源地址https://www.toymoban.com/news/detail-762045.html

到了这里,关于threeJS 实现加载模型 + 页面按钮交互 + 显示css2Renderer标注的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • threejs加载.Fbx .OBJ 3D模型文件

    在threejs官网下载threejs的文件,可以选择直接下载,也可以跳转到GitHub拉取 拉取下来的完整文件就是这个样子 拉取成功后我们在本地安装启动服务,这样就能很快速的查看threejs的各种例子了 可以先看看官网里的例子,你想要的东西官方里面都有 后期在开发的时候需要用到b

    2023年04月08日
    浏览(43)
  • 基于threejs加载大型BIM模型的优化尝试

    轻量化引擎,该合并的合并,该共享的共享,材质光影等等效果都很难再提升的时候,我们总不能转到隔壁的去渲染技术栈去吧? 最近几个月,陆陆续续做了很多的尝试,先把这些方案的思路记录下来,欢迎大佬给予点评,如果这里有坑,请偷偷告知我一声,避免踩雷,就当

    2024年01月21日
    浏览(67)
  • uniapp使用threejs-miniprogram在微信小程序加载模型

    1.通过 npm 安装 2.导入小程序版本的 Three.js并创建一个与 canvas 绑定的 three.js  3.创建渲染器 4.创建场景,创建相机,渲染 5.注册GLTF加载器,加载模型添加到场景 threejs-miniprogram/example/loaders/gltf-loader.js at master · wechat-miniprogram/threejs-miniprogram · GitHub 下载 gltf-loader.js 注册gltf-loader 加

    2024年02月08日
    浏览(49)
  • uniapp中页面滚动锚点位置及滚动到对应高度显示对应按钮

    可以把页面代码和组件代码放自己项目里跑一下 页面代码 吸顶按钮组件代码

    2024年04月11日
    浏览(35)
  • 基于QT使用OpenGL,加载obj模型,进行鼠标交互

    基于QT平台,使用OpenGL进行obj文件加载显示; 使用鼠标对场景进行缩放、移动、旋转交互;   OpenGL是基于C的,学习曲线比较抖,但是总的来说就是下面一幅图,   用语言简单的描述(个人理解,可能不太准确)是把 cpu里内存里的3D数据,传输到显卡的内存里,以及如何

    2024年02月04日
    浏览(50)
  • 网络正常,显示无Internet,Microsoft Store需要联网、无法加载页面

    完美解决无Internet但能正常上网的问题 - 哔哩哔哩 (转载)最近也遇到同样的问题,试过网上的几乎所有方法,例如禁用复用网卡、网络重置、禁用复用服务,也用了用修改注册表下HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesNlaSvcParametersInternet下的EnableActiveProbing项的办法,均

    2024年02月06日
    浏览(76)
  • uniapp 中 的progress加载进度条 的使用,在 页面显示数据加载的进度条,使用户的使用体验效果更好

    学习目标如下: 例如: uniapp 中 的progress加载进度条 的使用,在 页面显示数据加载的进度条,使用户的使用体验效果更好 学习内容如下所示: 相关属性的说明 进度条的显示 是否显示属性的控制 显示进度条 进度条的样式设置 提示:这里统计学习计划的总量 1、进度条的显

    2024年02月15日
    浏览(61)
  • OSG交互:选中场景模型并高亮显示

      可以在osg视图中选中指定模型实体,并高亮显示。共分为两种,一种鼠标点选,一种框选。 2.1 功能说明 生成两组对象,一组cow对象可以被选中,另一组robot不能被选中; 点击cow对象被选中高亮,点击robot被选中不高亮; 点击空白处,弹出“select nothing!”提示未选择任何

    2024年02月05日
    浏览(135)
  • android实现点击按钮切换页面

    一、实现的功能 点击页面按钮,切换到下一个页面。 二、主要代码 1)第一个页面 我们需要实现点击登录按钮进行页面切换 layout中设置一个Button,仅展示按钮部分代码  登录页面LoginActivity代码, 三、启动页面 启动页面要设置为第一个页面,在AndroidMainfest.xml

    2024年02月08日
    浏览(60)
  • 微信小程序:点击按钮实现数据加载(带模糊查询)

    wxml: 增加按钮 button class=\\\"last\\\" bindtap=\\\"bindMore\\\" wx:if=\\\"{{!allDataLoaded}}\\\"点击获取更多/button js: wxss: 后端thinkphp:

    2024年02月14日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包