[Three.js] Integrate IFC.js into the vue project - example

Code example

估计之前的文章,写得比较抽象?所以还是有朋友看不懂,那就放上我的demo,边看边印证。

Project home directory

I changed the rollup.config.jsand into suffixes here . ——For those who follow the steps, you can not change the suffix first, and then change it if an error is reported later.vue.config,js.mjs

Project home directory

package.json

{
    
    
  "name": "three-ifc",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    
    
    "start": "concurrently \"rollup -c ./rollup.config.mjs -w -m inline\"  \"vue-cli-service serve\" ",
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "ifcServe": "http-server . -p 8000",
    "ifcWatch": "rollup -c ./rollup.config.mjs -w -m inline",
    "ifcBuild": "rollup -c ./rollup.config.mjs"
  },
  "dependencies": {
    
    
    "and": "^0.0.3",
    "three": "^0.150.1",
    "three-mesh-bvh": "^0.5.23",
    "vue": "^2.6.10",
    "web-ifc": "^0.0.39",
    "web-ifc-three": "^0.0.122"
  },
  "devDependencies": {
    
    
    "@rollup/plugin-node-resolve": "^15.0.1",
    "@vue/cli-plugin-babel": "^3.11.0",
    "@vue/cli-plugin-eslint": "^3.11.0",
    "@vue/cli-service": "^3.11.0",
    "babel-eslint": "^10.0.1",
    "browserslist": "^4.21.4",
    "caniuse-lite": "^1.0.30001418",
    "copy-webpack-plugin": "^5.1.2",
    "core-js": "^2.6.5",
    "css-loader": "^2.1.1",
    "eslint": "^5.16.0",
    "eslint-plugin-vue": "^5.0.0",
    "exports-loader": "^4.0.0",
    "imports-loader": "^4.0.1",
    "rollup": "^3.18.0",
    "rollup-plugin-babel": "^4.4.0",
    "rollup-plugin-commonjs": "^10.1.0",
    "rollup-plugin-node-resolve": "^5.2.0",
    "rollup-plugin-vue": "^6.0.0",
    "vue-template-compiler": "^2.6.10"
  },
  "eslintConfig": {
    
    
    "root": true,
    "env": {
    
    
      "node": true
    },
    "extends": [
      "plugin:vue/essential",
      "eslint:recommended"
    ],
    "rules": {
    
    },
    "parserOptions": {
    
    
      "parser": "babel-eslint"
    }
  },
  "postcss": {
    
    
    "plugins": {
    
    
      "autoprefixer": {
    
    }
    }
  },
  "browserslist": [
    "> 1%",
    "last 2 versions"
  ]
}

rollup.config.mjs

// import vue from 'rollup-plugin-vue';
import babel from 'rollup-plugin-babel';
import commonjs from 'rollup-plugin-commonjs';

import resolve from 'rollup-plugin-node-resolve';
// import resolve from '@rollup/plugin-node-resolve';

export default {
    
    
    input: "./public/js/ifc/threeIfcScene.js", // 入口文件路径
    output: [
        {
    
    
            format: "umd", // 输出格式
            file: "./public/js/ifc/bundle.js", // 输出文件路径
            name: "threeIfcScene"
            //当入口文件有export时,'umd'格式必须指定 name
            //这样,在通过<script>标签引入时,才能通过 name 访问到 export 的内容。
        },
    ],
    plugins: [
        // vue(), // 处理vue组件
        babel({
    
    
            exclude: 'node_modules/**', // 不编译node_modules目录下的代码
            runtimeHelpers: true, // 配置runtime,不设置会报错
        }),
        commonjs(), // 将CommonJS模块转换为ES6模块
        resolve() // 解析模块路径
    ]
}

Disclaimer : vue.config.mjs, I just changed the suffix. There is no configuration related to ifc.js, so there is no need to ask what the code is.

public folder

As shown in the figure below, it is the content of the public folder:

  • Blue : is .ifcwhere the model data is stored. This place is where the demo is temporarily stored. If it is later deployed to production, it is recommended that the model be placed directly on the server and reference the server address.
  • Green : This is a worker thread attempt based on the official IFC.js document . If it is not needed, you can find the corresponding code and delete it.
  • Yellow : It is a collection of public js that calls IFC.js, renders models, model interactions, etc. Very important.
  • Red : According to the recommendation in the IFC.js official document, wasm is extracted and stored separately from node_modules .

public folder contents

