원리
눈에는 보이지않는 광선을 발사
광선이 충돌한 객체들을 특정배열에 부딪힌 순서대로 담음
제일 처음 부딪힌 객체의 이름을 가지고 이벤트 발생시킴
class App {
constructor() {
this._setupThreeJs()
this._setupCamera();
this._setupLight();
this._setupModel();
this._setupControls();
this._setupEvents();
this._pointer = new THREE.Vector2();
this._raycaster = new THREE.Raycaster();
//카메라이동 조작부관련
this._moveLeft = false;
this._moveRight = false;
this._moveFront = false;
this._moveBack = false;
this._rotateLeft = false;
this._rotateRight = false;
// 카메라 회전 각도 설정
this._rotationSpeed = 0.01;
// 직진 이동 거리 설정
this._moveSpeed = 0.08;
this._animate = this._animate.bind(this); // 컨텍스트 바인딩
this._setupInteractionEvents(); // 인터랙션 이벤트 설정 호출
// 이벤트 디스패처 생성
this.eventDispatcher = new THREE.EventDispatcher();
// 오브젝트 이벤트 처리 메서드 호출
this.currentObject = null;
this.handleObjectEvents();
// 오브젝트 색상 초기화
this.originalColor = new THREE.Color();
// 애니메이션 시작
this._animate();
}
_setupInteractionEvents() {
// 마우스 클릭 이벤트 리스너 등록
window.addEventListener('click', this.onClick.bind(this), false);
// 이벤트 리스너 설정
window.addEventListener('pointermove', this._onPointerMove.bind(this));
}
_onPointerMove(event) {
const rect = this._divContainer.getBoundingClientRect();
this._pointer.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
this._pointer.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
}
_setupThreeJs(){
const divContainer = document.querySelector("#webgl-container");
this._divContainer = divContainer;
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
divContainer.appendChild(renderer.domElement);
this._renderer = renderer;
const scene = new THREE.Scene();
this._scene = scene;
}
_setupLight() {
const light = new THREE.PointLight(0xffffff, 30);
let lightzposition = 3
light.position.set(0, 4, lightzposition); // 빛의 위치 설정
light.castShadow = true; // 그림자 생성 설정
this._scene.add(light);
light.shadow.mapSize.width = 4;
light.shadow.mapSize.height = 3;
const d = 3;
light.shadow.camera.left = - d;
light.shadow.camera.right = d;
light.shadow.camera.top = d;
light.shadow.camera.bottom = - d;
light.shadow.camera.far = 3;
light.shadow.bias = - 0.0001;
}
async _setupModel() {
fetch('/api/url')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
// 데이터 처리
this._data = data.jsonInfl
data.jsonInfl.forEach(item => {
switch (item.flagCd) {
case 'M202A':
this._m202a_flux = item.flux;
this._m202a_pumpEfcny = item.pumpEfcny;
this._m202a_pumpPwrerVol = item.pumpPwrerVol;
this._m202a_pumpStusVal = item.pumpStusVal;
break;
default:
break;
}
});
//수위
this._waterLevelA=data.jsonWaterLevel.levelA
this._waterLevelB=data.jsonWaterLevel.levelB
this._setupModelAfterDataLoaded()
})
.catch(error => {
console.error('There was a problem with the fetch operation:', error);
});
}
async _setupModelAfterDataLoaded(){
// // water material(물재질 현재 사용안함)
// const waterGeometry = new THREE.PlaneGeometry( 20, 20 );
// this._water = new Water( waterGeometry,{
// color:'#ffffff',
// scale: 4,
// flowDirection: new THREE.Vector2(1,1),
// textureWidth:1024,
// textureHeight: 1024
// })
// this._water.position.y = -2.5;
// this._water.rotation.x = Math.PI * - 0.5;
// this._water.scale.set(2,1)
// this._scene.add( this._water );
//water material
const waterGeometry = new THREE.PlaneGeometry( 20, 20 );
const material = new THREE.MeshBasicMaterial( {color: '#513408', side: THREE.DoubleSide, transparent:true, opacity:0.2} );
this._water = new THREE.Mesh( waterGeometry, material );
this._water.position.y = -2.5;
this._water.rotation.x = Math.PI * - 0.5;
this._water.scale.set(2,1)
this._scene.add( this._water );
//inflpump
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
loader.load( '../js/egovframework/SEWER/dt/Background1.glb', ( gltf ) => {
// GLTF 파일이 로드되었을 때 실행되는 콜백 함수
const model = gltf.scene;
this._scene.add(model);
}, undefined, ( error ) => {
// 오류가 발생했을 때 실행되는 콜백 함수
console.error( error );
});
dracoLoader.setDecoderPath( '../js/egovframework/com/lib/threejs/examples/jsm/libs/draco/' );
loader.setDRACOLoader( dracoLoader );
let onoffarr = [this._m202a_pumpStusVal]
let temp = "../js/egovframework/SEWER/dt/"
const modelPaths = ['1_M-203B.glb'];
for (const [index, path] of modelPaths.entries()) {
const model = await this._loadModel(loader, temp+path);
this._models.push(model);
//this._modelColorOptions[index] = onoffarr[index]
//this._applyColorFilter(model, this._modelColorOptions[index]);
this._scene.add(model);
}
//dashboard1
dracoLoader.setDecoderPath( '../js/egovframework/com/lib/threejs/examples/jsm/libs/draco/' );
loader.setDRACOLoader( dracoLoader );
loader.load( '../js/egovframework/SEWER/dt/Dashboard-1.glb', ( gltf ) => {
// GLTF 파일이 로드되었을 때 실행되는 콜백 함수
const model = gltf.scene;
this._scene.add(model);
}, undefined, ( error ) => {
// 오류가 발생했을 때 실행되는 콜백 함수
console.error( error );
});
//backpipe
dracoLoader.setDecoderPath( '../js/egovframework/com/lib/threejs/examples/jsm/libs/draco/' );
loader.setDRACOLoader( dracoLoader );
loader.load( '../js/egovframework/SEWER/dt/inflbackpipe.glb', ( gltf ) => {
// GLTF 파일이 로드되었을 때 실행되는 콜백 함수
const model = gltf.scene;
this._scene.add(model);
}, undefined, ( error ) => {
// 오류가 발생했을 때 실행되는 콜백 함수
console.error( error );
});
let gauz = 2.6
this._gaugeChart1 = new GaugeChart(this._scene,loader, -3.6, 0 ,gauz);
this._gaugeChart1.createGauge('M-203B',this._m203b_flux,'init'); // 최초 텍스트 생성
//수위 데이터
this._waterlevel1 = new Waterlevel(this._scene,loader,-1.5, -1.27 ,1.68)
this._waterlevel1.createWaterlevel( this._waterLevelA,'init');
this._waterlevel2 = new Waterlevel(this._scene,loader,1.5, -1.27 ,1.68)
this._waterlevel2.createWaterlevel( this._waterLevelB,'init');
//유입펌프팝업정보 데이터
this._popupinfo1 = new PopupInfo(this._scene,loader, -3.42, 0.85 ,2,'M203B')
this._popupinfo1.createPopupInfo(this._data.filter(data => data.flagCd=='M203B')[0],'init')
}
_loadModel(loader, path) {
return new Promise((resolve, reject) => {
loader.load(path, (gltf) => {
const model = gltf.scene;
resolve(model);
}, undefined, (error) => {
reject(error);
});
});
}
hidePopup() {
const duration = 500; // 애니메이션 지속 시간(ms)
const startOpacity = 1; // 시작 투명도
const endOpacity = 0; // 종료 투명도
const startTime = performance.now(); // 애니메이션 시작 시간
const animate = (timestamp) => {
const progress = timestamp - startTime; // 진행 시간 계산
const opacity = startOpacity - (progress / duration) * (startOpacity - endOpacity); // 현재 투명도 계산
// 팝업의 투명도 설정
this._popupinfo.setOpacity(opacity);
if (progress < duration) {
// 애니메이션 진행 중이면 다음 프레임 요청
requestAnimationFrame(animate);
} else {
// 애니메이션 종료 후 팝업 숨기기
this._popupinfo.hide();
}
};
// 첫 번째 프레임 요청
requestAnimationFrame(animate);
}
onClick = async (event) => {
const rect = this._divContainer.getBoundingClientRect();
// 마우스의 위치를 정규화된 장치 좌표로 변환
this._pointer.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
this._pointer.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
// 카메라와 포인터 위치를 기반으로 광선을 생성
this._raycaster.setFromCamera(this._pointer, this._camera);
// 광선이 충돌한 객체들을 계산
const intersects = this._raycaster.intersectObjects(this._scene.children);
// 첫 번째로 충돌한 객체가 조건을 만족하는지 확인하고 액션 실행
if (intersects.length > 0) {
const intersectedObject = intersects[0].object;
console.log(intersects)
const parentName = intersectedObject.parent ? intersectedObject.parent.name : null;
// 충돌한 객체의 부모 이름을 기반으로 팝업을 표시
switch (parentName) {
case '1_M-203B':
case 'M203B':
this.togglePopup(this._popupinfo1);
break;
default:
break;
}
// 추가로 객체 이름 기반 액션
if (intersectedObject.name === '설계정보' && intersectedObject.material.opacity > 1 && intersectedObject.parent.name == 'M203B') {
clickId = 'm203b_info';
const fileUrl = await selectFileUrl(clickId);
console.log(fileUrl);
if (fileUrl) {
document.getElementById('pdfViewer').src = fileUrl;
document.getElementById('overlay').classList.add('visible');
document.getElementById('popup').classList.add('visible');
$('#popup_title').text('M203B 설계정보');
document.getElementById('closePopup').addEventListener('click', () => {
document.getElementById('overlay').classList.remove('visible');
document.getElementById('popup').classList.remove('visible');
btnExit();
alertCancel();
});
document.getElementById('overlay').addEventListener('click', () => {
document.getElementById('overlay').classList.remove('visible');
document.getElementById('popup').classList.remove('visible');
btnExit();
alertCancel();
});
}
}
if (intersectedObject.name === '유지관리메뉴얼' && intersectedObject.material.opacity > 1 && intersectedObject.parent.name == 'M203B') {
clickId = 'm203b_manual';
const fileUrl = await selectFileUrl(clickId);
document.getElementById('pdfViewer').src = fileUrl;
document.getElementById('overlay').classList.add('visible');
document.getElementById('popup').classList.add('visible');
$('#popup_title').text('M203B 유지관리메뉴얼');
document.getElementById('closePopup').addEventListener('click', () => {
document.getElementById('overlay').classList.remove('visible');
document.getElementById('popup').classList.remove('visible');
btnExit();
alertCancel();
});
document.getElementById('overlay').addEventListener('click', () => {
document.getElementById('overlay').classList.remove('visible');
document.getElementById('popup').classList.remove('visible');
btnExit();
alertCancel();
});
}
}
}
}
togglePopup(popup) {
if (!popup.isVisible) {
popup.isVisible = true;
popup.show();
} else {
popup.isVisible = false;
popup.hide();
}
}
class PopupInfo {
constructor(scene,loader, x, y, z,name) {
this.name = name;
this.scene = scene;
this.loader = loader;
this.textMesh = undefined;
this.textMesh1 = undefined;
// 초기 위치값 설정
this.xPosition = x;
this.yPosition = y;
this.zPosition = z;
this.group = new THREE.Group(); //고정모델
this.group.position.set(this.xPosition,this.yPosition, this.zPosition);
this.group.scale.set(0.2,0.2,0.2)
this.group.name=name
this.scene.add(this.group);
this.datagroup = new THREE.Group(); //변동모델
this.datagroup.position.set(this.xPosition, this.yPosition, this.zPosition);
this.datagroup.scale.set(0.2,0.2,0.2)
this.scene.add(this.datagroup);
this.data;
this.pumpEfcny;
this.pumpPwrerVol;
this.pumpStusVal;
this.opacity = 0; // 초기 투명도 설정
this.group.visible = false; // 처음에는 팝업을 보이지 않도록 설정
this.datagroup.visible = false; // 처음에는 팝업을 보이지 않도록 설정
}
createPopupInfo(data,status) {
this.pumpEfcny = data.pumpEfcny;
this.pumpPwrerVol = data.pumpPwrerVol;
this.pumpStusVal = data.pumpStusVal==1 ? 'ON':'OFF';
const fontloader = new FontLoader();
// 팝업 배경
if(status == 'init'){
// 프레임
const sframebackgeo = RoundedRectangle( 5,3.7, 1.2, 10 )
const sframebackmat = new THREE.MeshBasicMaterial({ transparent: true, opacity: 0.5,color:'#2a302e'
});
const sframeback = new THREE.Mesh(sframebackgeo, sframebackmat);
sframeback.userData.initialOpacity = 0.6; // 사용자 정의 속성에 초기 투명도 저장
sframeback.position.set(this.xPosition - 1, this.yPosition-2, this.zPosition + 4);
sframeback.scale.set(1.7,1.5,1.7)
this.group.add(sframeback);
const textureLoader = new THREE.TextureLoader();
const texture1 = textureLoader.load('../js/egovframework/SEWER/dt/popicon1.png');
const material1 = new THREE.MeshBasicMaterial({
map: texture1,
depthWrite: false,
depthTest: false
});
const geo1 = new THREE.CircleGeometry(5, 32);
const circle1 = new THREE.Mesh(geo1, material1);
circle1.userData.initialOpacity = 1; // 사용자 정의 속성에 초기 투명도 저장
circle1.position.set(this.xPosition - 3.3, this.yPosition-0.5, this.zPosition + 4.01);
circle1.scale.set(0.17, 0.17, 0.17);
circle1.renderOrder = 2;
this.group.add(circle1);
const texture2 = textureLoader.load('../js/egovframework/SEWER/dt/popicon2.png');
const material2 = new THREE.MeshBasicMaterial({
map: texture2,
depthWrite: false,
depthTest: false
});
const geo2 = new THREE.CircleGeometry(5, 32);
const circle2 = new THREE.Mesh(geo2, material2);
circle2.userData.initialOpacity = 1; // 사용자 정의 속성에 초기 투명도 저장
circle2.position.set(this.xPosition - 1, this.yPosition-0.5, this.zPosition + 4.01);
circle2.scale.set(0.17, 0.17, 0.17);
circle2.renderOrder = 2;
this.group.add(circle2);
const texture3 = textureLoader.load('../js/egovframework/SEWER/dt/popicon3.png');
const material3 = new THREE.MeshBasicMaterial({
map: texture3,
depthWrite: false,
depthTest: false
});
const geo3 = new THREE.CircleGeometry(5, 32);
const circle3 = new THREE.Mesh(geo3, material3);
circle3.userData.initialOpacity = 1; // 사용자 정의 속성에 초기 투명도 저장
circle3.position.set(this.xPosition +1.3, this.yPosition-0.5, this.zPosition + 4.01);
circle3.scale.set(0.17, 0.17, 0.17);
circle3.renderOrder = 2;
this.group.add(circle3);
const texture4 = textureLoader.load('../js/egovframework/SEWER/dt/popicon4.png');
const material4 = new THREE.MeshBasicMaterial({
map: texture4,
depthWrite: false,
depthTest: false
});
const geo4 = new THREE.CircleGeometry(5, 32);
const circle4 = new THREE.Mesh(geo4, material4);
circle4.userData.initialOpacity = 1; // 사용자 정의 속성에 초기 투명도 저장
circle4.position.set(this.xPosition - 2.3, this.yPosition-3.2, this.zPosition + 4.01);
circle4.scale.set(0.17, 0.17, 0.17);
circle4.name = '설계정보'
circle4.renderOrder = 2;
this.group.add(circle4);
const texture5 = textureLoader.load('../js/egovframework/SEWER/dt/popicon5.png');
const material5 = new THREE.MeshBasicMaterial({
map: texture5,
depthWrite: false,
depthTest: false
});
const geo5 = new THREE.CircleGeometry(5, 32);
const circle5 = new THREE.Mesh(geo5, material5);
circle5.userData.initialOpacity = 1; // 사용자 정의 속성에 초기 투명도 저장
circle5.position.set(this.xPosition +0.4 , this.yPosition-3.2, this.zPosition + 4.01);
circle5.scale.set(0.17, 0.17, 0.17);
circle5.name = '유지관리메뉴얼'
circle5.renderOrder = 2;
this.group.add(circle5);
fontloader.load('../js/egovframework/com/lib/threejs/examples/fonts/Noto Sans SemiBold_Regular.json', function (font) {
const color1 = new THREE.Color('#fff');
const matLite1 = new THREE.MeshBasicMaterial({
color: color1,
transparent: true,
opacity: 1,
side: THREE.DoubleSide
});
let message1 = '펌프효율';
const shapes1 = font.generateShapes(message1, 0.25); // Font size
const geometry1 = new THREE.ShapeGeometry(shapes1);
geometry1.computeBoundingBox();
const xMid1 = -0.78 * (geometry1.boundingBox.max.x - geometry1.boundingBox.min.x);
geometry1.translate(xMid1, 0, 0);
// 텍스트 메쉬 생성
const textMesh1 = new THREE.Mesh(geometry1, matLite1);
textMesh1.position.set(this.xPosition-3, this.yPosition-1.6, this.zPosition +4.01); // Adjust position
textMesh1.scale.set(0.9, 1, 1);
textMesh1.userData.initialOpacity = 1;
this.group.add(textMesh1); // 텍스트 그룹에 추가
let message2 = '전력';
const shapes2 = font.generateShapes(message2, 0.25); // Font size
const geometry2 = new THREE.ShapeGeometry(shapes2);
geometry2.computeBoundingBox();
const xMid2 = -0.78 * (geometry2.boundingBox.max.x - geometry2.boundingBox.min.x);
geometry2.translate(xMid2, 0, 0);
// 텍스트 메쉬 생성
const textMesh2 = new THREE.Mesh(geometry2, matLite1);
textMesh2.position.set(this.xPosition-1, this.yPosition-1.6, this.zPosition +4.01); // Adjust position
textMesh2.scale.set(0.9, 1, 1);
textMesh2.userData.initialOpacity = 1;
this.group.add(textMesh2); // 텍스트 그룹에 추가
let message3 = '가동상태';
const shapes3 = font.generateShapes(message3, 0.25); // Font size
const geometry3 = new THREE.ShapeGeometry(shapes3);
geometry3.computeBoundingBox();
const xMid3 = -0.78 * (geometry3.boundingBox.max.x - geometry3.boundingBox.min.x);
geometry3.translate(xMid3, 0, 0);
// 텍스트 메쉬 생성
const textMesh3 = new THREE.Mesh(geometry3, matLite1);
textMesh3.position.set(this.xPosition + 1.5, this.yPosition-1.6, this.zPosition +4.01); // Adjust position
textMesh3.scale.set(0.9, 1, 1);
textMesh3.userData.initialOpacity = 1;
this.group.add(textMesh3); // 텍스트 그룹에 추가
let message4 = '설계정보';
const shapes4 = font.generateShapes(message4, 0.25); // Font size
const geometry4 = new THREE.ShapeGeometry(shapes4);
geometry4.computeBoundingBox();
const xMid4 = -0.78 * (geometry4.boundingBox.max.x - geometry4.boundingBox.min.x);
geometry4.translate(xMid3, 0, 0);
// 텍스트 메쉬 생성
const textMesh4 = new THREE.Mesh(geometry4, matLite1);
textMesh4.position.set(this.xPosition - 2.0, this.yPosition-4.25, this.zPosition +4.01); // Adjust position
textMesh4.scale.set(0.9, 1, 1);
textMesh4.userData.initialOpacity = 1;
this.group.add(textMesh4); // 텍스트 그룹에 추가
let message5 = '유지관리메뉴얼';
const shapes5 = font.generateShapes(message5, 0.25); // Font size
const geometry5 = new THREE.ShapeGeometry(shapes5);
geometry5.computeBoundingBox();
const xMid5 = -0.78 * (geometry5.boundingBox.max.x - geometry5.boundingBox.min.x);
geometry5.translate(xMid5, 0, 0);
// 텍스트 메쉬 생성
const textMesh5 = new THREE.Mesh(geometry5, matLite1);
textMesh5.position.set(this.xPosition + 1, this.yPosition-4.25, this.zPosition +4.01); // Adjust position
textMesh5.scale.set(0.9, 1, 1);
textMesh5.userData.initialOpacity = 1;
this.group.add(textMesh5); // 텍스트 그룹에 추가
}.bind(this));
}
// 텍스트 요소를 담을 새로운 그룹 생성
const textGroup1 = new THREE.Group();
// 값
if(status == 'init'|| status== 'update'){
fontloader.load('../js/egovframework/com/lib/threejs/examples/fonts/Noto Sans KR_Bold.json', function (font) {
const color = new THREE.Color('#ffa800');
const matLite = new THREE.MeshBasicMaterial({
color: color,
transparent: true,
opacity: 1,
side: THREE.DoubleSide
});
const shapes1 = font.generateShapes(String(this.pumpEfcny)+'%', 0.28); // Font size
const geometry1 = new THREE.ShapeGeometry(shapes1);
geometry1.computeBoundingBox();
geometry1.translate(0, -0.5, 0);
const shapes2 = font.generateShapes(String(this.pumpPwrerVol)+'V', 0.28); // Font size
const geometry2 = new THREE.ShapeGeometry(shapes2);
geometry2.computeBoundingBox();
geometry2.translate( 0, -0.5, 0);
const shapes3 = font.generateShapes(String(this.pumpStusVal), 0.28); // Font size
const geometry3 = new THREE.ShapeGeometry(shapes3);
geometry3.computeBoundingBox();
geometry3.translate(0, -0.5, 0);
// 텍스트 메쉬 생성
const textMesh1 = new THREE.Mesh(geometry1, matLite);
textMesh1.position.set(this.xPosition-3.7, this.yPosition - 1.6, this.zPosition +4.01); // Adjust position
textGroup1.add(textMesh1); // 텍스트 그룹에 추가
const textMesh2 = new THREE.Mesh(geometry2, matLite);
textMesh2.position.set(this.xPosition-1.4, this.yPosition - 1.6, this.zPosition +4.01); // Adjust position
textGroup1.add(textMesh2); // 텍스트 그룹에 추가
const textMesh3 = new THREE.Mesh(geometry3, matLite);
textMesh3.position.set(this.xPosition+0.9, this.yPosition - 1.6, this.zPosition +4.01); // Adjust position
textGroup1.add(textMesh3); // 텍스트 그룹에 추가
}.bind(this));
}
// 큰 그룹에 텍스트 그룹 추가
this.datagroup.add(textGroup1);
}
updateData(data){
this.data = data;
if (this.datagroup) { // 기존 메시가 있으면 씬에서 제거
this.scene.remove(this.datagroup);
}
this.datagroup = new THREE.Group(); //변동모델
this.datagroup.position.set(this.xPosition, this.yPosition, this.zPosition);
this.datagroup.scale.set(0.2,0.2,0.2)
this.scene.add(this.datagroup);
this.createPopupInfo(this.data,'update')
if(!this.group.visible){
this.datagroup.visible=false
}
}
setOpacity(opacity) {
// 팝업의 투명도를 설정하는 메서드 구현
this.group.traverse((child) => {
if (child.isMesh) {
// Material의 투명도를 설정합니다.
child.material.transparent = true; // 투명하게 설정
const initialOpacity = child.userData.initialOpacity; // 사용자 정의 속성에서 초기 투명도 값 가져오기
// Material의 투명도를 설정합니다. 초기 투명도에 현재 opacity를 곱합니다.
child.material.opacity = initialOpacity * opacity;
}
});
// datagroup의 모든 하위 요소에 대해 투명도 설정
this.datagroup.traverse((child) => {
if (child.isMesh) {
// Material의 투명도를 설정합니다.
child.material.transparent = true; // 투명하게 설정
child.material.opacity = opacity; // 투명도 설정
}
});
}
toggle() {
// 팝업의 가시성을 토글하는 메서드 구현
if (this.opacity === 0) {
// 팝업이 숨겨진 상태면 나타내는 애니메이션 실행
if (!this.animating) { // 애니메이션이 실행 중이 아닌 경우에만 실행
this.show();
}
} else if (this.opacity === 1) {
// 팝업이 보이는 상태면 숨김 애니메이션 실행
if (!this.animating) { // 애니메이션이 실행 중이 아닌 경우에만 실행
this.hide();
}
}
}
show() {
this.opacity = 0;
this.animating = true; // 애니메이션 실행 중 플래그 설정
// 팝업을 보이게 하고 투명도를 0으로 초기화
this.group.visible = true;
this.datagroup.visible = true;
this.setOpacity(0); // 초기 투명도 설정
const duration = 500; // 애니메이션 지속 시간(ms)
const startOpacity = 0; // 시작 투명도
const endOpacity = 1; // 종료 투명도
const startTime = performance.now(); // 애니메이션 시작 시간
const animate = (timestamp) => {
const progress = timestamp - startTime; // 진행 시간 계산
this.opacity = startOpacity + (progress / duration) * (endOpacity - startOpacity); // 현재 투명도 계산
// 팝업의 투명도 설정
this.setOpacity(this.opacity);
if (progress < duration) {
// 애니메이션 진행 중이면 다음 프레임 요청
requestAnimationFrame(animate);
} else {
// 애니메이션 종료 시 팝업을 보이도록 설정
this.opacity = endOpacity; // 투명도 보정
this.animating = false; // 애니메이션 종료 후 플래그 설정
}
};
// 첫 번째 프레임 요청
requestAnimationFrame(animate);
}
hide() {
this.animating = true; // 애니메이션 실행 중 플래그 설정
const duration = 500; // 애니메이션 지속 시간(ms)
const startOpacity = this.opacity; // 시작 투명도
const endOpacity = 0; // 종료 투명도
const startTime = performance.now(); // 애니메이션 시작 시간
const animate = (timestamp) => {
const progress = timestamp - startTime; // 진행 시간 계산
this.opacity = startOpacity + (progress / duration) * (endOpacity - startOpacity); // 현재 투명도 계산
// 팝업의 투명도 설정
this.setOpacity(this.opacity);
if (progress < duration) {
// 애니메이션 진행 중이면 다음 프레임 요청
requestAnimationFrame(animate);
} else {
// 애니메이션 종료 시 팝업을 숨기도록 설정
this.opacity = endOpacity; // 투명도 보정
this.group.visible = false;
this.datagroup.visible = false;
this.animating = false; // 애니메이션 종료 후 플래그 설정
}
};
// 첫 번째 프레임 요청
requestAnimationFrame(animate);
}
}
반응형
'웹 개발 > 🧊 ThreeJS' 카테고리의 다른 글
ThreeJS | FPS drop 방지를 위한 성능최적화 (0) | 2024.06.04 |
---|---|
Three.js | Stats 라이브러리 추가하기 (0) | 2024.05.19 |
Three.js | Geometry - PlaneGeometry (0) | 2024.05.11 |
Three.js | Geometry - RingGeometry (0) | 2024.05.11 |
Three.js | Geometry - SphereGeometry (0) | 2024.05.11 |