WebGL로 렌더링된 3D 컴퓨터실은 이제 새로운 것이 아닙니다. 이 글의 주된 목적은 프로젝트에서 우연히 사용되었던 3D 컴퓨터실의 눈과 중심의 문제를 설명하는 것이었습니다. 이 예가 내 요구 사항에 가장 적합하다고 생각하므로 기록으로 사용합니다.
렌더링이 3D 컴퓨터실의 데모는 꽤 훌륭하고, 예쁘고, 기본적인 상호작용도 만족스럽습니다. 구현 방법을 살펴보겠습니다.
코드 생성 클래스 정의먼저 index.html에서 호출된 js 경로에서 해당 js를 하나씩 엽니다. Editor.Server 클래스는 server.js에서 사용자 정의되었으며 HT로 캡슐화된 ht.Default.def 함수에 의해 생성됩니다(생성된 클래스는 참고). 이름은 Editor입니다. .Server 앞의 Editor는 E)로 대체될 수 없습니다.
ht.Default.def('Editor.Server', Object, {//첫 번째 매개변수는 클래스 이름입니다. 문자열인 경우 HT의 classMap에 자동으로 등록되며 두 번째 매개변수는 상속할 상위 클래스입니다. ; 세 번째 매개변수는 메소드 및 변수 선언입니다. addToDataModel: function(dm) { //데이터 컨테이너에 노드를 추가합니다. dm.add(this._node) // ht에 사전 정의된 함수입니다. 추가를 통한 노드 데이터 컨테이너에 추가된 메소드}, setHost: function() { //흡착 설정 this._node.setHost.apply(this._node, 인수) }, s3: function() {//노드 크기 설정; this._node .s3.apply(this._node, 인수) }, setElevation: function() {//Node 프리미티브의 중심 위치가 위치한 3D 좌표계의 y축 위치를 제어합니다. this._node.setElevation.apply(this._node, 인수) }});
Editor.Server 클래스 생성
이 클래스는 ht.Node 노드를 생성하고 노드의 색상과 전면 텍스처를 설정할 수 있습니다.
var S = E.Server = function(obj) {//서버 구성 요소 var color = obj.color, frontImg = obj.frontImg; var node = this._node = new ht.Node();//노드 node.s 생성 ({//노드의 스타일을 setStyle 'all.color'의 약어로 설정: color,//노드 'front.image'의 6면 색상 설정: frontImg //노드 전면에 이미지를 설정});};
이런 식으로 서버 컴포넌트를 생성해야 하는 곳에 새로운 서버 컴포넌트 객체를 직접 생성할 수 있고, 위에서 선언한 setHost 및 기타 함수를 직접 호출할 수 있는데, 이는 곧 사용할 것입니다.
다음으로 Editor.Cabinet 캐비닛 클래스를 만듭니다. 이 메서드는 위의 Editor.Server 클래스 정의 메서드와 유사합니다.
ht.Default.def('Editor.Cabinet', Object, { addToDataModel: function(dm) { dm.add(this._door); dm.add(this._node); this._serverList.forEach(함수(들) { s.addToDataModel(dm) }), p3: 함수() { this._node.p3.apply(this._node, 인수);//노드의 3D 좌표 설정}});
Editor.Cabinet 클래스 생성
이 클래스는 이전 Editor.Server 서버 구성 요소 클래스보다 상대적으로 더 복잡합니다. 이 클래스는 캐비닛 내부에 캐비닛 본체, 캐비닛 도어 및 서버 구성 요소를 만듭니다.
var C = E.Cabinet = function(obj) { var color = obj.color, doorFrontImg = obj.doorFrontImg, doorBackImg = obj.doorBackImg, s3 = obj.s3; var node = this._node = new ht.Node() ; // 캐비닛 node.s3(s3); //노드의 크기를 setSize3d로 설정합니다. node.a('cabinet', this);//캐비닛 속성 사용자 정의 node.s({//노드 스타일을 setStyle 'all.color'로 설정: color,//노드의 6개 측면 색상 설정 'front.visible': false//노드의 전면이 보이는지 설정}); if (Math.random() > 0.5) { node.addStyleIcon('alarm', {//노드에 아이콘 이름을 추가합니다. ['icon thermometer'], //여러 문자열을 포함하는 배열, 각 문자열은 그림 또는 벡터에 해당합니다(ht.Default.setImage를 통해 등록됨).face: 'top', //기본값은 정면, 아이콘 3D 방향 , 사용 가능한 값은 left|right|top|bottom|front|back|center position: 17, //자동 회전 아이콘 위치 지정: 'y', //기본값은 false, 아이콘이 3D에서 자동으로 눈의 방향을 향하는지 여부 t3: [0, 16, 0], //기본값은 정의되지 않음, 3D에서 아이콘의 오프셋, 형식은 [x ,y,z] 너비: 37,//각 아이콘의 너비를 지정합니다. 기본값은 이미지 높이 등록 시 너비를 기준으로 합니다: 32,//각 아이콘의 높이를 지정합니다. 기본값은 이미지 TextureScale을 등록할 때 높이를 기준으로 합니다. 4, //기본값은 2입니다. 이 값은 메모리에 의해 생성된 실제 맵의 배수를 나타냅니다. 너무 크게 설정하면 안 됩니다. 그렇지 않으면 visible: { func: function() { return !! E.alarmVisible; }}//표시 여부를 나타냅니다. }) } var door = this._door = new ht.DoorWindow();//캐비닛 문 door.setWidth(s3[0]);//3D 토폴로지에서 x축 방향의 그래픽 요소 길이를 설정합니다. door.setHeight(1);// 3D 토폴로지 z축의 그래픽 요소 길이 door.setTall(s3[1]);//y축에서 노드 프리미티브의 길이를 제어합니다. door.setElevation(0);//3D 좌표계 도어에서 프리미티브 중심의 y좌표를 설정합니다. .setY(s3[2 ] * 0.5);//y축에 노드의 위치를 설정합니다.door.setHost(node);//흡착문을 설정합니다.s({//노드 스타일을 설정합니다. setStyle 'all.color': color,/ /'front.image' 노드의 6면 색상 설정: doorFrontImg, //'front.transparent' 노드의 전면 이미지 설정: true, //노드의 앞면이 투명한지 여부 설정 'back .image': doorBackImg, //'back.uv' 노드 뒷면에 이미지를 설정합니다. [1,0, 1,1, 0,1, 0,0],//노드 뒤의 uv 맵을 사용자 정의합니다. 비어 있는 경우 기본값은 [0,0, 0,1, 1,1, 1입니다. ,0] ' dw.axis': 'right'//DoorWindow 요소 확장 및 닫기 작업을 위한 회전 축을 설정합니다. 가능한 값은 left|right|top|bottom|v|h }); ._serverList = []; max = 6, list = E.randomList(max, Math.floor(Math.random() * (max - 2)) + 2); //global.js에 선언된 난수를 구하는 함수 var server, h = s3 [0] / 4; list.forEach(function(r) { var server = new E.Server({ //서버 구성 요소 색상: 'rgb(51,49,49)', frontImg: '서버 구성 요소 양호' }); server.s3(s3[0] - 2, h, s3[2] - 4);//노드 크기 설정 server.setElevation((r - max * 0.5) * (h + 2 ));//y축에 노드 중심점의 좌표를 설정합니다. server.setHost(node);//노드의 흡착을 설정합니다. serverList.push(server);//serverList로 });};에 서버 노드를 추가합니다.
위 코드에서 언급되지 않은 유일한 것은 Editor.randomList 함수입니다. 이 함수는 global.js 파일에 선언되어 있으며 다음과 같이 선언됩니다.
var E = window.Editor = { leftWidth: 0, topHeight: 40, randomList: function(max, size) { var list = [], ran; while (list.length < size) { ran = Math.floor(Math. 무작위() * 최대); if (list.indexOf(ran) >= 0) list.push(ran) } 반환 목록;
좋아요, 이제 장면의 각 부분에 대한 클래스가 생성되었으므로 장면을 생성한 다음 해당 프리미티브를 그 안에 쌓아야 합니다!
장면 생성이에 익숙한 학생은 HT를 사용하여 3D 장면을 생성하려면 새로운 3D 구성 요소만 필요하고 addToDOM 함수를 통해 본문에 장면을 추가해야 한다는 점을 알아야 합니다.
var g3d = E.main = new ht.graph3d.Graph3dView() //3d 장면
main.js 파일은 벽, 바닥, 문, 에어컨, 모든 캐비닛의 생성 및 배출 위치 등 3D 장면에 필요한 몇 가지 요소와 매우 중요한 대화형 부분을 주로 수행합니다.
벽, 바닥, 문, 에어컨, 캐비닛 생성에 대한 코드는 게시하지 않겠습니다. 관심이 있으시면 코드를 직접 확인하십시오. 여기서는 주로 캐비닛 및 캐비닛과 관련된 개체를 두 번 클릭하는 것에 대해 설명합니다. 캐비닛 도어, 서버 장비)를 사용하여 3D를 생성하는데, 더블클릭을 하면 카메라의 시선 중심이 캐비닛 앞쪽의 특정 위치로 이동하게 되는데, 이 움직임이 예전에는 잘 안 됐던 것 같아서 생각했습니다. 이 부분에 대해 오랫동안 설명을 했고, 마지막으로 이 Demo의 구현 방법을 참조했습니다.
눈과 중심을 반복적으로 설정할 수 있도록 이 두 매개변수 설정에 해당하는 내용이 setEye 및 setCenter 메소드로 캡슐화됩니다. setCenter 메소드는 setEye 메소드와 유사하므로 여기서는 반복하지 않습니다.
//눈 위치 설정 var setEye = function(eye,finish) { if (!eye) return; var e = g3d.getEye().slice(0),//현재 눈 값 가져오기 dx = eye[0] - e[0], dy = eye[1] - e[1], dz = eye[2] - e[2] // 500밀리초 애니메이션 전환 시작 ht.Default.startAnim({ 지속 시간: 500, easing: easing,//애니메이션 easing 함수 FinishFunc: 종료 || function() {}, //애니메이션 종료 후 호출되는 함수 action: function(v, t) {//이징 전달을 나타내도록 애니메이션 v 설정 (t) 함수 연산 후의 값인 t는 현재 애니메이션의 진행률을 나타냅니다. [0~1], 일반적인 속성 변경은 v 매개변수 g3d.setEye([ //3D 장면에 눈을 설정합니다. 눈의 값은 x, y, z축 각각의 값에 해당하는 배열입니다. e[0] + dx * v, e[1] + dy * v, e[2] + dz * v ]) } }) ;};
setCenter 함수를 반복해서 선언하지 않았다고 해서 이 함수가 중요하지 않다는 뜻은 아니다. 반대로 시선을 이동시키는 과정에서 이 함수가 결정적인 역할을 한다는 것은 위의 setEye 함수가 내가 원하는 것과 같다. 내 목표 위치 앞으로 걸어가는 것(적어도 이 목적을 위해 내가 정의한 것이 사용됨), sCenter 정의는 목표의 위치로 시선을 이동시키는 것입니다(예를 들어 현재 위치에 서서 오른쪽 뒤에 있는 물체를 볼 수도 있고 오른쪽으로 뒤로 가서 물체 앞에 설 수도 있습니다). 보는 대상) 매우 중요합니다. 맛보시기 바랍니다.
두 번 클릭 이벤트는 간단합니다. HT로 캡슐화된 이벤트를 듣고 이벤트 유형을 결정하고 해당 조치를 취하면 됩니다.
g3d.mi(function(e) {//addInteractorListener 이벤트 수신 함수 if (e.kind !== 'doubleClickData') //이벤트 유형을 더블클릭 노드로 결정 return; var data = e.data, p3 ; if (data.a('cabinet')) //Body p3 = data.p3() else { host = data.getHost() //클릭한 노드의 흡착 객체를 가져옵니다. if (host && 호스트.a('cabinet')) {//흡착 객체가 캐비닛인 경우 p3 = 호스트.p3(); } } if (!p3) return; setCenter(p3) //중앙 타겟의 이동 위치를 The로 설정합니다. 캐비닛 위치 setEye([p3[0], 211, p3[2] + 247]) //눈이 움직일 위치 설정});상단 네비게이션 바
이 예제를 처음 봤을 때 이 사람 정말 대단하다고 생각했어요. 오랫동안 HT를 사용해왔지만 아직까지 HT의 ht.widget.Toolbar를 사용하여 이렇게 아름다운 효과를 만들어내지는 못했습니다. 그러고 보니 이게 실제로 HT를 사용하고 있다는 걸 깨달았습니다. 형태를 가지고 만든 거라니 놀랍네요. 정말 멍청하네요.
var form = E.top = new ht.widget.FormPane(); //상단 폼 컴포넌트 form.setRowHeight(E.topHeight);//행 높이 설정 form.setVGap(-E.topHeight);//폼 컴포넌트의 가로 간격을 행 높이의 음수 값으로 설정하여 유지합니다. 동일한 위치의 여러 행 Line form.setVPadding(0);//양식의 상단과 상단과 구성 요소 콘텐츠 사이의 간격을 설정합니다. form.addRow([null, {//양식에 구성요소 행을 추가합니다. 첫 번째 매개변수는 요소 배열입니다. 요소는 문자열, json 형식으로 설명된 구성요소 매개변수 정보, html 요소 또는 null 이미지일 수 있습니다. { icon: './symbols/ inputBG.json ', Stretch: 'centerUniform' }}], [40, 260]);//두 번째 매개변수는 각 요소의 너비 정보 배열입니다. 1보다 큰 너비 값은 고정된 절대값을 나타내고, 1보다 작거나 같은 너비 값은 상대값을 나타낼 수도 있습니다. 80+0.3의 조합 form.addRow([null, null , { id: 'searchInput', textField: {}}, { element: '컴퓨터실 시각적 관리 시스템', color: 'white', 글꼴: '18px arial , 산세리프'}, null, { 버튼: { // 라벨: 'ViewChange', 아이콘: './symbols/viewChange.json', 배경: null, selectBackground: 'rgb(128,128,128)', borderColor: 'rgba(0, 0, 0, 0 ) ', onClicked: function() { E.focusTo() } }}, null, { 버튼: { // 라벨: '경고', 아이콘: './symbols/alarm.json', togglable: true, selected: false, background: null, selectBackground: 'rgb(128,128,128)', borderColor: 'rgba(0, 0, 0, 0)', onClicked: function( e) { E.setAlarmVisible(this.isSelected()) } }}, null], [40, 42, 218, 300, 0.1, 50, 10, 50, 10]);
위의 내용만 가능하지만 실제로 html 태그에 추가되지는 않습니다. 이는 현재 인터페이스에 아무것도 없다는 의미입니다! 페이지가 로드될 때 본문에 3D 장면을 추가하는 것을 잊지 말고, 창 크기 변경 이벤트를 설정할 때 양식도 실시간으로 업데이트되어야 합니다.
window.addEventListener('load', function() { g3d.addToDOM(); //본문에 3D 장면 추가 document.body.appendChild(E.top.getView()); //본문의 기본 div 추가 양식 구성 요소 본문에 추가 window.addEventListener('resize', function() {//창 크기 변경 이벤트 수신 E.top.iv();//양식 양식의 기본 div 업데이트 });});
다음은 HT의 메커니즘을 이해하는 데 매우 중요한 addToDOM 함수에 대한 설명입니다. HT 구성 요소는 일반적으로 BorderPane, SplitView 및 TabView와 같은 컨테이너에 포함되어 있습니다. 가장 바깥쪽 HT 구성 요소는 사용자가 getView()에서 반환된 기본 div 요소를 페이지의 DOM 요소에 수동으로 추가해야 한다는 것입니다. , 상위 컨테이너의 크기가 변경되면 상위 컨테이너가 BorderPane 및 SplitView와 같은 HT의 미리 정의된 컨테이너 구성 요소인 경우 HT 컨테이너는 자동으로 하위 구성 요소의 무효화 함수를 반복적으로 호출하여 업데이트를 알립니다. 그러나 상위 컨테이너가 기본 HTML 요소인 경우 HT 구성 요소는 업데이트가 필요하다는 것을 알 수 없습니다. 따라서 가장 바깥쪽 HT 구성 요소는 일반적으로 창의 창 크기 변경 이벤트를 수신하고 가장 바깥쪽의 무효화 함수를 호출해야 합니다. 업데이트할 구성 요소입니다.
창을 채우기 위해 가장 바깥쪽 구성 요소의 로드를 용이하게 하기 위해 HT의 모든 구성 요소에는 addToDOM 기능이 있으며 해당 구현 논리는 다음과 같습니다. 여기서 iv는 무효화의 약어입니다.
addToDOM = function(){ var self = this, view = self.getView(), style = view.style; document.body.appendChild(view); //본문에 장면의 기본 div 추가 style.left = ' 0';//HT는 모든 구성요소의 기본 div 위치를 절대값으로 설정합니다. style.right = '0' style.top = '0'; window.addEventListener('resize', function () { self.iv(); }, false); //창 크기 변경에 대한 이벤트를 수신하고 구성 요소 변경 및 업데이트를 알림}
이렇게 하면 모든 코드가 끝났습니다. 마우스 오른쪽 버튼을 클릭하여 직접 확인할 수 있으며, 해당 json 파일을 네트워크에서 얻을 수 있습니다.
위의 내용은 이 기사의 전체 내용입니다. 모든 분들의 학습에 도움이 되기를 바랍니다. 또한 모든 분들이 VeVb Wulin Network를 지지해 주시길 바랍니다.