index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title>three-ifc</title>
    <style>
      html, body {
    
    
          width: 100%;
          height: 100%;
          margin: 0px;
          padding: 0px;
      }
  </style>    
  </head>
  <body>
    <noscript>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
    <script src="./js/ifc/bundle.js"></script>
  </body>
</html>

Note: Remember to quote the rollup.config.jsconvertedbundle.js

threeIfcScene.js

Most importantly, don’t blink! ! !

import * as THREE from 'three';
import {
    
    
    Raycaster,
    Vector2,
    AmbientLight,
    AxesHelper,
    DirectionalLight,
    GridHelper,
    PerspectiveCamera,
    Scene,
    WebGLRenderer,
    MeshLambertMaterial,
} from 'three';
import {
    
     OrbitControls } from "three/examples/jsm/controls/OrbitControls";

import {
    
     IFCLoader } from 'web-ifc-three/IFCLoader.js';
import {
    
     
    IFCSPACE,
    IFCWALLSTANDARDCASE,
	IFCSLAB,
	IFCFURNISHINGELEMENT,
	IFCDOOR,
	IFCWINDOW,
	IFCPLATE,
	IFCMEMBER,
} from 'web-ifc';

import {
    
     acceleratedRaycast, computeBoundsTree, disposeBoundsTree } from 'three-mesh-bvh';

window.scene = null; // 场景
window.mesh = null; // 网格模型对象Mesh
window.renderer = null; // 渲染器
window.camera = null; // 相机
window.controls = null; // 控制器
window.threeCanvas = null; // 渲染场景的canvas元素

//Sets up the IFC loading
const ifcModels = [];
window.ifcLoader = new IFCLoader(); // 创建web-ifc-three渲染器
const raycaster = new Raycaster(); // 光线投射,用于进行鼠标拾取(在三维空间中计算出鼠标移过了什么物体)
raycaster.firstHitOnly = true;
const mouse = new Vector2(); // 鼠标的桌面二维坐标

let animationFrameId = null; // 渲染动画ID,为了方便关闭动画
const subsets = {
    
    }; // 存储已创建的子集

// List of categories names 图层类别名称
const categories = {
    
    
    IFCSPACE,
	IFCWALLSTANDARDCASE,
	IFCSLAB,
	IFCFURNISHINGELEMENT,
	IFCDOOR,
	IFCWINDOW,
	IFCPLATE,
	IFCMEMBER,
};

// 创建子集材料
const preselectMat = new MeshLambertMaterial({
    
    
    transparent: true,
    opacity: 0.6,
    color: 0xff88ff,
    depthTest: false,
});
const ifc = ifcLoader.ifcManager;
// Reference to the previous selection
let preselectModel = {
    
     id: -1 };

/**
 * 场景
 */
function initScene() {
    
    
    scene = new THREE.Scene();  // 创建Three.js场景
    scene.background = new THREE.Color(0x8cc7de);
}

/**
 * 光线
 */
function initLights() {
    
    
    const directionalLight1 = new THREE.DirectionalLight(0xffeeff, 0.8);
    directionalLight1.position.set(1, 1, 1);
    // 告诉平行光需要开启阴影投射
    // directionalLight1.castShadow = true;
    scene.add(directionalLight1);

    const directionalLight2 = new THREE.DirectionalLight(0xffffff, 0.8);
    directionalLight2.position.set(-1, 0.5, -1);
    // 告诉平行光需要开启阴影投射
    // directionalLight2.castShadow = true;
    scene.add(directionalLight2);

    const ambientLight = new THREE.AmbientLight(0xffffee, 0.25);
    scene.add(ambientLight);
}

/**
 * 配置IFCLoader
 */
