Kawałek Kodu

Arcus tangens, to nie straszliwy władca Gór Wschodnioserbskich. Jest to jedna z funkcji cyklometrycznych, które stanowią grupę funkcji odwrotnych do funkcji trygonometrycznych. Skoro istnieje tangens, to arcus tangens jest jego "złym" bliźniakiem. "Zły", bo właśnie nie jest taki straszny jak go malują, a przy jego pomocy można malować fajniejsze rzeczy niż z pomocą tangensa.

Tangens przyjmując za argument kąt zwraca nam pewną wartość, a arcus tangens jako, że jest "odwrotny", zwraca kąt (w radianach) dla pewnej wartości. I tu się zaczyna jego urok.

W JavaScript mamy do dyspozycji dwie metody obiektu Math dotyczące naszej funkcji. Jedna to atan, druga to atan2.
Zaczniemy od atan2, bo jest łatwiejsza w zrozumieniu. Metoda ta przyjmuje dwa argumenty: y i x (w takiej kolejności). Zwraca nam natomiast kąt w zakresie -PI,PI, jaki utworzy punkt o wspołrzędnych (x,y) oraz dodatnia oś odciętych (oś X). Metoda atan przyjmuje jeden argument - iloraz y/x, a zwraca wartość -PI/2,PI/2 (pierwsza i czwarta ćwiartka układu). Z tego powodu, że iloraz gubi znak nie wiemy czy jest on ujemny, bo x jest ujemny, czy dlatego, że y jest ujemny. Nie wiemy również dlaczego jest dodatni, czy obydwie wartości są dodatnie czy ujemne. Oczywiście jego też możemy wykorzystać, ale wtedy musimy wynik korygować o +PI lub -PI, w zależności jaki znak miała współrzędna x i y.

Pewnie(?) już widzisz światełko w tunelu, bo skoro możemy podać współrzędne, a otrzymać kąt, to można to jakoś wykorzystać. I tu się nie mylisz! Nie byłoby frajdy gdyby nie podstawić za x oraz y odpowiadające im współrzędne myszy. A co będzie środkiem układu współrzędnych? Może być środek ekranu, ale u nas będzie ich kilka.

Naszym polem zabawy będzie siatka 5x5 złożona z kwadratowych pól, a w każdym z nich będzie strzałka. Strzałka domyślnie (dla kąta 0 stopni) będzie spoczywała poziomo, zwrócona w prawo (kąt 0 stopni). A jak będze się zachowywać dla innych kątów?

<div id="container">
  <div></div> <!-- 25 razy powtórzony -->
</div>

+

/* kontener
   505/5=101, dzieki temu strzalka bedzie dokladnie w polowie bloczka
*/
#container {
  width: 505px;
  height: 505px;
}

/* pojedynczy blok z pozioma kreska */
#container>div {
  width: 20%;
  height: 20%;
  float: left;
  background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=) left center repeat-x;
  position: relative;
}

/* grot strzalki gora i dol */
#container>div:before,
#container>div:after {
  position: absolute;
  left: 84px;
  top: 43px;
  content: '';
  display: block;
  width: 20px;
  height: 1px;
  transform: rotate(45deg);
  background: #000;
}

/* grot strzalki dol */
#container>div:after {
  top: 57px;
  transform: rotate(-45deg);
}

+

var dimensions = document.getElementById('container').getBoundingClientRect();
var arrows = document.querySelectorAll('#container>div');

var centerX = dimensions.width / 5 / 2;
var centerY = dimensions.height / 5 / 2;

document.body.addEventListener('mousemove', function(e) {
  for (var y = 0; y < 5; y++) {
    for (var x = 0; x < 5; x++) {
      var deltaX = (e.clientX - dimensions.left - (2 * centerX * x + centerX));
      var deltaY = (e.clientY - dimensions.top - (2 * centerY * y + centerY));
      arrows[x + y * 5].style.transform = 'rotate(' + (Math.atan2(deltaY, deltaX) * 180 / Math.PI) + 'deg)';
    }
  }
});

Co się dzieje w kodzie JavaScript?

Obiegamy każdy bloczek kontenera (obydwie pętle). Dla każdego bloczka obliczamy odległość w osi X oraz Y między środkiem bloczka, a kurosem myszy. Wyrażenie e.clientX-dimensions.left daje położenie myszy wewnątrz kontenera - kontener może być przesunięty na ekranie, więc musimy skorygować położenie kursora o jego przesunięcie. Od położenia kursora odejmujemy wartość środka bloczka. Dla bloczka pierwszego będzie to: 2*centerX*0+centerX=centerX, dla drugiego 2*centerX*1+centerX=3*centerX, czyli to przesunięcie względem pierwszego bloczka o szerokość całego bloczka (2*centerX=szerokość_bloczka). Analogicznie postępujemy przy obliczaniu deltaY.

Wprowadzając małą modyfikację już nie związaną z naszym bohaterem, możemy osiągnąć taki efekt:

var length = Math.sqrt(deltaX * deltaX + deltaY * deltaY) / 200;
arrows[x + y * 5].style.transform = 'scale(' + length + ') rotate(' + (Math.atan2(deltaY, deltaX) * 180 / Math.PI) + 'deg)';

Dodaliśmy tu obliczenie długości wektora pomiędzy kursorem myszy, a środkiem każdego bloczka i używamy go jako współczynnik skali (czym mniejsza długość wektora/odległość myszy od bloczka, tym skala mniejsza).

Mam nadzieję, że dzisiejszy wpis nie zrazi Cię tym, że nawet przy przykładaniu się do matematyki można stanąć do kąta.

 

Przydatne linki:
Funkcje cyklometryczne
Funkcja atan2
Metoda atan
Metoda atan2