ng-cw-v12
Version:
Angular UI Component Library
750 lines • 188 kB
JavaScript
import { Component, Input } from '@angular/core';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment';
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min';
import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader.js';
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
import * as i0 from "@angular/core";
import * as i1 from "./local.service";
import * as i2 from "@angular/common";
export class ObjViewerComponent {
constructor(eleRef, ls, ngZone) {
this.eleRef = eleRef;
this.ls = ls;
this.ngZone = ngZone;
/** 摄像机初始方位 */
this.ncCameraPlacement = 'default';
/** 背景图片URL */
this._bgUrl = '';
/** 操作提示 */
this._tips = false;
/** 控制面板 */
this._control = false;
/** 背景是否透明 */
this._alpha = false;
this.showLoading = false;
this.loadingText = '';
this.loadingText2 = '';
this.domId = '#objViewer';
this.cameraPlacement = 'default';
this.size = 0;
this.state = {
toneMapping: THREE.LinearToneMapping,
toneMappingExposure: 0,
background: false,
backgroundColor: '#191919',
useBgUrl: false,
useBgSphere: false,
axesHelper: false,
wireframe: false,
autoRotate: false,
autoRotateSpeed: 0.5,
ambientLight: true,
ambientLightColor: '#FFFFFF',
ambientLightIntensity: 2,
directionalLight: true,
directionalLightColor: '#FFFFFF',
directionalLightIntensity: 2,
directionalLightPosition: {
x: 0,
y: 0,
z: 0
},
directionalLightHelper: false,
directionalLight2: true,
directionalLightColor2: '#FFFFFF',
directionalLightIntensity2: 2,
directionalLightPosition2: {
x: 0,
y: 0,
z: 0
},
directionalLightHelper2: false,
pointLight: false,
pointLightColor: '#FFFFFF',
pointLightIntensity: 1,
pointLightDistance: 0,
pointLightDecay: 0.1,
pointLightPosition: {
x: 0,
y: 0,
z: 0
},
pointLightHelper: false
};
}
/** 是否启用背景 */
set ncBg(value) {
this.state.background = value !== null && value !== undefined && value !== false && value !== 'false';
}
/** 背景色值 */
set ncBgColor(value) {
this.state.backgroundColor = value;
}
set ncBgUrl(value) {
this._bgUrl = value;
this.state.useBgUrl = value ? true : false; //传了值,默认启用背景图
this.state.useBgSphere = value ? true : false; //传了值,默认启用背景球
}
get ncBgUrl() {
return this._bgUrl;
}
set ncTips(value) {
this._tips = value !== null && value !== undefined && value !== false && value !== 'false';
}
get ncTips() {
return this._tips;
}
set ncControl(value) {
this._control = value !== null && value !== undefined && value !== false && value !== 'false';
}
get ncControl() {
return this._control;
}
set ncAlpha(value) {
this._alpha = value !== null && value !== undefined && value !== false && value !== 'false';
}
get ncAlpha() {
return this._alpha;
}
/** 自动旋转 */
set ncAutoRotate(value) {
this.state.autoRotate = value !== null && value !== undefined && value !== false && value !== 'false';
}
/** 自动旋转速度 */
set ncAutoRotateSpeed(value) {
this.state.autoRotateSpeed = value;
}
ngOnChanges(changes) {
if (changes['ncObjInfo'] && !changes['ncObjInfo'].firstChange) {
this.updateModel();
}
if (changes['ncCameraPlacement'] && !changes['ncCameraPlacement'].firstChange) {
this.setCameraPlacement();
}
if (changes['ncBg'] && !changes['ncBg'].firstChange
|| changes['ncBgColor'] && !changes['ncBgColor'].firstChange
|| changes['ncBgUrl'] && !changes['ncBgUrl'].firstChange) {
this.updateBackground();
}
if (changes['ncAutoRotate'] && !changes['ncAutoRotate'].firstChange
|| changes['ncAutoRotateSpeed'] && !changes['ncAutoRotateSpeed'].firstChange) {
this.toggleAutoRotate();
}
}
ngAfterViewInit() {
this.onWindowResize = this.onWindowResize.bind(this);
this.animate = this.animate.bind(this);
if (this.ls.isBrowser) {
this.ngZone.runOutsideAngular(() => {
window.addEventListener('resize', this.onWindowResize);
});
}
setTimeout(() => {
this.ngZone.runOutsideAngular(() => {
this.init();
});
});
}
ngOnDestroy() {
if (this.ls.isBrowser) {
window.removeEventListener('resize', this.onWindowResize);
}
this.webglLoseContext();
}
init() {
let objArea = this.eleRef.nativeElement.querySelector(this.domId);
this.domWidth = objArea.offsetWidth;
this.domHeight = objArea.offsetHeight;
this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: this.ncAlpha });
this.renderer.setPixelRatio(objArea.devicePixelRatio);
this.renderer.setSize(this.domWidth, this.domHeight);
this.renderer.outputColorSpace = THREE.SRGBColorSpace;
objArea.appendChild(this.renderer.domElement);
this.scene = new THREE.Scene();
const environment = new RoomEnvironment();
const pmremGenerator = new THREE.PMREMGenerator(this.renderer);
this.scene.environment = pmremGenerator.fromScene(environment).texture;
this.updateModel();
}
render() {
this.renderer.render(this.scene, this.camera);
}
animate() {
requestAnimationFrame(this.animate);
// required if controls.enableDamping or controls.autoRotate are set to true
if (this.controls) {
this.controls.update();
}
if (this.scene && this.camera) {
this.renderer.render(this.scene, this.camera);
}
}
onWindowResize() {
setTimeout(() => {
let objArea = this.eleRef.nativeElement.querySelector(this.domId);
const width = objArea.offsetWidth;
const height = objArea.offsetHeight;
this.renderer.setPixelRatio(objArea.devicePixelRatio);
this.renderer.setSize(width, height);
this.render();
});
}
//更新模型
updateModel() {
if (!this.ncObjInfo.objUrl || !this.ncObjInfo.mtlUrl) {
return;
}
this.showLoading = true;
this.loadingText = '模型加载中...';
this.loadingText2 = '';
if (this.objScene) {
this.scene.remove(this.objScene);
this.objScene = null;
}
let baseUrl = this.ncObjInfo.objUrl.split('/').slice(0, -1).join('/');
// 创建新的LoadingManager来重置进度计数
const manager = new THREE.LoadingManager();
manager.onProgress = (url, loaded, total) => {
this.loadingText = `正在加载资源(${loaded}/${total})`;
if (loaded == total) {
this.showLoading = false;
}
};
new MTLLoader(manager).load(this.ncObjInfo.mtlUrl, (MaterialCreator) => {
// 在preload()之前先处理材质信息,如果有贴图,则替换为obj同目录下的图片
for (let name in MaterialCreator.materialsInfo) {
let materialInfo = MaterialCreator.materialsInfo[name];
// 需要处理的贴图类型
const mapTypes = [
'map_kd',
'map_ks',
'map_ka',
'map_d',
'map_bump',
'bump',
'disp',
'decal' // 贴花贴图
];
mapTypes.map((type) => {
if (materialInfo.hasOwnProperty(type)) {
let map_type_url = materialInfo[type];
let map_type_name = map_type_url.split(/[\/\\]/).pop();
materialInfo[type] = baseUrl + '/' + map_type_name;
}
});
}
MaterialCreator.preload();
new OBJLoader(manager).setMaterials(MaterialCreator).load(this.ncObjInfo.objUrl, (object) => {
this.objScene = object;
const box = new THREE.Box3().setFromObject(this.objScene);
this.size = box.getSize(new THREE.Vector3()).length();
this.center = box.getCenter(new THREE.Vector3());
//将坐标系原点设置在模型中心
this.objScene.position.x -= this.center.x;
this.objScene.position.y -= this.center.y;
this.objScene.position.z -= this.center.z;
//PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number )
// fov — 摄像机视锥体垂直视野角度,眼睛张开的角度,0闭眼
// aspect — 摄像机视锥体长宽比
// near — 摄像机视锥体近端面,能看到的最近垂直距离
// far — 摄像机视锥体远端面,能看到的最远垂直距离
this.camera = new THREE.PerspectiveCamera(50, this.domWidth / this.domHeight, this.size / 100, this.size * 100);
this.camera.updateProjectionMatrix();
//position-摄像机位置
//lookAt-摄像机望向位置
this.setCameraPlacement();
//controls
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
//根据模型size初始化state部分参数
this.state.directionalLightPosition.x = this.size;
this.state.directionalLightPosition.y = this.size / 2;
this.state.directionalLightPosition.z = this.size;
this.state.directionalLightPosition2.x = -this.size;
this.state.directionalLightPosition2.y = -this.size / 2;
this.state.directionalLightPosition2.z = -this.size;
this.state.pointLightPosition.x = this.size / 2;
this.state.pointLightPosition.y = 0;
this.state.pointLightPosition.z = 0;
this.state.pointLightDistance = this.size;
//添加默认光源(添加至camera,光源固定位置,不随场景旋转)
// this.scene.add(this.camera);
// let defaultLight = new THREE.DirectionalLight('#FFFFFF', 1.5);
// defaultLight.position.set(0, 0, this.size / 2);
// this.camera.add(defaultLight);
//添加GUI控制面板
if (this.ncControl) {
this.addGUI();
}
else {
if (this.gui) {
this.gui.destroy();
this.gui = null;
}
}
//根据state参数初始化相关设置
this.updateToneMapping();
this.updateToneMappingExposure();
this.updateBackground();
this.toggleAxesHelper();
this.toggleWireframe();
this.toggleAutoRotate();
this.toggleAmbientLight();
// this.updateAmbientLight();
this.toggleDirectionalLight();
// this.updateDirectionalLight();
this.toggleDirectionalLightHelper();
this.toggleDirectionalLight2();
// this.updateDirectionalLight2();
this.toggleDirectionalLightHelper2();
this.togglePointLight();
// this.updatePointLight();
this.togglePointLightHelper();
this.scene.add(this.objScene);
this.animate();
}, xhr => {
if (xhr.loaded != xhr.total) {
this.loadingText2 = '模型下载 ' + (xhr.loaded / xhr.total * 100).toFixed(0) + ' %';
}
else {
this.loadingText2 = '';
}
});
});
}
//设置相机角度和望向位置
setCameraPlacement() {
switch (this.cameraPlacement) {
case 'left': //x-红
this.camera.position.set(-this.size, 0, 0);
this.camera.lookAt(0, 0, 0);
break;
case 'right': //x-红
this.camera.position.set(this.size, 0, 0);
this.camera.lookAt(0, 0, 0);
break;
case 'top': //y-绿
this.camera.position.set(0, this.size, 0);
this.camera.lookAt(0, 0, 0);
break;
case 'bottom': //y-绿
this.camera.position.set(0, -this.size, 0);
this.camera.lookAt(0, 0, 0);
break;
case 'front': //z-蓝
this.camera.position.set(0, 0, this.size);
this.camera.lookAt(0, 0, 0);
break;
case 'back': //z-蓝
this.camera.position.set(0, 0, -this.size);
this.camera.lookAt(0, 0, 0);
break;
default:
this.camera.position.set(this.size / 1.3, this.size / 1.5, this.size / 1.3);
this.camera.lookAt(0, 0, 0);
break;
}
}
//add GUI
addGUI() {
//折叠参数
let closeState = {
gui: false,
displayFolder: false,
ambientLightFolder: true,
directionalLightFolder: true,
directionalLightHelperFolder: false,
directionalLightFolder2: true,
directionalLightHelperFolder2: false,
pointLightFolder: true,
pointlLightHelperFolder: false
};
if (this.gui) {
//记录上个gui的面板折叠值
closeState = {
gui: this.gui._closed,
displayFolder: this.gui.children[0]._closed,
ambientLightFolder: this.gui.children[1]._closed,
directionalLightFolder: this.gui.children[2]._closed,
directionalLightHelperFolder: this.gui.children[2].children[3]._closed,
directionalLightFolder2: this.gui.children[3]._closed,
directionalLightHelperFolder2: this.gui.children[3].children[3]._closed,
pointLightFolder: this.gui.children[4]._closed,
pointlLightHelperFolder: this.gui.children[4].children[5]._closed
};
this.gui.destroy();
this.gui = null;
}
this.gui = new GUI({ title: '控制面板', container: this.eleRef.nativeElement.querySelector('#control') });
if (closeState.gui) {
this.gui.close();
}
const displayFolder = this.gui.addFolder('显示设置');
if (closeState.displayFolder) {
displayFolder.close();
}
displayFolder.add(this.state, 'toneMapping', {
'ACESFilmic': THREE.ACESFilmicToneMapping,
'Linear': THREE.LinearToneMapping,
'Reinhard': THREE.ReinhardToneMapping,
'Cineon': THREE.CineonToneMapping,
'AgX': THREE.AgXToneMapping,
'Neutral': THREE.NeutralToneMapping
}).name('色调').onChange(() => this.updateToneMapping());
displayFolder.add(this.state, 'toneMappingExposure', -10, 5, 0.01).name('曝光').onChange(() => this.updateToneMappingExposure());
displayFolder.add(this.state, 'background').name('启用场景背景').listen().onChange(() => this.updateBackground());
displayFolder.addColor(this.state, 'backgroundColor').name('场景背景色').listen().onChange(() => this.updateBackground());
//传入背景图才显示背景图设置
if (this.ncBgUrl) {
displayFolder.add(this.state, 'useBgUrl').name('使用背景贴图').listen().onChange(() => this.updateBackground());
displayFolder.add(this.state, 'useBgSphere').name('球形背景贴图').listen().onChange(() => this.updateBackground());
}
displayFolder.add(this.state, 'axesHelper').name('坐标系').onChange(() => this.toggleAxesHelper());
displayFolder.add(this.state, 'wireframe').name('线框模式').onChange(() => this.toggleWireframe());
displayFolder.add(this.state, 'autoRotate').listen().name('自动旋转').onChange(() => this.toggleAutoRotate());
displayFolder.add(this.state, 'autoRotateSpeed', -10.0, 10.0, 0.1).listen().name('旋转速度').onChange(() => this.toggleAutoRotate());
const ambientLightFolder = this.gui.addFolder('环境光');
if (closeState.ambientLightFolder) {
ambientLightFolder.close();
}
ambientLightFolder.add(this.state, 'ambientLight').name('启用').onChange(() => this.toggleAmbientLight());
ambientLightFolder.addColor(this.state, 'ambientLightColor').name('颜色').onChange(() => this.updateAmbientLight());
ambientLightFolder.add(this.state, 'ambientLightIntensity', 0, 10, 0.1).name('强度').onChange(() => this.updateAmbientLight());
const directionalLightFolder = this.gui.addFolder('定向光1');
if (closeState.directionalLightFolder) {
directionalLightFolder.close();
}
directionalLightFolder.add(this.state, 'directionalLight').name('启用').onChange(() => this.toggleDirectionalLight());
directionalLightFolder.addColor(this.state, 'directionalLightColor').name('颜色').onChange(() => this.updateDirectionalLight());
directionalLightFolder.add(this.state, 'directionalLightIntensity', 0, 10, 0.1).name('强度').onChange(() => this.updateDirectionalLight());
const directionalLightHelperFolder = directionalLightFolder.addFolder('定向光位置');
if (closeState.directionalLightHelperFolder) {
directionalLightHelperFolder.close();
}
directionalLightHelperFolder.add(this.state, 'directionalLightHelper').listen().name('位置模拟').onChange(() => this.toggleDirectionalLightHelper());
directionalLightHelperFolder.add(this.state.directionalLightPosition, 'x', -this.size, this.size).name('x(坐标系-红)').decimals(2).onChange(() => this.updateDirectionalLight());
directionalLightHelperFolder.add(this.state.directionalLightPosition, 'y', -this.size, this.size).name('y(坐标系-绿)').decimals(2).onChange(() => this.updateDirectionalLight());
directionalLightHelperFolder.add(this.state.directionalLightPosition, 'z', -this.size, this.size).name('z(坐标系-蓝)').decimals(2).onChange(() => this.updateDirectionalLight());
const directionalLightFolder2 = this.gui.addFolder('定向光2');
if (closeState.directionalLightFolder2) {
directionalLightFolder2.close();
}
directionalLightFolder2.add(this.state, 'directionalLight2').name('启用').onChange(() => this.toggleDirectionalLight2());
directionalLightFolder2.addColor(this.state, 'directionalLightColor2').name('颜色').onChange(() => this.updateDirectionalLight2());
directionalLightFolder2.add(this.state, 'directionalLightIntensity2', 0, 10, 0.1).name('强度').onChange(() => this.updateDirectionalLight2());
const directionalLightHelperFolder2 = directionalLightFolder2.addFolder('定向光位置');
if (closeState.directionalLightHelperFolder2) {
directionalLightHelperFolder2.close();
}
directionalLightHelperFolder2.add(this.state, 'directionalLightHelper2').listen().name('位置模拟').onChange(() => this.toggleDirectionalLightHelper2());
directionalLightHelperFolder2.add(this.state.directionalLightPosition2, 'x', -this.size, this.size).name('x(坐标系-红)').decimals(2).onChange(() => this.updateDirectionalLight2());
directionalLightHelperFolder2.add(this.state.directionalLightPosition2, 'y', -this.size, this.size).name('y(坐标系-绿)').decimals(2).onChange(() => this.updateDirectionalLight2());
directionalLightHelperFolder2.add(this.state.directionalLightPosition2, 'z', -this.size, this.size).name('z(坐标系-蓝)').decimals(2).onChange(() => this.updateDirectionalLight2());
const pointLightFolder = this.gui.addFolder('点光源');
if (closeState.pointLightFolder) {
pointLightFolder.close();
}
pointLightFolder.add(this.state, 'pointLight').name('启用').onChange(() => this.togglePointLight());
pointLightFolder.addColor(this.state, 'pointLightColor').name('颜色').onChange(() => this.updatePointLight());
pointLightFolder.add(this.state, 'pointLightIntensity', 0, 10, 0.1).name('强度').onChange(() => this.updatePointLight());
pointLightFolder.add(this.state, 'pointLightDistance', 0, this.size).name('范围').decimals(2).onChange(() => this.updatePointLight());
pointLightFolder.add(this.state, 'pointLightDecay', 0, 2, 0.1).name('衰减').onChange(() => this.updatePointLight());
const pointlLightHelperFolder = pointLightFolder.addFolder('点光源位置');
if (closeState.pointlLightHelperFolder) {
pointlLightHelperFolder.close();
}
pointlLightHelperFolder.add(this.state, 'pointLightHelper').listen().name('位置模拟').onChange(() => this.togglePointLightHelper());
pointlLightHelperFolder.add(this.state.pointLightPosition, 'x', -this.size, this.size).name('x(坐标系-红)').decimals(2).onChange(() => this.updatePointLight());
pointlLightHelperFolder.add(this.state.pointLightPosition, 'y', -this.size, this.size).name('y(坐标系-绿)').decimals(2).onChange(() => this.updatePointLight());
pointlLightHelperFolder.add(this.state.pointLightPosition, 'z', -this.size, this.size).name('z(坐标系-蓝)').decimals(2).onChange(() => this.updatePointLight());
}
//色调
updateToneMapping() {
this.renderer.toneMapping = this.state.toneMapping;
}
//曝光
updateToneMappingExposure() {
this.renderer.toneMappingExposure = Math.pow(2, this.state.toneMappingExposure);
}
//背景
updateBackground() {
// 移除已存在的背景球体
if (this.backgroundSphere) {
this.scene.remove(this.backgroundSphere);
this.backgroundSphere = null;
}
//启用背景
if (this.state.background) {
//使用背景图
if (this.state.useBgUrl && this.ncBgUrl) {
//使用球体背景
if (this.state.useBgSphere) {
// 创建球形几何体
const geometry = new THREE.SphereGeometry(this.size * 50, 32, 32);
// 将法线朝内,这样纹理会显示在球体内侧
geometry.scale(-1, 1, 1);
// 加载背景图纹理
new THREE.TextureLoader().load(this.ncBgUrl, (texture) => {
texture.colorSpace = THREE.SRGBColorSpace;
const material = new THREE.MeshBasicMaterial({
map: texture
});
this.backgroundSphere = new THREE.Mesh(geometry, material);
this.scene.add(this.backgroundSphere);
});
}
else {
//使用平面背景图
new THREE.TextureLoader().load(this.ncBgUrl, (texture) => {
texture.colorSpace = THREE.SRGBColorSpace;
this.scene.background = texture;
});
}
}
else {
//使用背景色
this.scene.background = new THREE.Color(this.state.backgroundColor);
}
}
else {
//关闭背景
this.scene.background = null;
}
}
toggleAxesHelper() {
//x-红,y-绿,z-蓝
if (this.state.axesHelper) {
if (this.axesHelper) {
this.scene.remove(this.axesHelper);
this.axesHelper = null;
}
this.axesHelper = new THREE.AxesHelper(this.size);
this.scene.add(this.axesHelper);
}
else {
if (this.axesHelper) {
this.scene.remove(this.axesHelper);
this.axesHelper = null;
}
}
}
//线框模式
toggleWireframe() {
this.traverseMaterials(this.objScene, (material) => {
material.wireframe = this.state.wireframe;
if (material instanceof THREE.PointsMaterial) {
material.size = 1;
}
});
}
//遍历材质
traverseMaterials(object, callback) {
object.traverse((node) => {
if (!node.geometry)
return;
const materials = Array.isArray(node.material) ? node.material : [node.material];
materials.forEach(callback);
});
}
//自动旋转
toggleAutoRotate() {
this.controls.autoRotate = this.state.autoRotate;
this.controls.autoRotateSpeed = this.state.autoRotateSpeed;
}
toggleAmbientLight() {
if (this.state.ambientLight) {
if (this.ambientLight) {
this.scene.remove(this.ambientLight);
this.ambientLight = null;
}
this.ambientLight = new THREE.AmbientLight(this.state.ambientLightColor, this.state.ambientLightIntensity);
this.scene.add(this.ambientLight);
}
else {
if (this.ambientLight) {
this.scene.remove(this.ambientLight);
this.ambientLight = null;
}
}
}
//更新环境光
updateAmbientLight() {
if (this.state.ambientLight) {
this.ambientLight.color.set(this.state.ambientLightColor);
this.ambientLight.intensity = this.state.ambientLightIntensity;
}
}
toggleDirectionalLight() {
if (this.state.directionalLight) {
if (this.directionalLight) {
this.scene.remove(this.directionalLight);
this.directionalLight = null;
}
this.directionalLight = new THREE.DirectionalLight(this.state.directionalLightColor, this.state.directionalLightIntensity);
this.directionalLight.position.set(this.state.directionalLightPosition.x, this.state.directionalLightPosition.y, this.state.directionalLightPosition.z);
this.scene.add(this.directionalLight);
}
else {
if (this.directionalLight) {
this.scene.remove(this.directionalLight);
this.directionalLight = null;
}
//关闭位置模拟
this.state.directionalLightHelper = false;
this.toggleDirectionalLightHelper();
}
}
//更新定向光
updateDirectionalLight() {
if (this.state.directionalLight) {
this.directionalLight.color.set(this.state.directionalLightColor);
this.directionalLight.intensity = this.state.directionalLightIntensity;
this.directionalLight.position.set(this.state.directionalLightPosition.x, this.state.directionalLightPosition.y, this.state.directionalLightPosition.z);
if (this.directionalLightHelper) {
this.directionalLightHelper.update();
}
}
}
toggleDirectionalLightHelper() {
if (this.state.directionalLightHelper) {
if (this.directionalLight) { //开启定向光才能开启位置模拟
if (this.directionalLightHelper) {
this.scene.remove(this.directionalLightHelper);
this.directionalLightHelper = null;
}
this.directionalLightHelper = new THREE.DirectionalLightHelper(this.directionalLight, this.size / 20, 0xff0000);
this.scene.add(this.directionalLightHelper);
}
else {
this.state.directionalLightHelper = false;
}
}
else {
if (this.directionalLightHelper) {
this.scene.remove(this.directionalLightHelper);
this.directionalLightHelper = null;
}
}
}
toggleDirectionalLight2() {
if (this.state.directionalLight2) {
if (this.directionalLight2) {
this.scene.remove(this.directionalLight2);
this.directionalLight2 = null;
}
this.directionalLight2 = new THREE.DirectionalLight(this.state.directionalLightColor2, this.state.directionalLightIntensity2);
this.directionalLight2.position.set(this.state.directionalLightPosition2.x, this.state.directionalLightPosition2.y, this.state.directionalLightPosition2.z);
this.scene.add(this.directionalLight2);
}
else {
if (this.directionalLight2) {
this.scene.remove(this.directionalLight2);
this.directionalLight2 = null;
}
//关闭位置模拟
this.state.directionalLightHelper2 = false;
this.toggleDirectionalLightHelper2();
}
}
//更新定向光2
updateDirectionalLight2() {
if (this.state.directionalLight2) {
this.directionalLight2.color.set(this.state.directionalLightColor2);
this.directionalLight2.intensity = this.state.directionalLightIntensity2;
this.directionalLight2.position.set(this.state.directionalLightPosition2.x, this.state.directionalLightPosition2.y, this.state.directionalLightPosition2.z);
if (this.directionalLightHelper2) {
this.directionalLightHelper2.update();
}
}
}
toggleDirectionalLightHelper2() {
if (this.state.directionalLightHelper2) {
if (this.directionalLight2) { //开启定向光才能开启位置模拟
if (this.directionalLightHelper2) {
this.scene.remove(this.directionalLightHelper2);
this.directionalLightHelper2 = null;
}
this.directionalLightHelper2 = new THREE.DirectionalLightHelper(this.directionalLight2, this.size / 20, 0xffff00);
this.scene.add(this.directionalLightHelper2);
}
else {
this.state.directionalLightHelper2 = false;
}
}
else {
if (this.directionalLightHelper2) {
this.scene.remove(this.directionalLightHelper2);
this.directionalLightHelper2 = null;
}
}
}
togglePointLight() {
if (this.state.pointLight) {
if (this.pointLight) {
this.scene.remove(this.pointLight);
this.pointLight = null;
}
//PointLight( color : Integer, intensity : Float, distance : Number, decay : Float )
this.pointLight = new THREE.PointLight(this.state.pointLightColor, this.state.pointLightIntensity, this.state.pointLightDistance, this.state.pointLightDecay);
this.pointLight.position.set(this.state.pointLightPosition.x, this.state.pointLightPosition.y, this.state.pointLightPosition.z);
this.scene.add(this.pointLight);
}
else {
if (this.pointLight) {
this.scene.remove(this.pointLight);
this.pointLight = null;
}
//关闭位置模拟
this.state.pointLightHelper = false;
this.togglePointLightHelper();
}
}
//更新点光源
updatePointLight() {
if (this.state.pointLight) {
this.pointLight.color.set(this.state.pointLightColor);
this.pointLight.intensity = this.state.pointLightIntensity;
this.pointLight.decay = this.state.pointLightDecay;
this.pointLight.distance = this.state.pointLightDistance;
this.pointLight.position.set(this.state.pointLightPosition.x, this.state.pointLightPosition.y, this.state.pointLightPosition.z);
if (this.pointLightHelper) {
this.pointLightHelper.update();
}
}
}
togglePointLightHelper() {
if (this.state.pointLightHelper) {
if (this.pointLight) { //开启点光源才能开启位置模拟
if (this.pointLightHelper) {
this.scene.remove(this.pointLightHelper);
this.pointLightHelper = null;
}
this.pointLightHelper = new THREE.PointLightHelper(this.pointLight, this.size / 20, 0x2bff00);
this.scene.add(this.pointLightHelper);
}
else {
this.state.pointLightHelper = false;
}
}
else {
if (this.pointLightHelper) {
this.scene.remove(this.pointLightHelper);
this.pointLightHelper = null;
}
}
}
//webgl销毁
webglLoseContext() {
let objCanvasDom = this.eleRef.nativeElement.querySelector(`${this.domId} canvas`);
if (objCanvasDom) {
//手动销毁WebGL context对象(最多16个,超出的话最先加载的会被移除)
let gl = objCanvasDom.getContext('webgl2');
gl.getExtension('WEBGL_lose_context').loseContext();
//dom移除
objCanvasDom.remove();
}
}
}
ObjViewerComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: ObjViewerComponent, deps: [{ token: i0.ElementRef }, { token: i1.LocalService }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
ObjViewerComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.1.5", type: ObjViewerComponent, selector: "nc-obj-viewer", inputs: { ncObjInfo: "ncObjInfo", ncCameraPlacement: "ncCameraPlacement", ncBg: "ncBg", ncBgColor: "ncBgColor", ncBgUrl: "ncBgUrl", ncTips: "ncTips", ncControl: "ncControl", ncAlpha: "ncAlpha", ncAutoRotate: "ncAutoRotate", ncAutoRotateSpeed: "ncAutoRotateSpeed" }, usesOnChanges: true, ngImport: i0, template: "<div id=\"objViewer\">\r\n <div class=\"loading\" *ngIf=\"showLoading\">\r\n <div class=\"nc-loading\">\r\n <svg class=\"nc-loading-svg\" version=\"1.1\" viewBox=\"0 0 60 60\" xml:space=\"preserve\"\r\n xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\r\n <defs>\r\n <clipPath id=\"d-5\">\r\n <use width=\"100%\" height=\"100%\" overflow=\"visible\" xlink:href=\"#c\" />\r\n </clipPath>\r\n </defs>\r\n <g transform=\"translate(4.4 -.117)\">\r\n <defs>\r\n <path id=\"a\" d=\"m-5-1h62v62h-62z\" />\r\n </defs>\r\n <clipPath id=\"b\">\r\n <use width=\"100%\" height=\"100%\" overflow=\"visible\" xlink:href=\"#a\" />\r\n </clipPath>\r\n <g clip-path=\"url(#b)\">\r\n <defs>\r\n <path id=\"c\" d=\"m-5-1h62v62h-62z\" />\r\n </defs>\r\n <clipPath id=\"d\">\r\n <use width=\"100%\" height=\"100%\" overflow=\"visible\" xlink:href=\"#c\" />\r\n </clipPath>\r\n <g style=\"stroke:var(--ov_logo_border_color);\" stroke=\"#000\" stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\" stroke-miterlimit=\"10\">\r\n <path transform=\"translate(.039092 .02257)\"\r\n d=\"m38.361 22.877-12.4-7.3 9.2e-5 -14.177 24.8 14.2v28.8l-12.4-7.3226z\"\r\n clip-path=\"url(#d)\" fill=\"#4688b4\" />\r\n <path transform=\"rotate(120,26,30.082)\"\r\n d=\"m38.4 22.923-12.522-7.0887 0.14178-14.4 24.78 14.166v28.8l-12.522-7.0883z\"\r\n clip-path=\"url(#d-5)\" fill=\"#64c0ff\" />\r\n <path d=\"m26 30v14.4l12.4-7.3v-14.2z\" clip-path=\"url(#d)\" fill=\"#294e67\" />\r\n <path d=\"m13.6 37.1v-14.2l12.4-7.3v-14.177l-24.8 14.177v28.8z\" clip-path=\"url(#d)\"\r\n fill=\"#294e67\" />\r\n <path d=\"m26 15.6-12.4 7.3 12.4 7.1 12.4-7.1z\" clip-path=\"url(#d)\" fill=\"#64c0ff\" />\r\n <path d=\"m13.6 22.9 12.4 7.1v14.4l-12.4-7.3z\" clip-path=\"url(#d)\" fill=\"#4688b4\" />\r\n </g>\r\n </g>\r\n </g>\r\n </svg>\r\n <div class=\"ncTips\">\r\n <div>{{loadingText}}</div>\r\n <div>{{loadingText2}}</div>\r\n </div>\r\n </div>\r\n </div>\r\n <div id=\"tips\" *ngIf=\"ncTips\">\r\n <div class=\"tip-row\">\r\n <div class=\"row-img\">\r\n <img\r\n src=\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNS4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4KCjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICBpZD0iTGF5ZXJfMSIKICAgeD0iMHB4IgogICB5PSIwcHgiCiAgIHdpZHRoPSI2NHB4IgogICBoZWlnaHQ9IjY0cHgiCiAgIHZpZXdCb3g9IjAgMCA2NCA2NCIKICAgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgNjQgNjQiCiAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuNDguNCByOTkzOSIKICAgc29kaXBvZGk6ZG9jbmFtZT0iTW91c2VMZWZ0LnN2ZyI+PG1ldGFkYXRhCiAgIGlkPSJtZXRhZGF0YTIxIj48cmRmOlJERj48Y2M6V29yawogICAgICAgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlCiAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+PGRjOnRpdGxlIC8+PC9jYzpXb3JrPjwvcmRmOlJERj48L21ldGFkYXRhPjxkZWZzCiAgIGlkPSJkZWZzMTkiIC8+PHNvZGlwb2RpOm5hbWVkdmlldwogICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICBib3JkZXJvcGFjaXR5PSIxIgogICBvYmplY3R0b2xlcmFuY2U9IjEwIgogICBncmlkdG9sZXJhbmNlPSIxMCIKICAgZ3VpZGV0b2xlcmFuY2U9IjEwIgogICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMCIKICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIxMjgyIgogICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4ODYiCiAgIGlkPSJuYW1lZHZpZXcxNyIKICAgc2hvd2dyaWQ9ImZhbHNlIgogICBpbmtzY2FwZTp6b29tPSIzLjY4NzUiCiAgIGlua3NjYXBlOmN4PSItMTAuMzA1MDg1IgogICBpbmtzY2FwZTpjeT0iMzIiCiAgIGlua3NjYXBlOndpbmRvdy14PSIxMDQiCiAgIGlua3NjYXBlOndpbmRvdy15PSIxMzYiCiAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjAiCiAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImczIiAvPgo8ZwogICBpZD0iZzMiPgoJPGcKICAgaWQ9Imc1IgogICBzdHlsZT0iZmlsbDojNGQ0ZDRkO3N0cm9rZTojNGQ0ZDRkIj4KCQk8cGF0aAogICBmaWxsPSIjMDIwMjAyIgogICBkPSJNMzIuODU0LDIwLjAyOGMtMC4wMTUtMS4yNzQsMC41NzYtMi4yNjksMS4xNTEtMy4yMzZjMC43NTgtMS4yNzgsMS41NDMtMi42LDAuOTk3LTQuNDE3ICAgIGMtMC41NTUtMS42NjctMS42MDctMi40ODUtMi41MzktMy4yMTJjLTEuMjU0LTAuOTczLTIuMTU5LTEuNjc2LTEuNjg3LTQuMzQzbDAuMDQxLTAuMjI5bC0xLjM1LTAuMjM4TDI5LjQyNyw0LjU4ICAgIGMtMC42MTUsMy40ODQsMC44NzgsNC42NDMsMi4xOTUsNS42NjZjMC44NSwwLjY2MywxLjY1MywxLjI4OCwyLjA3MiwyLjU0MmMwLjM2NiwxLjIyNC0wLjE4LDIuMTQzLTAuODY5LDMuMzA2ICAgIGMtMC42NDEsMS4wODEtMS4zNjYsMi4zLTEuMzQ5LDMuOTMzYy02LjgzNywwLjAwNy0xMy44MjMsMC4yOTEtMTMuODIzLDI0LjU5YzAsMTAuOTUxLDguNzk5LDE1LjAzMiwxNC4zNDcsMTUuMDMyICAgIGM1LjU0NSwwLDE0LjM0Ny00LjA4MSwxNC4zNDctMTUuMDMyQzQ2LjM0NywyMC4zNDcsMzkuMjI4LDIwLjA0OCwzMi44NTQsMjAuMDI4eiBNMzIuNjg1LDIxLjM5OSAgICBjNS42NDMsMC4wMTIsMTAuOTI3LDAuMjM2LDEyLjA2MywxNi4xNTNjLTMuODYsMS44MDUtNy45MTYsMi43NjUtMTIuMDYzLDIuODZ2LTIuOTY2YzAuODQ1LTAuMTYyLDEuNDgxLTAuOTA5LDEuNDgxLTEuNzgydi04Ljk1NiAgICBjMC0wLjg3Ni0wLjYzNy0xLjYyMS0xLjQ4MS0xLjc4NFYyMS4zOTl6IE0zMS42NTgsMjYuMjU0aDAuNjg1YzAuMjQ5LDAsMC40NTMsMC4yMDUsMC40NTMsMC40NTR2OC45NTYgICAgYzAsMC4yNDktMC4yMDQsMC40NDktMC40NTMsMC40NDloLTAuNjg1Yy0wLjI0OSwwLTAuNDUyLTAuMi0wLjQ1Mi0wLjQ0OXYtOC45NTZDMzEuMjA2LDI2LjQ1OSwzMS40MDksMjYuMjU0LDMxLjY1OCwyNi4yNTR6ICAgICBNMzEuMzEzLDIxLjM5OXYzLjUyNWMtMC44NDUsMC4xNjMtMS40NzksMC45MS0xLjQ3OSwxLjc4NHY4Ljk1NmMwLDAuODczLDAuNjM0LDEuNjIsMS40NzksMS43ODJ2Mi45NjYgICAgYy02LjMxMS0wLjE0OC0xMC45MDUtMi4yNTItMTIuMDYzLTIuODM5QzIwLjM4MSwyMS42MzMsMjUuNjY4LDIxLjQxMSwzMS4zMTMsMjEuMzk5eiBNMzIsNTguMjc0ICAgIGMtNS4wMTYsMC0xMi45NzYtMy43MDctMTIuOTc2LTEzLjY1OGMwLTEuOTgzLDAuMDQ1LTMuODUzLDAuMTM0LTUuNTYzYzIuMDAzLDAuOTI5LDYuNjk2LDIuNzQsMTIuODY4LDIuNzQgICAgYzQuMzk0LDAsOC43MDQtMC45MzQsMTIuODEzLTIuNzcxYzAuMDg5LDEuNzE4LDAuMTM1LDMuNiwwLjEzNSw1LjU5NUM0NC45NzQsNTQuNTY3LDM3LjAxNiw1OC4yNzQsMzIsNTguMjc0eiIKICAgaWQ9InBhdGg3IgogICBzdHlsZT0iZmlsbDojNGQ0ZDRkO3N0cm9rZTojNGQ0ZDRkIiAvPgoJPC9nPgoJCgkKPHBhdGgKICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtzdHJva2U6I2ZmZmZmZjtzdHJva2Utd2lkdGg6MC4yNzExODY0NCIKICAgZD0ibSAzMi45MjIwMzQsMzguOTUwNzkzIGMgMCwtMS4xMzU4MjUgMC4xMTQ5MTksLTEuNDU4MDQ1IDAuNjc3OTY2LC0xLjkwMDkzOCBsIDAuNjc3OTY2LC0wLjUzMzI4OCAwLC01LjI4MDc4IGMgMCwtNS4yNzEyNDkgLTAuMDAxMiwtNS4yODIwMDMgLTAuNjc3OTY2LC01Ljk1ODc0NSAtMC41NTc0MzksLTAuNTU3NDM5IC0wLjY3Nzk2NiwtMC45NDA5MzUgLTAuNjc3OTY2LC0yLjE1NzE2NSBsIDAsLTEuNDc5MTk5IDEuNTM1NDg3LDAgYyA1LjY1NzU1NCwwIDguNTg0MzM4LDMuNzI4NTI5IDkuNzE4NTUsMTIuMzgwNzkxIDAuNDk4NDM0LDMuODAyMjY1IDAuNTAzNDgsMy41MjU3MzEgLTAuMDY3NiwzLjcwNDU3MiAtMC4yNjEwMTcsMC4wODE3NCAtMS41Mjg2OTQsMC41MTIwMTYgLTIuODE3MDU5LDAuOTU2MTY3IC0yLjAzNzIwNiwwLjcwMjMwNiAtNC4wNDgwMzIsMS4xMTMwNDQgLTcuNDg4MDI2LDEuNTI5NTI4IGwgLTAuODgxMzU2LDAuMTA2NzA3IDAsLTEuMzY3NjUgeiIKICAgaWQ9InBhdGgzNzY4IgogICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPjxwYXRoCiAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7c3Ryb2tlOiNmZmZmZmY7c3Ryb2tlLXdpZHRoOjAuMjcxMTg2NDQiCiAgIGQ9Im0gMzEuNDg1MDgxLDM1Ljg0MjE0MyBjIC0wLjEwNDU5MSwtMC4xMDQ1OTEgLTAuMTkwMTY2LC0yLjIzMTM0NyAtMC4xOTAxNjYsLTQuNzI2MTIzIDAsLTMuOTgzOTk0IDAuMDU1MDIsLTQuNTU3MDcxIDAuNDUyMTI3LC00LjcwOTQ1NiAwLjI0ODY3LC0wLjA5NTQyIDAuNTU2ODAyLC0wLjA2ODgyIDAuNjg0NzM4LDAuMDU5MTIgMC4xMjc5MzcsMC4xMjc5MzYgMC4xOTkwNTYsMi4yOTY0ODEgMC4xNTgwNDMsNC44MTg5ODggLTAuMDYzMTksMy44ODY0MDUgLTAuMTM4NjcsNC41OTg2ODMgLTAuNDk0NTcyLDQuNjY3MDA5IC0wLjIzMTAwMywwLjA0NDM1IC0wLjUwNTU3OSwtMC4wMDQ5IC0wLjYxMDE3LC0wLjEwOTUzMyBsIDAsMCB6IgogICBpZD0icGF0aDM3NzAiCiAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+PHBhdGgKICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtzdHJva2U6I2ZmZmZmZjtzdHJva2Utd2lkdGg6MC4yNzExODY0NCIKICAgZD0ibSAzMC40NDA0MzMsNTcuOTQ1MzE2IGMgLTMuMDY0MDUzLC0wLjQyMTEzIC02Ljc3NzIzLC0yLjQ3NDk1MyAtOC40NTc2MjEsLTQuNjc4MDYgLTIuMTU4OCwtMi44MzAzMzMgLTMuMTEzOTE4LC02LjczNzcxNiAtMi43Njk1OTIsLTExLjMzMDM4NSBsIDAuMTkzMTIxLC0yLjU3NTg3MiAyLjA3OTg4LDAuNzM1NjI4IGMgMy41MjcwMjYsMS4yNDc0NjYgNi4zMzc3MDEsMS43MTkwNjggMTAuMjE1NDc0LDEuNzE0MDUgNC4xNzE3NzEsLTAuMDA1NCA2LjY0Mjk4MSwtMC4zODg2OCAxMC4yNTgzNiwtMS41OTEwNTggMi4xNzg5NzMsLTAuNzI0NjY4IDIuNjkyMTkzLC0wLjgxNTg1NiAyLjgxMzQ0NCwtMC40OTk4ODMgMC4wODE1NywwLjIxMjU2IDAuMDg2NjQsMi4xMTk4NjQgMC4wMTEyNyw0LjIzODQ1MyAtMC4xMDYyMTcsMi45ODU4MSAtMC4yNjY1MzEsNC4yMTI5MzggLTAuNzEyOTM1LDUuNDU3MjA4IC0xLjQ1MDIxNyw0LjA0MjIwNiAtNC40MDExMTgsNi44NDQxNTYgLTguNDM3OTM4LDguMDEyMDE3IC0yLjMxNDc0NywwLjY2OTY2MSAtMy4zNDMxNCwwLjc3MjIxNSAtNS4xOTM0NjUsMC41MTc5MDIgeiIKICAgaWQ9InBhdGgzNzcyIgogICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPjwvZz4KPHBhdGgKICAgc3R5bGU9ImZpbGw6IzY2Q0NGRjtzdHJva2U6IzY2Q0NGRjtzdHJva2Utd2lkdGg6MC4yNzExODY0Mzk5OTk5OTk5NyIKICAgZD0ibSAyOC43NDA1NTYsNDAuMDYzNjgyIGMgLTIuNDU3Nzk1LC0wLjI4NDk5NSAtNS4yOTE1MjIsLTAuOTc2ODgxIC03LjQ3OTUzOSwtMS44MjYyMTIgLTAuOTY5NDkyLC0wLjM3NjMzMiAtMS43OTI5MzksLTAuNzA0OTYxIC0xLjgyOTg4NCwtMC43MzAyODcgLTAuMTUyNTY0LC0wLjEwNDU4NCAwLjc2NjAxNiwtNi4yNDI1NDEgMS4xNjgwMSwtNy44MDQ2NTYgMS40MjExOTEsLTUuNTIyNjMxIDQuMzA0NDc3LC04LjA2MTg0OSA5LjE1NDI0MSwtOC4wNjE4NDkgbCAxLjU0MTUzMSwwIDAsMS42MjcxMTkgYyAwLDEuMjM3MTI1IC0wLjA5MzgxLDEuNjI3MTE4IC0wLjM5MTQwMiwxLjYyNzExOCAtMC4yMTUyNzEsMCAtMC41ODEzNzMsMC4yNzEyMjEgLTAuODEzNTU5LDAuNjAyNzE0IC0wLjM0NTg4OSwwLjQ5MzgyNiAtMC40MjIxNTcsMS41Mjc1MjQgLTAuNDIyMTU3LDUuNzIxNzA2IGwgMCw1LjExODk5MiAwLjgxMzU1OSwwLjc1OTk3OCBjIDAuNjc1ODQ2LDAuNjMxMzM1IDAuODEzNTU5LDAuOTcxMDgzIDAuODEzNTU5LDIuMDA3MTA4IDAsMC44MzU5MjEgLTAuMTExNzcxLDEuMjM1Mzc4IC0wLjMzODk4MywxLjIxMTQ4OSAtMC4xODY0NCwtMC4wMTk2IC0xLjE4MzM2LC0wLjEzMzU1MSAtMi4yMTUzNzYsLTAuMjUzMjIgbCAwLDAgeiIKICAgaWQ9InBhdGgzNzY2IgogICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPjwvc3ZnPg==\">\r\n </div>\r\n <div class=\"row-word\">\u65CB\u8F6C</div>\r\n </div>\r\n <div class=\"tip-row\">\r\n <div class=\"row-img\">\r\n <img\r\n src=\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNS4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4KCjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICBpZD0iTGF5ZXJfMSIKICAgeD0iMHB4IgogICB5PSIwcHgiCiAgIHdpZHRoPSI2NHB4IgogICBoZWlnaHQ9IjY0cHgiCiAgIHZpZXdCb3g9IjAgMCA2NCA2NCIKICAgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgNjQgNjQiCiAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuNDguNCByOTkzOSIKICAgc29kaXBvZGk6ZG9jbmFtZT0iTW91c2VSaWdodC5zdmciPjxtZXRhZGF0YQogICBpZD0ibWV0YWRhdGEyMSI+PHJkZjpSREY+PGNjOldvcmsKICAgICAgIHJkZjphYm91dD0iIj48ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD48ZGM6dHlwZQogICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPjxkYzp0aXRsZSAvPjwvY2M6V29yaz48L3JkZjpSREY+PC9tZXRhZGF0YT48ZGVmcwogICBpZD0iZGVmczE5IiAvPjxzb2RpcG9kaTpuYW1lZHZpZXcKICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgYm9yZGVyb3BhY2l0eT0iMSIKICAgb2JqZWN0dG9sZXJhbmNlPSIxMCIKICAgZ3JpZHRvbGVyYW5jZT0iMTAiCiAgIGd1aWRldG9sZXJhbmNlPSIxMCIKICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAiCiAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTI4MiIKICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODg2IgogICBpZD0ibmFtZWR2aWV3MTciCiAgIHNob3dncmlkPSJmYWxzZSIKICAgaW5rc2NhcGU6em9vbT0iMy42ODc1IgogICBpbmtzY2FwZTpjeD0iMzIiCiAgIGlua3NjYXBlOmN5PSIzMiIKICAgaW5rc2NhcGU6d2luZG93LXg9IjMxIgogICBpbmtzY2FwZTp3aW5kb3cteT0iMTUiCiAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjAiCiAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImczIiAvPgo8ZwogICBpZD0iZzMiPgoJPGcKICAgaWQ9Imc1IgogICBzdHlsZT0ic3Ryb2tlOiM0ZDRkNGQ7ZmlsbDojNGQ0ZDRkIj4KCQk8cGF0aAogICBmaWxsPSIjMDIwMjAyIgogICBkPSJNMzIuODU0LDIwLjAyOGMtMC4wMTUtMS4yNzQsMC41NzYtMi4yNjksMS4xNTEtMy4yMzZjMC43NTgtMS4yNzgsMS41NDMtMi42LDAuOTk3LTQuNDE3ICAgIGMtMC41NTUtMS42NjctMS42MDctMi40ODUtMi41MzktMy4yMTJjLTEuMjU0LTAuOTczLTIuMTU5LTEuNjc2LTEuNjg3LTQuMzQzbDAuMDQxLTAuMjI5bC0xLjM1LTAuMjM4TDI5LjQyNyw0LjU4ICAgIGMtMC42MTUsMy40ODQsMC44NzgsNC42NDMsMi4xOTUsNS42NjZjMC44NSwwLjY2MywxLjY1MywxLjI4OCwyLjA3MiwyLjU0MmMwLjM2NiwxLjIyNC0wLjE4LDIuMTQzLTAuODY5LDMuMzA2ICAgIGMtMC42NDEsMS4wODEtMS4zNjYsMi4zLTEuMzQ5LDMuOTMzYy02LjgzNywwLjAwNy0xMy44MjMsMC4yOTEtMTMuODIzLDI0LjU5YzAsMTAuOTUxLDguNzk5LDE1LjAzMiwxNC4zNDcsMTUuMDMyICAgIGM1LjU0NSwwLDE0LjM0Ny00LjA4MSwxNC4zNDctMTUuMDMyQzQ2LjM0NywyMC4zNDcsMzkuMjI4LDIwLjA0OCwzMi44NTQsMjAuMDI4eiBNMzIuNjg1LDIxLjM5OSAgICBjNS42NDMsMC4wMTIsMTAuOTI3LDAuMjM2LDEyLjA2MywxNi4xNTNjLTMuODYsMS44MDUtNy45MTYsMi43NjUtMTIuMDYzLDIuODZ2LTIuOTY2YzAuODQ1LTAuMTYyLDEuNDgxLTAuOTA5LDEuNDgxLTEuNzgydi04Ljk1NiAgICBjMC0wLjg3Ni0wLjYzNy0xLjYyMS0xLjQ4MS0xLjc4NFYyMS4zOTl6IE0zMS42NTgsMjYuMjU0aDAuNjg1YzAuMjQ5LDAsMC40NTMsMC4yMDUsMC40NTMsMC40NTR2OC45NTYgICAgYzAsMC4yNDktMC4yMDQsMC40NDktMC40NTMsMC40NDloLTAuNjg1Yy0wLjI0OSwwLTAuNDUyLTAuMi0wLjQ1Mi0wLjQ0OXYtOC45NTZDMzEuMjA2LDI2LjQ1OSwzMS40MDksMjYuMjU0LDMxLjY1OCwyNi4yNTR6ICAgICBNMzEuMzEzLDIxLjM5OXYzLjUyNWMtMC44NDUsMC4xNjMtMS40NzksMC45MS0xLjQ3OSwxLjc4NHY4Ljk1NmMwLDAuODczLDAuNjM0LDEuNjIsMS40NzksMS43ODJ2Mi45NjYgICAgYy02LjMxMS0wLjE0OC0xMC45MDUtMi4yNTItMTIuMDYzLTIuODM5QzIwLjM4MSwyMS42MzMsMjUuNjY4LDIxLjQxMSwzMS4zMTMsMjEuMzk5eiBNMzIsNTguMjc0ICAgIGMtNS4wMTYsMC0xMi45NzYtMy43MDctMTIuOTc2LTEzLjY1OGMwLTEuOTgzLDAuMDQ1LTMuODUzLDAuMTM0LTUuNTYzYzIuMDAzLDAuOTI5LDYuNjk2LDIuNzQsMTIuODY4LDIuNzQgICAgYzQuMzk0LDAsOC43MDQtMC45MzQsMTIuODEzLTIuNzcxYzAuMDg5LDEuNzE4LDAuMTM1LDMuNiwwLjEzNSw1LjU5NUM0NC45NzQsNTQuNTY3LDM3LjAxNiw1OC4yNzQsMzIsNTguMjc0eiIKICAgaWQ9InBhdGg3IgogICBzdHlsZT0ic3Ryb2tlOiM0ZDRkNGQ7ZmlsbDojNGQ0ZDRkIiAvPgoJPC9nPgoJCgkKPHBhdGgKICAgc3R5bGU9ImZpbGw6IzY1RkQwMDtzdHJva2U6IzY1RkQwMDtzdHJva2Utd2lkdGg6MC4yNzExODY0Mzk5OTk5OTk5NyIKICAgZD0ibSAzMi45MjIwMzQsMzguOTUwNzkzIGMgMCwtMS4xMzU4MjUgMC4xMTQ5MTksLTEuNDU4MDQ1IDAuNjc3OTY2LC0xLjkwMDkzOCBsIDAuNjc3OTY2LC0wLjUzMzI4OCAwLC01LjI4MDc4IGMgMCwtNS4yNzEyNDkgLTAuMDAxMiwtNS4yODIwMDMgLTAuNjc3OTY2LC01Ljk1ODc0NSAtMC41NTc0MzksLTAuNTU3NDM5IC0wLjY3Nzk2NiwtMC45NDA5MzUgLTAuNjc3OTY2LC0yLjE1NzE2NSBsIDAsLTEuNDc5MTk5IDEuNTM1NDg3LDAgYyA1LjY1NzU1NCwwIDguNTg0MzM4LDMuNzI4NTI5IDkuNzE4NTUsMTIuMzgwNzkxIDAuNDk4NDM0LDMuODAyMjY1IDAuNTAzNDgsMy41MjU3MzEgLTAuMDY3NiwzLjcwNDU3MiAtMC4yNjEwMTcsMC4wODE3NCAtMS41Mjg2OTQsMC41MTIwMTYgLTIuODE3MDU