Zdarzenia webowe na Androidzie

0

Tworzę sobie pewną "apkę" w czystym HTML5/JS/CSS3, uruchamianą w przeglądarce. Najważniejszy element tej aplikacji to powierzchnia Canvas, która jest obsługiwana myszką. Dla uproszczenia, powiedzmy, że ta aplikacja, to taki ala Paint z Windows. Co oczywiste, aby taki Paint działał, należy obsłużyć 3 zdarzenia na powierzchni rysowania:

  1. Naciśnięcie przycisku myszki
  2. Przeciągnięcie myszki na naciśniętym przycisku
  3. Puszczenie przycisku myszki
    Nie będę opisywać, jak działa Paint i każdy inny program tego typu obsługiwany myszką, bo każdy to wie.

Utworzyłem 3 metody, do których wchodzą współrzędne XY położenia kursora. Do tych celów obsłużyłem "mousedown", "mousemove" i "mouseup". Na komputerze z Windows lub Linux działa to bardzo dobrze, natomiast na smartfonie z Androidem działa tak sobie, bo telefon reaguje na naciśnięcie z pewnym opóźnieniem, a przeciąganie powoduje przewijanie strony. Dla przejrzystości kodu, napisałem funkcje MouseDown, MouseUp, MouseMove, przy czym ta ostatnia jest uruchamiana tylko na naciśniętym przycisku. Przejeżdżanie myszką nad canvas bez naciśniętego przycisku nie uruchamia żadnej funkcji, jest to celowo zrobione, w palcu nie ma odpowiednika tej czynności. Dalej poszukałem w www.google.com, jak zrealizować zdarzenia na wyświetlaczu dotykowym i podpiąłem funkcje pod zdarzenia z "touch" w nazwie, przy czym one również uruchamiają wyżej wymienione funkcje.

Działa to dobrze, ale jest pewien problem:
Jak się uruchomi wszystkie funkcje, to na smartfonie naciśnięcie przycisku i zwolnienie uruchamia się dwa razy, co nie jest pożądane. Na komputerze działa prawidłowo. Połowicznie rozwiązałem problem poprzez wprowadzenie zmiennej ConfTouch (true/false), która decyduje, czy używać funkcji dotykowych. Jak się ustawia tą zmienną na true lub na false odpowiednio do rodzaju urządzenia, to aplikacja działa w pełni prawidłowo na wszystkim, ale konieczne jest udostępnienie możliwości zmiany tego ustawienia, a więc też jest takie sobie.

W jaki sposób można automatycznie i wiarygodnie wyczuć, czy w danym przypadku bardziej odpowiednie są funkcje "dotykowe", czy "myszkowe"? Na komputerze działają tylko funkcje "myszkowe", a na smartfonie działają jedne i drugie, przy czym przy użyciu tych "dotykowych" aplikacja lepiej reaguje na dotykanie. Chodzi o to, żeby aplikacja działała i na komputerze i na smartfonie bez potrzeby ustawiania czegokolwiek, ani podwójnego wywoływania zdarzeń.

// Na komputerze - false
// Na smartfonie - true
var ConfTouch = false;

// Obiekt Canvas
var ScreenMouse = document.getElementById('ScreenMouse');

function MouseDown(X, Y)
{
    // Kod zdarzenia na naciśniecie przycisku lub palca
}

function MouseMove(X, Y)
{
    // Kod zdarzenia w czasie ruchu myszki na naciśniętym przycisku lub przyłożonego palca
}

function MouseUp(X, Y)
{
    // Kod zdarzenia na puszczenie przycisku lub palca
}

var MouseBtn = false;

function MouseDown_(Evt)
{
    if (!ConfTouch)
    {
        var _ = ScreenMouse.getBoundingClientRect();
        MouseBtn = true;
        MouseDown(Evt.clientX + window.scrollX - _.left, Evt.clientY + window.scrollX - _.top);
    }
}

function MouseMove_(Evt)
{
    if (!ConfTouch)
    {
        if (MouseBtn)
        {
            var _ = ScreenMouse.getBoundingClientRect();
            MouseMove(Evt.clientX + window.scrollX - _.left, Evt.clientY + window.scrollX - _.top);
        }
    }
}