async function setUpIFCLoader() {
    
    
    // const ifcLoader = new IFCLoader(); // 创建web-ifc-three渲染器
    // await ifcLoader.ifcManager.setWasmPath( 'https://unpkg.com/[email protected]/', true );
    await ifcLoader.ifcManager.setWasmPath('./');

    // 渲染的时候是否显示空间图层
    // 设置可选类别图层
    await ifcLoader.ifcManager.parser.setupOptionalCategories({
    
    
        [ IFCSPACE ]: false,
    });

    await ifcLoader.ifcManager.applyWebIfcConfig({
    
    
        USE_FAST_BOOLS: true
    });

    // Sets up optimized picking
    await ifcLoader.ifcManager.setupThreeMeshBVH(
        computeBoundsTree,
        disposeBoundsTree,
        acceleratedRaycast
    );

    // const ifcURL = URL.createObjectURL(changed.target.files[0]);
    // ifcLoader.load(ifcURL, (ifcModel) => scene.add(ifcModel));

    try {
    
    
        loadIfcModel();
    } catch(err) {
    
    
        console.log(err);
        loadIfcModel();
    }
}
/**
 * 加载IFC模型
*/
function loadIfcModel() {
    
    
    ifcLoader.load('../../data/ifc/rac_advanced_sample_project.ifc', async (ifcModel) => {
    
      // IFC文件地址

        ifcModels.push(ifcModel);
        // mesh = ifcModel.mesh ? addMaterial(ifcModel.mesh, '../../data/ifc/textures/woods-plastics.finishcarpentry.wood.redbirch_1.png') : null;
        mesh = ifcModel.mesh ? ifcModel.mesh : null;
        scene.add(mesh || ifcModel); // 将IFC模型添加到Three.js场景中
        // render();

        if (mesh) {
    
    
            console.log(ifcModel);
            console.log(mesh);
            setCameraView(mesh);
        }

        await setupAllCategories();
    });
}

// Gets all the items of a category
async function getAll(category) {
    
    
	return ifcLoader.ifcManager.getAllItemsOfType(0, category, false);
}

// Creates a new subset containing all elements of a category
async function newSubsetOfType(category) {
    
    
	const ids = await getAll(category);
	return ifcLoader.ifcManager.createSubset({
    
    
		modelID: 0,
		scene,
		ids,
		removePrevious: true,
		customID: category.toString(),
	});
}

async function setupAllCategories() {
    
    
	const allCategories = Object.values(categories); // 获取类型对应的ID集合
	for (let i = 0; i < allCategories.length; i++) {
    
    
		const category = allCategories[i];
        await setupCategory(category, Object.keys(categories)[i]);
        // const newCategory = addMaterial(category, '../../data/ifc/textures/124_1.png');
		// await setupCategory(newCategory);
	}
}

// Creates a new subset and configures the checkbox
async function setupCategory(category, type) {
    
    
	subsets[category] = await newSubsetOfType(category);

    if (type == 'IFCSPACE') {
    
    
        subsets[category].removeFromParent(); // 把IFCSPACE图层踢出去
    }
	// setupCheckBox(category);
}

// Gets the name of a category 从categories图层类别对象中获取对应的name
function getName(category) {
    
    
	const names = Object.keys(categories);
	return names.find(name => categories[name] === category);
}

/**
 * 模型上材质
*/
function addMaterial(mesh, materialUrl) {
    
    
    const geometry = mesh.geometry; // 获取立方体的几何体

    var material;
    if (!materialUrl) {
    
    
        const texture = new THREE.TextureLoader().load(materialUrl); // 创建立方体的材质,并将材质数组传入构造函数
        material = new THREE.MeshBasicMaterial({
    
     map: texture });
    } else {
    
    
        material = new THREE.MeshBasicMaterial({
    
    
            color: 0x00ffff, // 设置材质颜色为蓝色
            opacity: 0.5, // 设置材质透明度为0.5
            transparent: true, // 开启材质透明
            blending: THREE.AdditiveBlending, // 设置材质混合模式为加法混合
            depthWrite: false, // 关闭深度写入,避免发光部分被遮挡
            map: null, // 关闭材质贴图,不需要使用纹理贴图
            emissive: 0x00ffff, // 设置发光颜色为蓝色
            emissiveIntensity: 0.2 // 设置发光强度为0.2
        });
    }

    // 创建新的立方体的网格模型,并将几何体和材质传入构造函数
    const newMesh = new THREE.Mesh(geometry, material);
    return newMesh;
}

/**
 * 元素高亮
*/
function highlight(event, material, model) {
    
    
    const found = cast(event)[0];
    if (found) {
    
    
      // 获取模型ID
        model.id = found.object.modelID;

        // 获取快递ID
        const index = found.faceIndex;
        const geometry = found.object.geometry;
        const id = ifc.getExpressId(geometry, index);

        // 创建子集
        ifcLoader.ifcManager.createSubset({
    
    
            modelID: model.id,
            ids: [id],
            material: material,
            scene: scene,
            removePrevious: true,
        });
    } else {
    
    
        // 移除之前的高亮部分
        ifc.removeSubset(model.id, material);
    }
}

