Encontrar un hipotenusa con JavaScript

Recientemente, tuve la idea de crear un sitio que te permitiera intercambiar ideas. Esto es lo que básicamente había imaginado:

Un usuario comenzaría con una idea o pensamiento central y sería capaz de ramificar ideas o pensamientos relacionados. Esto sería ideal para planear lecciones, presentaciones o incluso para estudiar. Mientras hacía una lluvia de ideas sobre esta idea, se me ocurrieron cuatro componentes que el proyecto necesitaría:

1 una entrada / Textarea

2 Un botón para crear una nueva rama de una idea.

3 Una línea para conectar visualmente la idea y la idea ramificada.

Para hacer esto, rápidamente encontré un problema. Crear una entrada / textarea es fácil. Crear un botón que crea una nueva entrada / textarea también es bastante fácil. La pieza difícil es visualmente diseñarla de una manera que sea funcional y tenga sentido visualmente. Por ejemplo, para simplificar, simplemente podríamos hacer que cada botón que crea un nuevo elemento de formulario coloque el elemento de formulario verticalmente debajo del elemento anterior. Aunque esto sería más simple desde el punto de vista de la programación, visualmente, no tendría mucho sentido para el usuario, ya que sería difícil saber qué cuadro de texto ramificado estaba conectado a qué idea o cuadro de texto anterior. Como normalmente me sirve bien, decidí empezar de a poco, y ver si podía hacer que los mecánicos trabajaran en pequeña escala primero. Empecé con puntos, cada punto representa un cuadro de elemento / texto. Cada punto tiene 25 píxeles de ancho y 25 píxeles de altura, de color negro. Mi primer objetivo fue agregar un nuevo punto cuando se hace clic en el primer punto, y luego distribuir los puntos subsiguientes alrededor del primer punto cada vez que se hace clic. Para resolver esto, creé una variable llamada "clic" y la configuré en 0; Luego, en cada evento de clic, agrego uno.

 let clicks = 0; 