function MouseUp_(Evt)
{
    if (!ConfTouch)
    {
        var _ = ScreenMouse.getBoundingClientRect();
        MouseUp(Evt.clientX + window.scrollX - _.left, Evt.clientY + window.scrollX - _.top);
        MouseBtn = false;
    }
}

function MouseDown_0(Evt)
{
    if ((ConfTouch) && (Evt.touches.length > 0))
    {
        document.getElementsByTagName("BODY")[0].className = "lock-screen";
        var _ = ScreenMouse.getBoundingClientRect();
        MouseBtn = true;
        MouseDown(Evt.touches[0].clientX + window.scrollX - _.left, Evt.touches[0].clientY + window.scrollX - _.top);
    }
}

function MouseMove_0(Evt)
{
    if ((ConfTouch) && (Evt.touches.length > 0))
    {
        if (MouseBtn)
        {
            var _ = ScreenMouse.getBoundingClientRect();
            MouseMove(Evt.touches[0].clientX + window.scrollX - _.left, Evt.touches[0].clientY + window.scrollX - _.top);
        }
    }
}

function MouseUp_0(Evt)
{
    if ((ConfTouch))
    {
        document.getElementsByTagName("BODY")[0].className = "";
        var _ = ScreenMouse.getBoundingClientRect();
        MouseUp(0, 0);
        MouseBtn = false;
    }
}

function MouseReset(Evt)
{
    if ((ConfTouch == 1))
    {
        document.getElementsByTagName("BODY")[0].className = "";
    }
}

ScreenMouse.addEventListener("mousedown", MouseDown_);
ScreenMouse.addEventListener("mousemove", MouseMove_);
ScreenMouse.addEventListener("mouseup", MouseUp_);
ScreenMouse.addEventListener("touchstart", MouseDown_0);
ScreenMouse.addEventListener("touchmove", MouseMove_0);
ScreenMouse.addEventListener("touchend", MouseUp_0);
ScreenMouse.addEventListener("touchcancel", MouseReset);

Klasa lock-screen w CSS ma taką definicję:

   .lock-screen {
    height: 100%;
    overflow: hidden;
    width: 100%;
    //position: fixed;
   }
1

Ja bym spróbował wykryć na jakim urządzeniu odpalana jest strona i przetestowałbym kilka funkcji ze stackoverflow xd, albo drugi lekko gorszy sposób to przypisywanie eventów na podstawie wielkości ekranu i tutaj najlepiej skorzystać z metody matchMedia obiektu window.

0
Xarviel napisał(a):

Ja bym spróbował wykryć na jakim urządzeniu odpalana jest strona i przetestowałbym kilka funkcji ze stackoverflow xd, albo drugi lekko gorszy sposób to przypisywanie eventów na podstawie wielkości ekranu i tutaj najlepiej skorzystać z metody matchMedia obiektu window.

Większośc propozycji opiera się na analizie tekstu "navigator.userAgent". Była jedna propozycja oprarta na sprawdzeniu "navigator.maxTouchPoints" i w moim przypadku sprawdzenie "navigator.maxTouchPoints" zadziałało (w załączonym pliku automatycznie ustawia opcję i dobrze trafia). Czy to można uznać za najlepsze rozwiązanie? Jakby nie było, ekran niedotykowy nie posiada punktów dotykowych.

W przypadku myszki, jak się wyjedzie z canvas, to odpala się "mouseout" i da się to obsłużyć. Natomiast teoretycznie jest "touchcancel", ale coś u mnie nie działa, bo można przeciągać poza obiektem. Teoretycznie można poprzez analizę współrzędnych, czy jest >=0 i w ramach obiektu, ale mi chodzi o to, co zapewnia API. Jak wyczuć wyjechanie poza obiekt, ale nie poprzez analize współrzędnych, tylko jaki event się wtedy uruchamia? Czy pozostaje jedynie analiza współrzędnych i jak ruch będzie poza dozwolony obszar, to wtedy uruchomić puszczenie, bo nastąpił wyjazd poza ten obszar?

Załączam kompletny plik HTML z testerem, który z założenia ma działać tak:

  1. Naciśnięcie to namalowanie czerwonego koła.
  2. Przeciągnięcie to namalowanie czarnych kropek.
  3. Puszczenie to namalowanie zielonego koła.

Jak widać, miejsce puszczenia palca uzyskałem poprzez zapamiętanie miejsca ostatniego przyłożenia lub przemieszczenia. Ale jak odczytać miejsce puszczenia palca tak samo, jak miejsce puszczeni myszki, skoro tablica "Evt.touches" jest pusta?

