Próbuję sprawić, aby połączenie obracało się płynnie wokół środka płótna, w kierunku kąta wskaźnika myszy. To, co mam, działa, ale chcę, aby animowało jak najkrótszą odległość, aby uzyskać kąt myszy. Problem występuje, gdy wartość zapętla się wokół linii poziomej ( 3.14
i -3.14
). Najedź myszką na ten obszar, aby zobaczyć, jak zmienia się kierunek, i zajmie to długą drogę z powrotem.
Odpowiedni kod
// ease the current angle to the target angle
joint.angle += ( joint.targetAngle - joint.angle ) * 0.1;
// get angle from joint to mouse
var dx = e.clientX - joint.x,
dy = e.clientY - joint.y;
joint.targetAngle = Math.atan2( dy, dx );
Jak sprawić, by obracał się na najkrótszej odległości, nawet „w poprzek”?
mathematics
javascript
animation
lerp
easing
chrząknięcie
źródło
źródło
Odpowiedzi:
Nie zawsze jest to najlepsza metoda i może być droższa pod względem obliczeniowym (choć ostatecznie zależy to od sposobu przechowywania danych), ale przedstawię argument, że przeszukiwanie wartości 2D działa dość dobrze w większości przypadków. Zamiast ustawiać żądany kąt, możesz przeciągać pożądany znormalizowany wektor kierunku.
Zaletą tej metody w porównaniu z metodą „wybierz najkrótszą drogę do kąta” jest to, że działa ona, gdy trzeba interpolować więcej niż dwie wartości.
Kiedy lerping wartości barwy, można wymienić
hue
w[cos(hue), sin(hue)]
wektorze.W twoim przypadku utrzymanie znormalizowanego kierunku połączenia:
Kod może być krótszy, jeśli można użyć klasy wektorowej 2D. Na przykład:
źródło
dx /= len
...len ? len : 1.0
Część po prostu unika dzielenia przez zero, w rzadkim przypadku, gdy mysz jest umieszczona dokładnie w pozycji połączenia. To mogło być napisane:if (len != 0) dx /= len;
.0°
i180°
? W postaci wektorowej:[1, 0]
i[-1, 0]
. Interpolację wektory da Ci albo0°
,180°
albo podzielić przez 0 błędów, w przypadkut=0.5
.Sztuka polega na tym, aby pamiętać, że kąty (przynajmniej w przestrzeni euklidesowej) są okresowe o 2 * pi. Jeśli różnica między bieżącym kątem a kątem docelowym jest zbyt duża (tzn. Kursor przekroczył granicę), po prostu dostosuj aktualny kąt, odpowiednio dodając lub odejmując 2 * pi.
W takim przypadku możesz spróbować wykonać następujące czynności: (Nigdy wcześniej nie programowałem w JavaScript, więc wybacz mój styl kodowania).
EDYCJA : W tej implementacji zbyt szybkie przesuwanie kursora wokół środka stawu powoduje jego szarpnięcie. Jest to zamierzone zachowanie, ponieważ prędkość kątowa złącza jest zawsze proporcjonalna do
dtheta
. Jeśli takie zachowanie jest niepożądane, problem można łatwo rozwiązać, nakładając kołpak na przyspieszenie kątowe stawu.Aby to zrobić, musimy śledzić prędkość połączenia i nałożyć maksymalne przyspieszenie:
Następnie dla naszej wygody wprowadzimy funkcję obcinania:
Teraz nasz kod ruchu wygląda tak. Najpierw obliczamy
dtheta
jak poprzednio, dostosowującjoint.angle
w razie potrzeby:Następnie, zamiast natychmiastowego przesunięcia stawu, obliczamy prędkość docelową i używamy jej
clip
do wymuszenia jej w dopuszczalnym zakresie.Zapewnia to płynny ruch, nawet przy zmianie kierunku, podczas wykonywania obliczeń tylko w jednym wymiarze. Ponadto umożliwia niezależną regulację prędkości i przyspieszenia złącza. Zobacz demo tutaj: http://codepen.io/anon/pen/HGnDF/
źródło
dtheta
. Czy zamierzałeś, aby staw miał pewien impet?Uwielbiam inne udzielone odpowiedzi. Bardzo techniczne!
Jeśli chcesz, mam bardzo prostą metodę, aby to osiągnąć. Przyjmiemy kąty dla tych przykładów. Pojęcie to można ekstrapolować na inne typy wartości, takie jak kolory.
Właśnie utworzyłem to w przeglądarce i nigdy nie byłem testowany. Mam nadzieję, że dobrze zrozumiałem logikę przy pierwszej próbie.
[Edytuj] 2017/06/02 - Trochę wyjaśniono logikę.
Zacznij od obliczenia odległości do przodu i odległości do tyłu i pozwól, aby wyniki wykroczyły poza zakres (0–360).
Normalizowanie kątów przywraca te wartości z zakresu (0–360). Aby to zrobić, dodajesz 360, aż wartość przekroczy zero, i odejmujesz 360, gdy wartość jest wyższa niż 360. Wynikowe kąty początkowe / końcowe będą równoważne (-285 jest takie samo jak 75).
Następnie znajduje się najmniejszy znormalizowany kąt odległości do przodu lub do tyłu. distanceForward w tym przykładzie staje się 75, czyli mniej niż znormalizowana wartość distanceBackward (300).
Jeśli distanceForward jest najmniejszym ORAZ endAngle <startAngle, przedłuż endAngle poza 360 poprzez dodanie 360. (w przykładzie staje się 375).
Jeśli distanceBackward jest najmniejszym ORAZ endAngle> startAngle, przedłuż endAngle do wartości poniżej 0, odejmując 360.
Będziesz teraz lerp od startAngle (300) do nowego endAngle (375). Silnik powinien automatycznie dostosowywać wartości powyżej 360, odejmując dla Ciebie 360. W przeciwnym razie musiałbyś lerp od 300 do 360, NASTĘPNIE lerp od 0 do 15, jeśli silnik nie znormalizuje dla ciebie wartości.
źródło
atan2
pomysł oparty na tym jest prosty, ten może zaoszczędzić trochę miejsca i trochę wydajności (nie trzeba przechowywać xiy, ani zbyt często obliczać sin, cos i atan2). Myślę, że warto nieco wyjaśnić, dlaczego to rozwiązanie jest poprawne (tj. Wybierając najkrótszą ścieżkę na okręgu lub kuli, tak jak SLERP robi dla czwartorzędów). To pytanie i odpowiedź należy umieścić na wiki społeczności, ponieważ jest to bardzo częsty problem, z którym boryka się większość programistów.