【Three.js】将IFC.js结合到vue项目中——实例

代码实例

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

项目主目录

我这里是把rollup.config.jsvue.config,js改成.mjs后缀了。
——按步骤来的朋友,可以先不改这个后缀,如果后续报错了,再改。
项目主目录

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() // 解析模块路径
    ]
}

声明vue.config.mjs,我就是改了个后缀,里面没有任何与 ifc.js 相关的配置,所以不用追问这里面的代码是啥。

public文件夹

如下图所示,是public文件夹的内容:

  • 蓝色:是.ifc的模型数据存放的地方。这个地方是demo暂时存放的,后续要布到生产上的话,建议模型直接放到服务器,引用服务器地址。
  • 绿色:是根据IFC.js官方文档上进行的工作线程尝试,如果不需要,可以找到对应的代码删去。
  • 黄色:是调用IFC.js 、渲染模型、模型交互等集大成的公用js。很重要。
  • 红色:是根据IFC.js官方文档上建议的,把wasm单独从node_modules里面提取出来存放

public文件夹内容

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>

注意:这里要记得引用rollup.config.js转换后的bundle.js

threeIfcScene.js

重中之重,不要眨眼!!!

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);
}

这个js里面我已经放了很多东西了,有工作线程、材质、相机视角调整等等,可以根据注释去删掉自己不需要的代码。(我才不会说,我自己是懒得删了…)

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

html, body {
    
    
    overflow: hidden;
}

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

src文件夹

src文件夹目录
只有一个.vue做demo页面,名字随心意定。

使用的vue页面
<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>

运行

直接运行 start 命令就行了。
它会去把rollup.config.mjs里面相关的js压缩转化成bundle.js,并启动项目。

打开这篇文章,边看边印证

【Three.js】将IFC.js结合到vue项目中

猜你喜欢

转载自blog.csdn.net/jiangxinyu50/article/details/129947635
今日推荐