Jeżeli obiekt jest wewnątrz jakiegoś div, to dlaczego on nie uruchamia zdarzenia touchmove, a jak nie jest wewnątrz div, to uruchamia? W przypadku myszki nie ma tego problemu.

Tutaj kompletny HTML

<!DOCTYPE html>
<html> 
    <head>
        <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
        <meta http-equiv="X-UA-Compatible" content="IE=edge"/> 
        <meta charset="UTF-8">
        <title></title>
        <script>
var Canvas;
var CanvasCtx;

var CanvasW = 320;
var CanvasH = 240;

function Test(N, X)
{
    document.getElementById('Test' + N).textContent = "[" + X + "]";
}

function Init()
{
    document.getElementById('Screen').style["width"] = CanvasW + "px";
    document.getElementById('Screen').style["height"] = CanvasH + "px";

    Canvas = document.getElementById('ScreenC');
    CanvasCtx = Canvas.getContext('2d');
    
    Canvas.width = CanvasW;
    Canvas.height = CanvasH;

    document.getElementById('Test1').textContent = "[" + navigator.userAgent + "]";
    document.getElementById('Test2').textContent = "[" + navigator.maxTouchPoints + "]";
    if (navigator.maxTouchPoints > 0)
    {
        document.getElementById('TouchScreen').selectedIndex = 1;
    }
    else
    {
        document.getElementById('TouchScreen').selectedIndex = 0;
    }
    
    SetConfTouch();
}

function MouseDown(X, Y)
{
    CanvasCtx.strokeStyle = "red";
    CanvasCtx.fillStyle = "red";
    CanvasCtx.beginPath();
    CanvasCtx.arc(X, Y, 20, 0, 2 * Math.PI);
    CanvasCtx.fill();
    Test(3, "Down  " + Math.round(X) + "_" + Math.round(Y));
}

function MouseMove(X, Y)
{
    CanvasCtx.strokeStyle = "black";
    CanvasCtx.fillStyle = "black";
    CanvasCtx.beginPath();
    CanvasCtx.arc(X, Y, 5, 0, 2 * Math.PI);
    CanvasCtx.fill();
    Test(3, "Move  " + Math.round(X) + "_" + Math.round(Y));
}

function MouseUp(X, Y)
{
    CanvasCtx.strokeStyle = "green";
    CanvasCtx.fillStyle = "green";
    CanvasCtx.beginPath();
    CanvasCtx.arc(X, Y, 20, 0, 2 * Math.PI);
    CanvasCtx.fill();
    Test(3, "Up    " + Math.round(X) + "_" + Math.round(Y));
}
        </script>
        <style>
            .lock-screen {
                height: 100%;
                overflow: hidden;
                width: 100%;
                //position: fixed;
            }
        </style>
    </head> 
    <body>
        <span id="Test1"></span><br>
        <span id="Test2"></span><br>
        <span id="Test3"></span><br>
        <!--
        <span id="Test4"></span><br>
        <span id="Test5"></span><br>
        <span id="Test6"></span><br>
        <span id="Test7"></span><br>
        <span id="Test8"></span><br>
        <span id="Test9"></span><br>
        -->
        <div>
            <select id="TouchScreen" onchange="SetConfTouch()">
                <option>Mouse</option>
                <option>Touch</option>
            </select>
        </div>

        <div id="Screen" style="width:100px; height:100px;overflow:auto;border-style:solid; position: relative;">
            <canvas id="ScreenC" style="border: 0px solid #000000; position: absolute; left:0px; top:0px; display: inline;"></canvas>
            <div id="ScreenMouse" style="background-color:#000000; opacity: 0.00; position: absolute; left:0px; top:0px; right:0px; bottom:0px">
        </div>
        <script>
var ConfTouch = 0;

function SetConfTouch()
{
    ConfTouch = document.getElementById('TouchScreen').selectedIndex;
}

var ScreenX = document.getElementById('Screen');
var ScreenMouse = document.getElementById('ScreenMouse');
var MouseBtn = false;
var MouseX;
var MouseY;

function MouseDown_(Evt)
{
    if (ConfTouch == 0)
    {
        var _ = ScreenMouse.getBoundingClientRect();
        MouseBtn = true;
        MouseDown(Evt.clientX + window.scrollX - _.left, Evt.clientY + window.scrollX - _.top);
    }
}

