1,介绍
该示例使用的是 r95版本Three.js库。
主要实现功能:引用水厂模型进行展示,模拟水流效果,动态显示数据信息。效果图如下:
2,主要说明
1,加载模型设置模型颜色效果并添加到场景中。
2,创建管道并添加纹理,这里不过多介绍具体可查看上一篇文章。
3,添加标签并实时刷新渲染实时数据
引入模型设置颜色效果并添加到场景中,这里只介绍添加模型方法
function initShuiChang() { var loader = new THREE.GLTFLoader(); // assets/models/fang/shapan.glb' loader.load('assets/models/shuichang/shuichang.glb', function(result) { var object = result.scene; console.log(object) object.traverse(function(item) { if (item instanceof THREE.Mesh) { item.material.color.set(0x1DA9FC); item.material.transparent = true; item.material.opacity = 0.5; } }); object.scale.set(2, 2, 2); object.rotateY(3.14); scene.add(object); }); }
添加标签并实时刷新渲染实时数据
function createPath(pointsArr) { pointsArr = pointsArr.map((point) => new THREE.Vector3(...point)); // 将参数数组转换成点数组的形式 // 方法一:自定义三维路径 curvePath const path = new THREE.CurvePath(); for (let i = 0; i < pointsArr.length - 1; i++) { const lineCurve = new THREE.LineCurve3(pointsArr[i], pointsArr[i + 1]); // 每两个点之间形成一条三维直线 path.curves.push(lineCurve); // curvePath有一个curves属性,里面存放组成该三维路径的各个子路径 } return path; } const count = 200 const gColor = '#28f260' const prop1 = { width: 416, height: 112, pos: [60, 35, 30], // scale:[24, 6, 0] scale: [0.24375 * count, 0.065625 * count, 0] } const tab1 = [ ['一级高压泵后压力:', '20.2bar', gColor], ['一级循环泵后压力:', '0.2bar', ] ] const prop2 = { width: 352, height: 196, pos: [-60, 40, 0], scale: [0.20625 * count, 0.11484375 * count, 0], } const tab2 = [ ['进水情况', ''], ['进水温度:', '25.6℃', gColor], ['进水流量:', '2.5m³', ], ['进水电导:', '28.5ms/cm', gColor], ] const prop3 = { width: 384, height: 256, pos: [5, 60, -60], scale: [0.225 * count, 0.15 * count, 0] } const tab3 = [ ['产水情况', ''], ['一级回收率:', '58%', gColor], ['一级产水流量:', '1.75㎡', ], ['一级产水电量:', '980.5/cm', gColor], ['一级产水电量:', '0.5bar', gColor], ] const prop4 = { width: 256, height: 64, pos: [-85, 30, 40], scale: [0.15 * count, 0.0375 * count, 0], } const tab4 = [ ['泵机状态 ', '• 开启', gColor], ] const prop5 = { width: 256, height: 64, pos: [-10, 50, 30], scale: [0.15 * count, 0.0375 * count, 0], } const tab5 = [ ['阀门状态 ', '• 开启', gColor], ] const props = [prop1, prop2, prop3, prop4, prop5] const tabs = [tab1, tab2, tab3, tab4, tab5] // 调用渲染标签,并定时刷新 handleDatachange(); setInterval(handleDatachange, 2000) function handleDatachange() { let r = (Math.random() * 10 + 20).toFixed(2) tab2[1][1] = r + '℃' if (r > 25) tab2[1][2] = 'red' else tab2[1][2] = gColor r = Math.random().toFixed(2) tab3[4][1] = r + 'bar' if (r > 0.5) tab3[4][2] = 'red'; else tab3[4][2] = gColor if (Math.random() > 0.5) { tab5[0][1] = '• 开启' tab5[0][2] = gColor } else { tab5[0][1] = '• 关闭' tab5[0][2] = 'red' } console.time('render sprite') initSprite() console.timeEnd('render sprite') } function initSprite() { clearSprite(); (scene.children || []).forEach((v, idx) => { if (v.type == 'Mesh') { const borderColor = 'rgba(39, 179, 236, 1)' const color = 'rgba(255,255,255, 1)' makeTextSprite(scene, tabs, props, { color: color, borderColor, backgroundColor: 'rgba(255,255,255,0.05)' }); } }); } // 清空雪碧图 function clearSprite(type = 'Sprite') { const children = []; (scene.children || []).forEach((v, idx) => { if (v.type !== type) { children.push(v); } }); scene.children = children; }
/* 创建字体精灵 */ function makeTextSprite(scene, tabs, props, parameters) { if (parameters === undefined) parameters = {} tabs.forEach((tab, k) => { let { width, height } = props[k] /* 创建画布 */ let canvas = document.createElement('canvas'); let context = canvas.getContext('2d') canvas.width = width canvas.height = height let gap = 10 let fontface = parameters.hasOwnProperty("fontface") ? parameters["fontface"] : "sans-serif" /* 字体大小 */ let fontsize = parameters.hasOwnProperty("fontsize") ? parameters["fontsize"] : 30 let color = parameters.hasOwnProperty("color") ? parameters["color"] : 'rgba(0, 0, 0, 1.0)' /* 边框厚度 */ let borderWith = parameters.hasOwnProperty("borderWith") ? parameters["borderWith"] : 2 /* 边框颜色 */ let borderColor = parameters.hasOwnProperty("borderColor") ? parameters["borderColor"] : { r: 0, g: 0, b: 0, a: 1.0 } /* 背景颜色 */ let backgroundColor = parameters.hasOwnProperty("backgroundColor") ? parameters["backgroundColor"] : { r: 255, g: 255, b: 255, a: 1.0 } /* 字体加粗 */ // context.font = "Bold " + fontsize + "px " + fontface context.font = fontsize + "px " + fontface let unit = gap + fontsize /* 背景颜色 */ context.fillStyle = backgroundColor /* 边框的颜色 */ context.strokeStyle = borderColor context.lineWidth = borderWith /* 绘制圆角矩形 */ roundRect(context, gap, gap, width - gap, height - gap, 4) tab.forEach((d, i) => { context.fillStyle = color; context.fillText(d[0], gap * 2, gap + unit * (i + 1)) if (d[2]) { context.fillStyle = d[2] } context.fillText(d[1], gap * 2 + measureText(d[0], context), gap + unit * (i + 1)) }) /* 画布内容用于纹理贴图 */ let texture = new THREE.Texture(canvas); texture.needsUpdate = true let spriteMaterial = new THREE.SpriteMaterial({ map: texture, // sizeAttenuation:false, // transparent:true }); let sprite = new THREE.Sprite(spriteMaterial) // console.log(sprite.spriteMaterial) /* 缩放比例 */ sprite.scale.set(...props[k].scale) sprite.center = new THREE.Vector2(0, 0); scene.add(sprite); sprite.position.set(...props[k].pos); }) } function measureText(text, ctx, font) { if (font) ctx.font = font return ctx.measureText(text).width; } /* 绘制圆角矩形 */ function roundRect(ctx, x, y, w, h, r) { ctx.beginPath(); ctx.moveTo(x + r, y); ctx.lineTo(x + w - r, y); ctx.quadraticCurveTo(x + w, y, x + w, y + r); ctx.lineTo(x + w, y + h - r); ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h); ctx.lineTo(x + r, y + h); ctx.quadraticCurveTo(x, y + h, x, y + h - r); ctx.lineTo(x, y + r); ctx.quadraticCurveTo(x, y, x + r, y); ctx.closePath(); // ctx.shadowColor = "#qb95cf"; // ctx.shadowOffsetX = 0; // ctx.shadowOffsetY = 0; // ctx.shadowBlur = 4; ctx.fill(); ctx.stroke(); ctx.shadowColor = ""; ctx.shadowOffsetX = 0; ctx.shadowOffsetY = 0; ctx.shadowBlur = 0; }