/**
 * 渲染
*/
function initRender(elemId) {
    
    
    // antialias 抗锯齿 || alpha 控制默认的透明值
    renderer = new THREE.WebGLRenderer({
    
     antialias: true, alpha: true }); // 创建Three.js渲染器 antialias: false, 
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.setPixelRatio(window.devicePixelRatio);
    const threeElementId = elemId || 'three-canvas';
    // renderer.domElement.id = threeElementId;
    // document.body.appendChild(renderer.domElement);
    document.getElementById(threeElementId).appendChild(renderer.domElement);

    // 给three场景所在的canvas增加一个双击事件
    window.threeCanvas = document.getElementById(threeElementId);
    window.threeCanvas.ondblclick = pick;
}

/**
 * 辅助工具
*/
function initHelper() {
    
    
    // grids辅助网格
    const grid = new GridHelper(50, 30);
    scene.add(grid);

    // Axes辅助轴线
    // axes轴(右手坐标系):红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.
    const axes = new AxesHelper();
    axes.material.depthTest = false;
    axes.renderOrder = 1;
    scene.add(axes);

    // 相机辅助线
    const cameraHelp = new THREE.CameraHelper(camera);
    scene.add(cameraHelp);
}

/**
 * 控制器
*/
function initControls() {
    
    
    controls = new OrbitControls(camera, renderer.domElement);
    // controls.addEventListener('change', render); // addEventListener 第二个参数 是一个方法,不能直接把renderer写进去 || 不能写成 render()

    /**
     * 设置控制器角度
     * phi 是俯仰角(0 俯视 --> 大于0,趋于仰视)
     * theta 是水平旋转角(小于0,逆时针转;大于0,顺时针转)
     * distance 是距离
     * 
    */
    controls.setAngle = function (phi, theta, distance) {
    
    

        var r = distance || controls.object.position.distanceTo(controls.target);

        var x = r * Math.cos(phi - Math.PI / 2) * Math.sin(theta) + controls.target.x;
        var y = r * Math.sin(phi + Math.PI / 2) + controls.target.y;
        var z = r * Math.cos(phi - Math.PI / 2) * Math.cos(theta) + controls.target.z;

        controls.object.position.set(x, y, z);
        controls.object.lookAt(controls.target);
    };

    controls.enableDamping = true; // 使动画循环使用时阻尼或自转 意思是否有惯性
    // controls.autoRotate = true; // 自动旋转
    controls.rotateSpeed = 0.5; // 旋转速度(ORBIT的旋转速度,鼠标左键),默认1
    controls.panSpeed = 0.5; // 位移速度(ORBIT的位移速度,鼠标右键),默认1
    // 请注意,可以通过将 polarAngle 或者 azimuthAngle 的min和max设置为相同的值来禁用单个轴, 这将使得垂直旋转或水平旋转固定为所设置的值。
    // 你能够垂直旋转的角度的上、下限,范围是0到Math.PI,其默认值为Math.PI。
    // Tips:如下图设置,是限制相机向下旋转,但是可以向上旋转
    controls.maxPolarAngle = Math.PI / 2; 
    controls.minPolarAngle = 0;
    controls.target.set(0, 0, 0);

    // controls.minDistance = 1;// 设置相机距离原点的最近距离
    controls.maxDistance = 200;// 设置相机距离原点的最远距离
}
/**
 * 控制器动画
*/
function animate() {
    
    
    //更新控制器
    render();

    controls.update();
    animationFrameId = requestAnimationFrame(animate);  // 使用requestAnimationFrame可以让浏览器根据自身的渲染节奏调整动画的帧率,从而避免过度渲染,优化three.js渲染性能
}

/**
 * 停止requestAnimationFrame动画
 * 
*/
export function stopAnimationFrame() {
    
    
    window.cancelAnimationFrame(animationFrameId);
}

/**
 * 渲染场景
*/
export function render() {
    
    
    renderer.render(scene, camera);
}

/**
 * 视窗的尺寸重新变化
*/
export function onWindowResize() {
    
    
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);

    render();
}

function initCamera() {
    
    
    camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
    // camera.position.set(0, 40, 100);
    camera.lookAt(new THREE.Vector3(0, 0, 0));
}