function MouseMove_(Evt)
{
    if (ConfTouch == 0)
    {
        if (MouseBtn)
        {
            var _ = ScreenMouse.getBoundingClientRect();
            MouseMove(Evt.clientX + window.scrollX - _.left, Evt.clientY + window.scrollX - _.top);
        }
    }
}

function MouseUp_(Evt)
{
    if (ConfTouch == 0)
    {
        if (MouseBtn)
        {
            var _ = ScreenMouse.getBoundingClientRect();
            MouseUp(Evt.clientX + window.scrollX - _.left, Evt.clientY + window.scrollX - _.top);
            MouseBtn = false;
        }
    }
}

function MouseDown_0(Evt)
{
    if ((ConfTouch == 1) && (Evt.touches.length > 0))
    {
        document.getElementsByTagName("BODY")[0].className = "lock-screen";
        var _ = ScreenMouse.getBoundingClientRect();
        MouseBtn = true;
        MouseX = Evt.touches[0].clientX + window.scrollX - _.left;
        MouseY = Evt.touches[0].clientY + window.scrollX - _.top;
        MouseDown(Evt.touches[0].clientX + window.scrollX - _.left, Evt.touches[0].clientY + window.scrollX - _.top);
    }
}

function MouseMove_0(Evt)
{
    if ((ConfTouch == 1) && (Evt.touches.length > 0))
    {
        if (MouseBtn)
        {
            var _ = ScreenMouse.getBoundingClientRect();
            MouseX = Evt.touches[0].clientX + window.scrollX - _.left;
            MouseY = Evt.touches[0].clientY + window.scrollX - _.top;
            MouseMove(Evt.touches[0].clientX + window.scrollX - _.left, Evt.touches[0].clientY + window.scrollX - _.top);
        }
    }
}

function MouseUp_0(Evt)
{
    if ((ConfTouch == 1))
    {
        document.getElementsByTagName("BODY")[0].className = "";
        if (MouseBtn)
        {
            var _ = ScreenMouse.getBoundingClientRect();
            MouseUp(MouseX, MouseY);
            MouseBtn = false;
        }
    }
}

ScreenMouse.addEventListener("mousedown", MouseDown_);
ScreenMouse.addEventListener("mousemove", MouseMove_);
ScreenMouse.addEventListener("mouseup", MouseUp_);
ScreenMouse.addEventListener("mouseout", MouseUp_);
ScreenX.addEventListener("touchstart", MouseDown_0);
ScreenX.addEventListener("touchmove", MouseMove_0);
ScreenX.addEventListener("touchend", MouseUp_0);
ScreenX.addEventListener("touchcancel", MouseUp_0);


Init();
        </script>

    </body> 
</html> 
1

Większośc propozycji opiera się na analizie tekstu "navigator.userAgent". Była jedna propozycja oprarta na sprawdzeniu "navigator.maxTouchPoints" i w moim przypadku sprawdzenie "navigator.maxTouchPoints" zadziałało (w załączonym pliku automatycznie ustawia opcję i dobrze trafia). Czy to można uznać za najlepsze rozwiązanie? Jakby nie było, ekran niedotykowy nie posiada punktów dotykowych.

Jeśli działa to tak, według mnie możemy to uznać. Wykrywanie urządzenia po stronie JavaScriptu nie należy do najprostszych, bo nie ma takiego standardowego sposobu, żeby to zrobić, oraz nie wszystkie przeglądarki to wspierają, lub udostępniają potrzebne informacje.

W przypadku myszki, jak się wyjedzie z canvas, to odpala się "mouseout" i da się to obsłużyć. Natomiast teoretycznie jest "touchcancel", ale coś u mnie nie działa, bo można przeciągać poza obiektem

Sprawdziłem przykład z tym touchcancel i odpala się on czasami w dziwnych momentach jak kopiowanie tekstu, albo klikanie kilkoma palcami, więc działa trochę inaczej niż mouseout i nie wiem czy korzystałbym z tego w tej sytuacji.