$ ('botón'). clic (función () {
clics = clics + 1;

Luego creo un elemento dentro de una declaración if.

 if (clicks == 1) { 
let blackDot = document.createElement ('div');
blackDot.id = "outerDiv1";
document.body.appendChild (blackDot);
document.getElementById ('contenedor'). appendChild (blackDot);
blackDot.className = "blackDotClass";
}

Eso es lo básico. Luego le agrego un margen superior e izquierdo. El no está incluido en la clase "blackDotClass" porque el margen será diferente para cada elemento creado. Por ejemplo, el primer punto estará a la derecha del elemento principal, el segundo punto creado estará debajo de él, el tercero a la izquierda, etc., etc. Lo insertaré así:

 if (clicks == 1) { 
let blackDot = document.createElement ('div');
blackDot.id = "blackDotID1";
document.body.appendChild (blackDot);
document.getElementById ('contenedor'). appendChild (blackDot);
blackDot.style.marginTop = "25px";
blackDot.style.marginLeft = "200px";
blackDot.className = "blackDotClass";
}

Luego, si se hace clic en el punto padre una segunda vez, podríamos hacer algo como esto:

 if (clics == 2) { 
let blackDot = document.createElement ('div');
blackDot.id = "blackDotID2";
document.body.appendChild (blackDot);
document.getElementById ('contenedor'). appendChild (blackDot);
blackDot.style.marginTop = "0px";
blackDot.style.marginLeft = "200px";
blackDot.className = "blackDotClass";
}

Lo único que hemos cambiado es la "ID" y el margen superior. Luego, para el tercer elemento, probablemente cambiemos el margen superior e izquierdo, para colocar cada nuevo elemento en un círculo alrededor del punto principal. Esta parte es lo suficientemente simple, pero aún así, esto sería confuso para un usuario sin líneas físicas que conectan un punto a otro. De lo contrario, de nuevo, sería realmente difícil saber qué elementos están realmente conectados, sin nada más que espacios.

Mi idea inicial para resolver esto fue usar polígonos. Dado que cada "punto" o "elemento" o lo que sea que usemos tendrá un conjunto de coordenadas xey, podría usar la coordenada del elemento padre y las coordenadas para dibujar una línea poligonal de un elemento al siguiente. Aquí hay un diagrama de lo que había imaginado:

De hecho, pasé por varias iteraciones de esta idea antes de llegar a la conclusión de que svg polygons no funcionaría. La razón es que los polígonos son un elemento svg y deben estar dentro de un contenedor svg. Debido a esto, no estás simplemente alineado con b. Pero también los alinea con el elemento svg, que tiene su propio conjunto de dimensiones. Toma esto por ejemplo:

Puede iniciar el proceso, y tener todo alineado correctamente, y terminar con el ejemplo anterior, donde la línea no se conecta desde el punto a al punto b. Naturalmente, la suposición es que la línea no es lo suficientemente larga, y es un problema con las coordenadas o la longitud del polígono. Cuando su problema puede ser que el contenedor svg no tiene el tamaño correcto o no está alineado correctamente. Lo que realmente tienes es esto:

Tu línea es correcta, y tus coordenadas pueden ser correctas, pero debido a que el contenedor svg es demasiado pequeño, solo ves una pequeña porción del polígono real. Claro, puedes hacer un borde alrededor del contenedor para ver dónde está, pero imagina cuán complejo se hace cuando tienes varios elementos, luego los contenedores y polígonos svg … es una pesadilla.

Así que se me ocurrió una solución un poco más simple. Solo un div, con un ancho de 1 y un borde. Cada vez que hago clic en "A" y creo un nuevo elemento secundario "B", también creo un tercer elemento, "C" una línea que conecta los dos, o un div con un borde, entre los dos elementos.

Si A y B están en el mismo eje X, y la pantalla está configurada en línea, o están dentro de una etiqueta span, entonces su trabajo está hecho, porque no es necesario que realice ningún cálculo para el eje y. Sin embargo, nuevamente, desde el punto de vista del usuario, sería difícil saber dónde están conectadas las ideas y los elementos si todo está en línea recta. Por lo tanto, en estos diagramas de mapas mentales, por lo general tienden a ser de forma circular. Así que esto es lo que se me ocurrió. Después de crear B, al hacer clic en A, obtengo las coordenadas de cada uno, tal como lo hice con el polígono.

 let element1 = dot1.getBoundingClientRect (); 
let element2 = dot2.getBoundingClientRect ();
console.log (elemento1);
console.log (element2);

También quiero encontrar el punto medio de mi elemento. En caso de que mi punto sea 300px grande, no quiero que la línea se conecte a la parte superior, sino a la mitad. Hago esto dividiendo el alto y el ancho por 2, que son los datos que puedo encontrar de mi función "getboundingClient".

 let midpointX1 = element1.width / 2; 
let midpointY1 = element1.height / 2;

let midpointX2 = element2.width / 2;
let midpointY2 = element2.height / 2;

Ahora, mi proceso de pensamiento es esto. Si conozco las coordenadas xey, también incluidas en la función "getBoudningClient", espero que pueda hacer algo de matemática. Lo que quiero saber es la longitud de la línea que conectaría ambos elementos y el ángulo de la línea. Puedo hacer esto con algo de trigonometría. Primero, encontraré la longitud con el Teorema de Pitágoras: A cuadrado + B cuadrado = C al cuadrado.

Al convertir la relación de los dos elementos en las esquinas de un triángulo, podemos usar las matemáticas para descubrir la longitud de la línea, como mencioné anteriormente, y luego podemos usar la tangente para descubrir el ángulo de la línea. Lo que haré es crear una función que tome las coordenadas de ambos y los ejecute para encontrar lo que estoy buscando.

 let midpointX1 = element1.width / 2; 
let midpointY1 = element1.height / 2;

let midpointX2 = element2.width / 2;
let midpointY2 = element2.height / 2;

let top1 = element1.top - midpointY1;
let top2 = element2.top - midpointY2;
let left1 = element1.left - midpointX1;
let left2 = element2.left - midpointX2;

función findTriangle (w, x, y, z) {

let difference = function (a, b) {return Math.abs (a - b); }
dejar opuesto = diferencia (w, x);
dejar adyacente = diferencia (y, z);

let hypotenuseLengthSquared = Math.pow (opposite, 2) + Math.pow (adyacente, 2);
console.log (hypotenuseLengthSquared);

let hipenuseLength = Math.sqrt (hypotenuseLengthSquared);
console.log (hypotenuseLength);
console.log (adyacente);

let angle = Math.atan (opuesto / adyacente) * 100;
console.log (ángulo);
return [opuesto, adyacente, hypotenuseLength, ángulo];
}
let triangle = findTriangle (top1, top2, left1, left2);
console.log (triángulo);

La función "findTriangle" toma el elemento superior e izquierdo, menos el punto medio, suponiendo que nuestros elementos son simétricos, y le da básicamente las coordenadas xey de ambos elementos para calcular el ángulo y la longitud de la hipotenusa. También tengo la función devolver los lados adyacentes y opuestos en caso de que necesite usarlos más tarde también. Ahora, crearé mi div, usando esas coordenadas y regresa.

 dejar newDiv = document.createElement ('div'); 
newDiv.id = "prueba";
document.body.appendChild (newDiv);
document.getElementById ('dot1'). appendChild (newDiv);
newDiv.style.borderColor = "# 1cce3a";
newDiv.style.borderWidth = "3px";
newDiv.style.borderStyle = "sólido";
newDiv.style.borderColor = "# 1cce3a";
newDiv.style.width = "" + triángulo [2] + "px";
newDiv.style.transform = "rotar (" + triángulo [3] + "deg)";
newDiv.style.zIndex = -1;

Como mi declaración de devolución es una matriz, cuando solicito el ancho y la transformación de mi elemento, estoy usando solo los índices de matriz que necesito [2] y [3].

Ahora puedo ejecutar exactamente esta misma función dentro de mi segunda instrucción if. Dado que el segundo punto aparecerá un poco más bajo en el DOM que en el primero, la función calculará la distancia entre los dos y devolverá la línea de conexión (div) para que estén conectados visualmente en la pantalla, y podemos tener algo similar a cuál fue mi visión original Sin embargo, incluso con estos cálculos precisos, las cosas pueden ir mal aquí. Por ejemplo, si el contenedor está configurado en una pantalla de flexbox, arrojará todos los cálculos. Pero, en general, es un ejercicio bastante divertido.

Una de las razones principales para escribir esto es que, como hombre más joven, detestaba las matemáticas. No era bueno en eso, y honestamente, no hice mucho esfuerzo. Incluso cuando aprendí JavaScript por primera vez, me encontré con estos ejercicios en los que necesitaría encontrar algún número oscuro a través de alguna fórmula complicada. Y siempre pensaba: "¿Por qué necesitaría usar esto alguna vez? ¡¡Carece de sentido!!". Lo sé al menos por mí, cuando tengo esos pensamientos, inmediatamente empiezo a desconectar. Pero, durante este proceso, fue genial ver cuán increíblemente práctico y útil es utilizar el Teorema de Pitágoras y otras ecuaciones de trigonometría. El punto es que si alguna vez te sientes así, no te desconectes. ¡Es posible que necesite usar esa información antes de lo que cree! No dude en comunicarse para obtener comentarios o preguntas. ¡Gracias!