/**
 * 获取模型的高度,并设置相机初始视角(也初始化控制器的视角)
 * 
*/
function setCameraView(mesh) {
    
    
    // 获取模型边界框
    const boundingBox = new THREE.Box3().setFromObject(mesh);
    // 获取中心位置
    const center = boundingBox.getCenter(new THREE.Vector3());
    // 获取模型高度
    const height = boundingBox.max.y - boundingBox.min.y;

    // 计算相机位置
    const cameraPosition = new THREE.Vector3(0, height / 2.8, height * 2); // x, y, z
    // 计算相机目标点
    const target = new THREE.Vector3(center.x, center.y, center.z);

    // 计算模型绕 y 轴逆时针旋转 45 度的四元数。
    const angle = Math.PI / 4; // 将相机向左偏转 45 度
    const axis = new THREE.Vector3(0, 1, 0); // 绕 y 轴旋转
    // const quaternion = new THREE.Quaternion().setFromAxisAngle(axis, angle);
    // mesh.applyQuaternion(quaternion);
    
    // mesh.rotateY(angle); // 模型本身旋转,这效果与上面注释的两行代码一样

    // 将模型偏移原先坐标位置,可能会引发一系列的问题
    // const offset = center.clone().negate(); // 计算模型在屏幕中心的偏移量,即将模型平移到屏幕中心的向量。
    // mesh.position.add(offset); // 将模型平移到屏幕中心。

    controls.setAngle(1.4, -0.8, height * 2); // 控制器视角:有点俯视,且逆时针旋转30°左右
    controls.target.copy(center); // 将视角的目标点移到包围盒的中心点
    controls.update(); // 更新视角的位置    

    // 设置相机位置和目标点
    // camera.position.set(cameraPosition);// 设置相机位置 —— 这里好像没有发挥作用
    // camera.position.copy(cameraPosition); // 将源摄像机的属性复制到新摄像机中。—— 这个去掉导致会看不到模型
    camera.lookAt(target); // camera.lookAt 与 orbitcontrol冲突不能使用,要用controls.target代替 //controls.target = new THREE.Vector3(0,-100,0);
    // camera.rotation.y = angle; //物体的均匀从左到又平移可以用相机旋转Y轴来实现 —— 这里好像没有发挥作用
    // camera.fov = 100; // 摄像机视锥体垂直视野角度,从视图的底部到顶部,以角度来表示。默认值是50。这个值是用来确定相机前方的垂直视角,角度越大,我们能够查看的内容就越多。
    console.log('Height:' + height);
}

/**
 * 设置开启多进程
 * 
*/
async function setUpMultiThreading() {
    
    
	await ifcLoader.ifcManager.useWebWorkers(true, "js/ifc/IFCWorker.js"); // Uncaught SyntaxError: Unexpected token '<' (at IFCWorker.js:1:1)
	await ifcLoader.ifcManager.setWasmPath("./");
}
/**
 * 设置模型加载进度
 * 
*/
function setupProgressNotification() {
    
    
	const progressText = document.getElementById('progress-text');
	ifcLoader.ifcManager.setOnProgress((event) => {
    
    
		const result = Math.trunc(event.loaded / event.total * 100);
        if (result == '100') {
    
    
            setTimeout(() => {
    
    
                progressText.innerText = result.toString();
            }, 3000);
        } else {
    
    
            progressText.innerText = result.toString();
        }
	});
}


/**
 * 计算鼠标在屏幕上的位置
 * 
*/
function cast(event) {
    
    

    const bounds = window.threeCanvas.getBoundingClientRect();

    const x1 = event.clientX - bounds.left;
    const x2 = bounds.right - bounds.left;
    mouse.x = (x1 / x2) * 2 - 1;

    const y1 = event.clientY - bounds.top;
    const y2 = bounds.bottom - bounds.top;
    mouse.y = -(y1 / y2) * 2 + 1;

    // 将其放置在指向鼠标的相机上
    raycaster.setFromCamera(mouse, camera);

    // 投射射线
    return raycaster.intersectObjects(ifcModels);
}

/**
 * 鼠标拾取(双击)
 * 
*/
async function pick(event) {
    
    
			
    const found = cast(event)[ 0 ];
    if (found) {
    
    

        const index = found.faceIndex;
        const geometry = found.object.geometry;
        const ifc = ifcLoader.ifcManager;
        const id = ifc.getExpressId(geometry, index);
        console.log(id);
        const modelID = found.object.modelID;
        
        const props = await ifc.getItemProperties(modelID, id); // 直接属性信息(在 IFC 模式中,有两种类型的属性:直接和间接。间接属性(psets、qsets 和类型属性))
        const type = await ifc.getIfcType(modelID, id); // 指定元素的IFC类型
        const materials = await ifc.getMaterialsProperties(modelID, id); // 材质信息
        // const spaces = await ifc.getSpatialStructure(modelID);
        const typeProps = await ifc.getTypeProperties(modelID, id); // 类型属性
        const propSets = await ifc.getPropertySets(modelID, id, true); // 同时获取了属性集和数量集
        
        const output = document.getElementById('output');
        output.innerHTML = JSON.stringify(props, null, 2);
        console.log(props);
        console.log(type);
        console.log(materials);
        // console.log(spaces);
        console.log(typeProps);
        console.log(propSets);
        // logAllSlabs( modelID );

    }

}

