Les salles informatiques 3D rendues avec WebGL ne sont plus une nouveauté. L'objectif principal de cet article est d'expliquer le problème de l'œil et du centre dans les salles informatiques 3D, qui ont été utilisées dans le projet. Après y avoir réfléchi pendant un moment, j'ai finalement réussi. Je pense que cet exemple répond le mieux à mes besoins, je l'utilise donc comme enregistrement.
rendusLa démo de cette salle informatique 3D est plutôt bonne, elle est jolie, et les interactions de base sont satisfaisantes. Voyons comment la mettre en œuvre.
génération de code Définir la classeTout d'abord, ouvrez les js correspondants un par un à partir du chemin js appelé dans index.html. Une classe Editor.Server est personnalisée dans server.js et est créée par la fonction ht.Default.def encapsulée par HT (notez que la classe créée le nom est Editor L'éditeur devant .Server ne peut pas être remplacé par E) :
ht.Default.def('Editor.Server', Object, {//Le premier paramètre est le nom de la classe. S'il s'agit d'une chaîne, elle sera automatiquement enregistrée dans le classMap de HT ; le deuxième paramètre est la classe parent à hériter par cette classe. ; Le troisième paramètre est la déclaration des méthodes et des variables addToDataModel : function(dm) { //Ajoute le nœud au conteneur de données dm.add(this._node); // La fonction prédéfinie dans ht, passe le nœud via ajouter Méthode ajoutée au conteneur de données}, setHost: function() { //Définir l'adsorption this._node.setHost.apply(this._node, arguments }, s3: function() {//Définir la taille du nœud); this._node .s3.apply(this._node, arguments); setElevation: function() {//Contrôlez la position de l'axe Y du système de coordonnées 3D où se trouve la position centrale de la primitive Node this._node.setElevation.apply(this._node, arguments }});
Créer une classe Editor.Server
Cette classe peut créer un nœud ht.Node et définir la couleur et la texture du devant du nœud :
var S = E.Server = function(obj) {//Composant serveur var color = obj.color, frontImg = obj.frontImg; var node = this._node = new ht.Node();//Créer un nœud node.s ({//Définissez le style s du nœud sur l'abréviation de setStyle 'all.color': color,//Définissez la couleur des six côtés du nœud 'front.image': frontImg //Définit l'image sur le devant du nœud});};
De cette façon, je peux créer directement un nouvel objet composant serveur là où je dois créer le composant serveur, et je peux appeler directement setHost et d'autres fonctions que nous avons déclarées ci-dessus, que nous utiliserons bientôt.
Ensuite, créez la classe cabinet Editor.Cabinet. La méthode est similaire à la méthode de définition de la classe Editor.Server ci-dessus :
ht.Default.def('Editor.Cabinet', Object, { addToDataModel: function(dm) { dm.add(this._door); dm.add(this._node); this._serverList.forEach(function(s) { s.addToDataModel(dm); }); p3 : fonction() { this._node.p3.apply(this._node, arguments);//Définir les coordonnées 3D du nœud}});
Créer une classe Editor.Cabinet
Cette classe est relativement plus complexe que la classe précédente de composants de serveur Editor.Server. Cette classe crée un corps d'armoire, une porte d'armoire et des composants de serveur à l'intérieur de l'armoire :
var C = E.Cabinet = function(obj) { var color = obj.color, doorFrontImg = obj.doorFrontImg, doorBackImg = obj.doorBackImg, s3 = obj.s3; ; // Cabinet node.s3(s3); //Définit la taille du nœud sur setSize3d node.a('cabinet', this);//Personnaliser les propriétés de l'armoire node.s({//Définir le style du nœud sur setStyle 'all.color': color,//Définir la couleur des six côtés du nœud 'front.visible ': false//Définir si l'avant du nœud est visible}); if (Math.random() > 0.5) { node.addStyleIcon('alarm', {//Ajouter des noms d'icônes au nœud : ['icon thermomètre'], //Un tableau contenant plusieurs chaînes, chaque chaîne correspond à une image ou un vecteur (enregistré via ht.Default.setImage) face : 'top', //La valeur par défaut est front, icône Orientation en 3D , les valeurs disponibles sont left|right|top|bottom|front|back|center position : 17, //Spécifiez la position des icônes en rotation automatique : 'y', //La valeur par défaut est fausse, si l'icône fait automatiquement face à la direction de l'œil en 3D t3 : [0, 16, 0], //La valeur par défaut n'est pas définie, le décalage de l'icône en 3D, le format est [x ,y,z] largeur : 37,//Spécifiez la largeur de chaque icône, la valeur par défaut est basée sur la largeur lors de l'enregistrement de la hauteur de l'image : 32,//Spécifiez la hauteur de chaque icône, la valeur par défaut est en fonction de la hauteur lors de l'enregistrement de l'image textureScale : 4, //La valeur par défaut est 2. Cette valeur représente le multiple de la carte réelle générée par la mémoire. Elle ne doit pas être trop grande sinon cela affectera les performances visibles : { func: function() { return !! E.alarmVisible; }}//Indique le groupe d'images à afficher }); } var door = this._door = new. ht.DoorWindow();//Porte d'armoire door.setWidth(s3[0]);//Définissez la longueur de l'élément graphique dans la direction de l'axe x dans la topologie 3D door.setHeight(1);//Définissez la élément graphique dans la topologie 3D axe z dans Longueur door.setTall(s3[1]);//Contrôler la longueur de la primitive Node sur l'axe y door.setElevation(0);//Définir la coordonnée y du centre de la primitive dans le système de coordonnées 3D door. .setY(s3[2 ] * 0.5);//Définir la position du nœud sur l'axe y door.setHost(node);//Définir la porte d'adsorption.s({//Définir le style du nœud setStyle 'all.color': color,/ /Définit la couleur des six côtés du nœud 'front.image' : doorFrontImg, //Définit l'image de face du nœud 'front.transparent' : true, //Définit si la face avant du nœud est transparente 'back .image': doorBackImg, //Définit l'image au dos du nœud 'back.uv' : [1,0, 1,1, 0,1, 0,0],//Personnalisez la carte uv derrière le nœud. Si elle est vide, la valeur par défaut [0,0, 0,1, 1,1, 1. ,0] ' dw.axis': 'right'//Définissez l'axe de rotation pour les opérations d'expansion et de fermeture de l'élément DoorWindow, les valeurs possibles sont left|right|top|bottom|v|h }); ._serverList = []; max = 6, list = E.randomList(max, Math.floor(Math.random() * (max - 2)) + 2 //Fonction pour obtenir des nombres aléatoires déclarés dans le serveur var global.js, h = s3); [0] / 4; list.forEach(function(r) { var server = new E.Server({ //Couleur du composant du serveur : 'rgb(51,49,49)', frontImg : 'Composant serveur bien' }); server.s3(s3[0] - 2, h, s3[2] - 4);//Définir la taille du nœud server.setElevation((r - max * 0.5) * (h + 2 ));//Définir les coordonnées du point central du nœud sur l'axe y server.setHost(node);//Définir l'adsorption du nœud serverList.push(server);//To serverList Ajouter un nœud de serveur dans });};
La seule chose qui n'est pas mentionnée dans le code ci-dessus est la fonction Editor.randomList. Cette fonction est déclarée dans le fichier global.js et est déclarée comme suit :
var E = window.Editor = { leftWidth : 0, topHeight : 40, randomList : function(max, size) { var list = [], ran; while (list.length < size) { ran = Math.floor(Math. random() * max); if (list.indexOf(ran) >= 0) continue list.push(ran) ;
Bon, maintenant que les classes pour chaque partie de la scène ont été créées, nous devons créer la scène puis y empiler ces primitives !
Création de scèneLes étudiants qui le connaissent doivent savoir que l'utilisation de HT pour créer une scène 3D ne nécessite qu'un nouveau composant 3D, puis ajouter la scène au corps via la fonction addToDOM :
var g3d = E.main = new ht.graph3d.Graph3dView(); //scène 3D
Le fichier main.js contient principalement certains éléments nécessaires à la scène 3D, tels que les murs, les sols, les portes, les climatiseurs et les positions de génération et de décharge de toutes les armoires, ainsi que des parties interactives très importantes.
Je ne publierai pas le code pour la création de murs, sols, portes, climatiseurs et armoires. Si vous êtes intéressé, veuillez vérifier vous-même le code. Ici, nous parlons principalement du double-clic sur l'armoire et de tout objet lié à l'armoire (. portes d'armoires, équipement de serveur) pour créer de la 3D. Le centre de la ligne de mire de la caméra se déplacera vers une certaine position devant l'armoire sur laquelle on a double-cliqué, et ce mouvement est très fluide. Je n'étais pas doué avant, alors j'ai pensé. J'ai longuement parlé de cette partie, et j'ai finalement évoqué la méthode de mise en œuvre de cette démo.
Afin de pouvoir régler de manière répétée eye et center, le contenu correspondant au réglage de ces deux paramètres est encapsulé dans les méthodes setEye et setCenter. La méthode setCenter est similaire à la méthode setEye et ne sera pas répétée ici :
//Définir la position de l'œil var setEye = function(eye, finish) { if (!eye) return; var e = g3d.getEye().slice(0),//Obtenir la valeur actuelle de l'œil dx = eye[0] - e[0], dy = eye[1] - e[1], dz = eye[2] - e[2]; // Démarre une transition d'animation de 500 millisecondes ht.Default.startAnim({ durée : 500, easing : easing,//Fonction d'assouplissement de l'animation finishFunc: finish || function() {}, //Fonction appelée après la fin de l'animation : function(v, t) {//Définir l'animation v pour représenter le passage de l'assouplissement (t) La valeur après l'opération de la fonction, t représente la progression de l'animation en cours [0~1], les changements d'attributs généraux sont basés sur le paramètre v g3d.setEye([ //Définir l'œil dans la scène 3D La valeur de l'œil est un tableau, correspondant aux valeurs des axes x, y et z respectivement e[0] + dx * v, e[1] + dy * v, e[2] + dz * v ]); } }) ;};
Le fait que je n'ai pas déclaré à plusieurs reprises la fonction setCenter ne signifie pas que cette fonction n'est pas importante. Au contraire, cette fonction joue un rôle décisif dans le processus de déplacement de la ligne de mire. La fonction setEye ci-dessus équivaut à ce que je veux. marcher devant ma position cible (du moins je définis est utilisé à cet effet), tandis que sCenter La définition est de déplacer mon viseur vers la position de la cible (par exemple, je peux me tenir à ma position actuelle et regarder l'objet derrière moi vers la droite, ou je peux aller derrière ma droite et me tenir devant). l'objet de le regarder). C'est très important, savourez-le.
L'événement double-clic est simple. Écoutez simplement l'événement encapsulé par HT, déterminez le type d'événement et effectuez les actions correspondantes :
g3d.mi(function(e) {//addInteractorListener fonction d'écoute d'événement if (e.kind !== 'doubleClickData') //Détermine le type d'événement comme étant un retour de nœud par double-clic ; var data = e.data, p3 ; if (data. a('cabinet')) //Body p3 = data.p3(); else { host = data.getHost( //Obtenir l'objet d'adsorption du nœud cliqué if (host &&); host.a('cabinet')) {//Si l'objet d'adsorption est cabinet p3 = host.p3(); } } if (!p3) return; setCenter(p3); position du meuble setEye([p3[0], 211, p3[2] + 247]); //Définit la position où l'œil se déplacera});barre de navigation supérieure
Quand j'ai vu cet exemple pour la première fois, je pensais que cette personne est tellement géniale. J'utilise HT depuis si longtemps, mais je n'ai toujours pas réussi à créer d'aussi beaux effets en utilisant la ht.widget.Toolbar de HT. là-dessus, j'ai réalisé que cela utilisait en fait HT. C'est fait avec un formulaire, c'est incroyable, je suis tellement stupide.
var form = E.top = new ht.widget.FormPane(); //Composant de formulaire supérieur form.setRowHeight(E.topHeight);//Définissez la hauteur de ligne form.setVGap(-E.topHeight);//Définissez l'espacement horizontal du composant de formulaire sur une valeur négative de la hauteur de ligne à conserver plusieurs lignes dans la même position Ligne form.setVPadding(0);//Définit le haut du formulaire et l'espacement entre le haut et le contenu du composant form.addRow([null, {//Ajoutez une ligne de composants au formulaire. Le premier paramètre est un tableau d'éléments. Les éléments peuvent être des chaînes, des informations sur les paramètres du composant décrites au format json, des éléments HTML ou une image nulle : { icon : './symbols/. inputBG.json ', stretch : 'centerUniform' }}], [40, 260]);//Le deuxième paramètre est un tableau d'informations de largeur pour chaque élément. Une valeur de largeur supérieure à 1 représente une valeur absolue fixe, et une valeur de largeur inférieure ou égale à 1 représente une valeur relative. une combinaison de 80+0,3 form.addRow([null, null , { id : 'searchInput', textField : {}}, { element : 'Système de gestion visuelle de la salle informatique', couleur : 'blanc', police : '18px arial , sans empattement'}, nul, { bouton : { // étiquette : 'ViewChange', icône : './symbols/viewChange.json', arrière-plan : null, selectBackground : 'rgb(128,128,128)', borderColor : 'rgba(0, 0, 0, 0 ) ', onClicked : function() { E.focusTo(); } }}, null, { bouton : { // label : 'Alerte', icône : './symbols/alarm.json', basculable : vrai, sélectionné : faux, arrière-plan : null, selectBackground : 'rgb(128,128,128)', borderColor : 'rgba(0, 0, 0, 0)', onClicked : fonction ( e) { E.setAlarmVisible(this.isSelected()); 42, 218, 300, 0,1, 50, 10, 50, 10]);
Ce qui précède n'est que possible, mais il n'est pas réellement ajouté à la balise html, ce qui signifie qu'il n'y a rien sur l'interface maintenant ! N'oubliez pas d'ajouter la scène 3D au corps lors du chargement de la page, et n'oubliez pas d'ajouter le formulaire au corps lors de la définition de l'événement de changement de taille de la fenêtre, le formulaire doit également être mis à jour en temps réel :
window.addEventListener('load', function() { g3d.addToDOM(); //Ajouter la scène 3D dans le corps du document.body.appendChild(E.top.getView()); //Ajouter le div sous-jacent du composant de formulaire Ajouter au corps window.addEventListener('resize', function() {//Écouter les événements de changement de taille de fenêtre E.top.iv();//Mettre à jour le div sous-jacent du formulaire form });});
Voici une explication de la fonction addToDOM, très importante pour comprendre le mécanisme de HT. Les composants HT sont généralement intégrés dans des conteneurs tels que BorderPane, SplitView et TabView. Le composant HT le plus externe nécessite que l'utilisateur ajoute manuellement l'élément div sous-jacent renvoyé par getView() à l'élément DOM de la page. , Lorsque la taille du conteneur parent change, si le conteneur parent est constitué de composants de conteneur prédéfinis par HT tels que BorderPane et SplitView, le conteneur HT appellera automatiquement la fonction d'invalidation du composant enfant de manière récursive pour notifier la mise à jour. Mais si le conteneur parent est un élément HTML natif, le composant HT ne peut pas savoir qu'il doit être mis à jour. Par conséquent, le composant HT le plus externe doit généralement écouter l'événement de changement de taille de la fenêtre et appeler la fonction d'invalidation du composant le plus externe. composant à mettre à jour.
Afin de faciliter le chargement du composant le plus externe pour remplir la fenêtre, tous les composants de HT ont la fonction addToDOM, et sa logique d'implémentation est la suivante, où iv est l'abréviation d'invalidate :
addToDOM = function(){ var self = this, view = self.getView(), style = view.style; document.body.appendChild(view); //Ajoute le div sous-jacent de la scène dans le corps style.left = ' 0';//HT définit la position du div sous-jacent de tous les composants sur style.right = '0'; style.top = '0'; window.addEventListener('resize', function () { self.iv(); }, false); //Écouter les événements pour les changements de taille de fenêtre et notifier les modifications et mises à jour des composants}
De cette façon, tout le code est terminé. Vous pouvez cliquer avec le bouton droit pour le vérifier vous-même et le fichier json correspondant peut être obtenu à partir du réseau.
Ce qui précède représente l’intégralité du contenu de cet article. J’espère qu’il sera utile à l’étude de chacun. J’espère également que tout le monde soutiendra le réseau VeVb Wulin.