Teoretycznie można poprzez analizę współrzędnych, czy jest >=0 i w ramach obiektu, ale mi chodzi o to, co zapewnia API. Jak wyczuć wyjechanie poza obiekt, ale nie poprzez analize współrzędnych, tylko jaki event się wtedy uruchamia? Czy pozostaje jedynie analiza współrzędnych i jak ruch będzie poza dozwolony obszar, to wtedy uruchomić puszczenie, bo nastąpił wyjazd poza ten obszar?

Powinien się odpalać event touchend i trzeba raczej zrobić warunek tak jak mówisz, który sprawdzałby współrzędne.

0
Xarviel napisał(a):

Większośc propozycji opiera się na analizie tekstu "navigator.userAgent". Była jedna propozycja oprarta na sprawdzeniu "navigator.maxTouchPoints" i w moim przypadku sprawdzenie "navigator.maxTouchPoints" zadziałało (w załączonym pliku automatycznie ustawia opcję i dobrze trafia). Czy to można uznać za najlepsze rozwiązanie? Jakby nie było, ekran niedotykowy nie posiada punktów dotykowych.

Jeśli działa to tak, według mnie możemy to uznać. Wykrywanie urządzenia po stronie JavaScriptu nie należy do najprostszych, bo nie ma takiego standardowego sposobu, żeby to zrobić, oraz nie wszystkie przeglądarki to wspierają, lub udostępniają potrzebne informacje.

https://caniuse.com/?search=navigator.maxTouchPoints - Wygląda na to, że wszystkie najważniejsze przeglądarki obsługują "navigator.maxTouchPoints". Jeżeli robię apkę na własny użytek, to nie ma problemu. Czy jednak dobrze wnioskuję, że z takim czymś może iść "w świat'? Jeżeli przeglądarka nie obsługuje, to chyba nie wywali się, tylko zwróci wartość "undefined", która spowoduje uruchomienie wersji dla myszki (bo to chyba nie prawda, że "undefined > 0"). Skoro "navigator.maxTouchPoints" wydaje się być pewniejsze, to skąd taki nacisk na analizę UserAgent (wydaje się być to bardzo niepewne, bo co jeden to inaczej, nie ma najlepszego sprawdzonego sposobu rozstrzygania, czy jest dotyk na podstawie UserAgent).

Czy jest jakaś przeglądarka na komputer (Linux lub ewentualnie Windows), w której dobrze działa symulacja dotyku jednym palcem za pomocą myszki (nie potrzebuję obsługiwać wielodotyku)? Na Firefox teoretycznie jest taka funkcja w widoku responsywnym, ale w praktyce nie działa tak samo, jak na smartfonie, w Edge nie mogę takowej znaleźć. Zakładam, że niejeden już to wie od dawna i nie muszę instalować wszystkich istniejących przeglądarek tylko po to, żeby sprawdzić działanie symulacji dotyku za pomocą myszki. A przerzucanie co chwile na telefon co chwile tylko po to, żeby sprawdzić zachowanie tak średnio mi pasuje, ale jest do przejścia, ewentualnie uruchomię serwer WWW i tam będę przerzucać stronę. Jednak, już pisałem w innym temacie, że w telefonie odświeżanie strony s podłączonymi zewnętrznymi plikami *.js jest problematyczne, zwykłe odświeżanie niezupełnie odświeża, dopiero wchodzenie do ustawień i czyszczenie cache rozwiązuje problem. W komputerze wciskam Ctrl+F5 i tyle, mam pewność, ze wszystko jest odświeżone.

0

Jeśli nie ma jakieś właściwości np navigator.alaMaKota to domyślnie zwróci undefined, więc tym się nie musisz martwić.

Nacisk na userAgent pojawił się pewnie dlatego, że w tamtym okresie nie było innej alternatywy. W tym linku co podrzuciłem są wpisy/odpowiedzi z 2013, 2014 roku, a wtedy JavaScript wyglądał inaczej i ludzie przejmowali się jakimś Internet Explorer, gdzie teraz się to kompletnie olewa w 99% przypadków i lepiej zaoszczędzić kilka kB na wadze skryptu dla innych przeglądarek.

O przeglądarce obsługującej dotyk na PC nic nie wiem, ale możesz sobie skonfigurować serwer z automatycznym odświeżaniem podglądu po edycji plików. Jest sporo przykładów jak to zrobić za pomocą npm. Ewentualnie jakiś XAMPP w roli serwera do przenoszenia plików.

1 użytkowników online, w tym zalogowanych: 0, gości: 1