/**
 * 释放所有的IFCLoader内存
 * 
*/
// 设置内存处理
// const button = document.getElementById("memory-button");
// button.addEventListener(`click`, () => releaseMemory());
export async function releaseMemory() {
    
    
    // 这将释放所有的IFCLoader内存
    await ifcLoader.ifcManager.dispose();
    ifcLoader = null;
    ifcLoader = new IFCLoader();

    // 如果之前设置了wasm路径,
    // 我们需要重置它
    await ifcLoader.ifcManager.setWasmPath('./');

    // 如果IFC模型是一个数组或对象,
    // 你也必须在那里释放它们。
    // 否则,它们将不会被垃圾回收。
    ifcModels.length = 0;
}

function initModel() {
    
    
    const geometry = new THREE.BoxGeometry();
    const material = new THREE.MeshPhongMaterial({
    
     color: 0xffffff });
    const cube = new THREE.Mesh(geometry, material);
    scene.add(cube);
}

/**
 * Three 初始化渲染场景
 *
 * @param {Object} comp vue组件的this对象
 * @param {String} elemId 渲染模型的元素Id
 * @param {Object} config 额外增加的一些配置
 * @param {Object} offsetOption 模型渲染偏移配置
 * @return {void}  无
 */
export async function initThree(elemId, comp, config, offsetOption) {
    
    
    //Scene
    initScene();

    //Camera
    initCamera();

    //Lights
    initLights();

    //Setup IFC Loader
    setUpIFCLoader();

    //Renderer
    initRender(elemId);

    //Helper
    initHelper();

    //Controls
    initControls();

    setUpMultiThreading();
    setupProgressNotification();

    //Animation loop
    animate();

    window.addEventListener('resize', onWindowResize);
    window.onclick = (event) => highlight(event, preselectMat, preselectModel);
}

I have put a lot of things in this js, including working threads, materials, camera angle adjustment, etc. You can delete the code you don't need based on the comments. (I won’t say it, I’m just too lazy to delete it myself…)

styles.css
* {
    
    
    padding: 0;
    margin: 0;
}

html, body {
    
    
    overflow: hidden;
}

#threeCanvas {
    
    
    position: fixed;
    top: 0;
    left: 0;
    outline: none;
}

src folder

src folder directory
There is only one .vue demo page, and the name is whatever you want.

vue page used
<template>
    <div class="model">
        <div id="threeContainer">
            <div style="position: absolute;width: 300px;background: rgba(255,255,255, 0.9);color: #333;left: 1px;border-radius: 5px;height: 350px;overflow: auto;text-align: left;">
                <p>Properties:</p>
                <p id="output" style="padding: 0 5px;"></p>
            </div>

            <!-- 模型加载进度 -->
            <div style="position: absolute;bottom: 0;left: 10px;">
                <p>Progress:<span id="progress-text">0</span> %</p>
            </div>
        </div>
    </div>
</template>

<script>
// 1 创建视图容器的vue组件
var EarthComp = {
    
    
    data() {
    
    
        return {
    
    
        };
    },
    watch: {
    
    
    },
    created() {
    
    
    },
    // 1.1 资源创建
    mounted() {
    
    
        threeIfcScene.initThree('threeContainer');
    },
    // 1.2 资源销毁
    beforeDestroy() {
    
    
    },
    methods: {
    
    
    },
};

export default EarthComp;
</script>

<style scoped>
    html, body {
    
    
        width: 100%;
        height: 100%;
        margin: 0;
        padding: 0;
        overflow: hidden;
        position: relative;
    }
</style>

run

Just run it directly start 命令.
It will compress and convert the relevant js in rollup.config.mjs into bundle.js and start the project.

Open this article and verify it while reading it

【Three.js】Integrate IFC.js into the vue project

Guess you like

Origin blog.csdn.net/jiangxinyu50/article/details/129947635