Execute JavaScript in Android Kotlin WebView to find an element’s coordinates on the screen

I have an Android app which I am creating with the Kotlin language. I am trying to get a specific <div> element within the webpage I am displaying and then put the coordinates of the element into Kotlin variables. In my case, I’m displaying a YouTube page and trying to get the coordinates of the video player (I will be later displaying a button over it). I’m using the following code to initialize my WebView:

mainWebView.webViewClient = CustomWebViewClient(...) // This custom class doesn't do anything other than check if the user leaves the YT website.
mainWebView.webChromeClient = WebChromeClient()
mainWebView.settings.javaScriptEnabled = true
mainWebView.settings.domStorageEnabled = true

Then, I load the page:

mainWebView.loadUrl("https://www.youtube.com/watch?app=desktop&v=lh7WIW3pcso")

Next, I execute some JavaScript on the click of a specific button and display the result. I tested the JavaScript code in my browser’s console and it worked fine. (It gave the result of {"x":0,"y":56}.)

mainWebView.evaluateJavascript(
    "(function() { var element = document.getElementById('full-bleed-container'); var rect = element.getBoundingClientRect(); return JSON.stringify({ x: rect.left, y: rect.top }); })();"
) { result ->

    if (result == "null") {
        Toast.makeText(this, "NULL!", Toast.LENGTH_SHORT).show()
    } else {
        val coordinates = JSONObject(result)
        val x = coordinates.getDouble("x")
        val y = coordinates.getDouble("y")
        Toast.makeText(this, "Coordinates: X: $x, Y: $y", Toast.LENGTH_SHORT).show()
    }
}

However, my app crashes with the following exception: org.json.JSONException: Value {"x":0,"y":56} of type java.lang.String cannot be converted to JSONObject.

I also checked to see if the page wasn’t done loading with the following code (and I received the Toast saying it was done):

    override fun onPageFinished(view: WebView?, url: String?) {
        super.onPageFinished(view, url)

        Toast.makeText(context, "Loaded '$url'!", Toast.LENGTH_SHORT).show()
    }

First of all, why isn’t the JSON response string being converted into the JSONObject successfully, and secondly, the app shouldn’t be giving the same JSON response on different device sizes, should it? Not sure what’s going on here, but I need to get the coordinates in such a format that I can display a button over that <div> element.

why is app jsx not displaying everything?

i am trying to write a react code but
app .jsx doesn’t display anything except the (♥) no matter what i do

here is the first file……….
moviecard.jsx

  import React from "react"
function Moviecard (Movie) {
   function onFavouriteClick () {
    alert("clicked")
   }
return (
    <div className="movie-card">

        <div className="movie-poster">
        <img src={Movie.url} alt={Movie.title} />

            <div className="movie-overlay">
        <button className="favourite-btn" onClick={onFavouriteClick} >
        ♥
        </button>
             </div>

        </div>
        <div className="movie-info">
            <h3>{Movie.title}</h3>
            <p>{Movie.release_date}</p>
        </div>
    </div>
)
}

export default Moviecard

second file………..
home.jsx
…………………………………

    import React from "react";
import Moviecard from "../components/Moviecard";
function Home() {
 const movies = [
        {id: 1, title: "john wick", release_date:"2020"},
        
        {id: 2, title: "john wick 2", release_date:"2021"},
        
        {id: 3, title: "john wick 3", release_date:"2022"},
        
        {id: 4, title: "john wick 4", release_date:"2023"},
    ]
 return (
    <div className="home">
        <div className="movies-grid">
            {movies.map((Movie) => ( 
            <Moviecard key={Movie.id}/>
            ))}
        </div>
    </div>
    )
}

export default Home;

third file……..
app.jsx
……………………………….

    import React from "react";
import './App.css';
import Home from "./pages/home";
function App() {
  return (
  <>
  <Home/>
  </>
  )
};
export default App;

……..

Eraser.js on Readymag

I’m trying to add this code above my website on readymag. It places an image above the website that can be erased, but when I insert the code, the website doesn’t scroll. I tried using pointer-events: none;, and while it allows scrolling, the eraser function doesn’t work. Can someone help me?

HTML

       <div class="wrapper">
            
            <img id="img2" src="https://freight.cargo.site/t/original/i/Z2283582099087810873620708068841/stelline-rettangolo.svg"> </div>        


    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://freight.cargo.site/m/K2262565859332669963966899162601/jquery.eraser.js"></script>
    <script type="text/javascript">
        
        $('#img2').eraser({ size: 200 });

    </script>

CSS

.wrapper {
    position: relative;
}

#img2 {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100vh;
    object-fit: cover;
}

#img2 {
    z-index: 99;
}

SVG & Web Responsivity

this took me too long so I come here for help. I want to have multiple lines like this, connected to the text. But they dont on other monitor sizes. Anyone who can help??

:root {
  --color-background: black;
  --color-on-background: white;
}

body {
  width: 100vw;
  height: auto;
  background-color: var(--color-background);
}

.hero_page {
  width: 100vw;
  height: 100vh;
  display: flex;
  flex-flow: column nowrap;
  justify-content: center;
  align-items: center;
}

.hero_page_title {
  font-family: title;
  font-size: 25vh;
  fill: var(--color-on-background);
  text-align: center;
  text-anchor: middle;
  dominant-baseline: middle;
}

.line {
  fill: none;
  stroke: var(--color-on-background);
  stroke-width: 0.3vh;
}

.filled {
  fill: var(--color-on-background);
  stroke: none;
}
<html>

<body>
  <div class="hero_page">
    <svg width="100vw" height="100vh" viewbox="0 0 2000 1000" preserveAspectRatio="xMidYMid meet">
      <polyline class="line" points="0,170 300,170 320,192 370,192 390,170 600,170 691,400"></polyline>
      <text class="hero_page_title" x="50%" y="50%">OSKAR KOPČIL</text>
    </svg>
  </div>
</body>

</html>

enter image description here

D3.js interpolate coordinates between two points forming an arc instead of a straight line

My goal is simple, I want to add direction markers on a path (like so).
The code that I’ve implemented gives the result on the initial load.

lineCoords.forEach(coords => {

    for (let i = 1; i < coords.length; i++) {
      const A = (coords[i - 1]);
      const B = (coords[i]);

      const interpolateA = d3.interpolate(A[0], B[0]);
      const interpolateB = d3.interpolate(A[1], B[1]);

      const spacing = 3;
      const distance = Math.hypot(A[0] - B[0], A[1] - B[1]);
      const numPoints = Math.max(1, Math.floor(distance / spacing));
      const range = d3.range(1, numPoints + 1);
      const points = range.map(j => {
        const t = j / (numPoints + 1);
        const interpolatedA = interpolateA(t);
        const interpolatedB = interpolateB(t);

        return [interpolatedA, interpolatedB];
      });

      onewayCoords.push(points);
    }

  })

However, when I zoom the map, the markers form an arc instead of a straight line.
Since the code above runs inside the drag event, I don’t think that’s the issue.
I suspect the problem is related to the coordinates not being precise.

If there’s a better way to achieve the desired result, please share your insights.

My browser is not showing some of the content i write inside my ejs file

i am working on a full stack web developement project and i’m stuck where localhost:3000 in my browser can’t show the before and after mentioned in the layout.ejs file, which are outside the <%- body %> code. The localhost:3000 can show what’s inside the index.ejs file which have been included in the <%- body %> code but it can’t show what’s outside <%- body ->

code inside my layout.ejs file

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Mybrary</title>
</head>
<body>
    Before
    <br>
    <%- body %>
    <br>
    After
</body>
</html>

code inside my index.ejs file

Middle

code inside my server.js file

const express = require('express')
const app = express()
const expressLayouts = require('express-ejs-layouts')

const indexRouter = require('./routes/index')

app.set('view engine', 'ejs')
app.set('views', __dirname + '/views')
app.set('layouts', 'layouts/layout')

app.use(express.static('public'))

app.use('/', indexRouter)
app.use(expressLayouts)

app.listen(process.env.PORT || 3000)

React 19’s use hook is running in an endless loop but should only run once [duplicate]

I am using react 19’s use function, but the problem that I am running into is that it seems to be called in an endless loop, and I am not sure why or what I am doing wrong.

I created an async function that makes an http request to my graphql server. The response returns a 200 with the correct data, but then the console.log never executes, and the fetch gets called endlessly making lots of http requests.

Stackblitz Example (See console for output).

// The http request
export const getDistance = async (start, end) => {
  const res = await fetch(environment.hasura.v2.url, {
    method: 'POST',
    headers: { /* API Headers */ },
    body: JSON.stringify({ query: /* Graphql query */, variables: { start, end } }),
  });
  const json = await res.json();
  return json.data;
};

// The header to display
export default function UserHeader() {
  const distance = use(getDistance(1, 20));
  console.log('distance', distance);

  return <>{distance}</>;
}

// The page root
export default function App() {
  return (
    <Suspense fallback={<>Loading...</>}>
      <UserHeader />
    </Suspense>
  );
}

I thought that this was because of re-rendering but if I remove the use function the console.log only displays twice (due to strict mode):

export default function UserHeader() {
  console.log('distance');
}

What is causing this endless call?

how do I configure Typescript transpiled to Javascript to run in the browser?

I’m converting a large set of JavaScript files into Typescript and the result will run in the browser. I few years ago I got this all working with require.js and the module format AMD but now I’m reading that’s deprecated. What is the current thinking about how to do this? StackOverflow recommended some other posts which were many years old. Bonus points if I can obfuscate the JavaScript file(s).

Thanks, Dennis

Selenium. Python. Followers list on Instagram’s pop-up window doesn’t scroll down

Uses:

python = “^3.12.7”
selenium = “^4.30.0”

Initialize variable “driver”:

from time import sleep
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.select import Select
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By


driver = webdriver.Chrome()
driver.get('https://www.instagram.com/')
sleep(3)

#   *   *   *
#   Sign in code here
#   *   *   *

#  chose user account for example:
driver.get('https://www.instagram.com/geeks_for_geeks/followers/')
sleep(3)

driver.find_element(By.PARTIAL_LINK_TEXT, 'followers').click()
print('4')

#  set followers pop-up list to new variable (i checked three XPATH based on the page code source, see img below):
#  pop_up_window = driver.find_element(By.XPATH, '/html/body/div[4]/div[2]/div/div/div[1]/div/div[2]/div/div/div/div/div[2]/div/div/div[3]')
#  pop_up_window = driver.find_element(By.XPATH, '/html/body/div[4]/div[2]/div/div/div[1]/div/div[2]/div/div/div/div/div[2]/div/div/div[3]/div[1]')
pop_up_window = driver.find_element(By.XPATH, '/html/body/div[4]/div[2]/div/div/div[1]/div/div[2]/div/div/div/div/div[2]/div/div/div[3]/div[1]/div')

Chrome page code

then i uses few difference variants for try to scroll down:

1st. – uses ActionChains from selenium.webdriver.common.action_chains:

num_chains = 10
for _ in range(num_chains):
    print('=========== Chains Start =================')
    ActionChains(driver).send_keys(Keys.END).perform()

2nd. – uses javascript:

v2.1

while True:
    driver.execute_script("arguments[0].scrollTop = arguments[0].scrollHeight", pop_up_window)
    sleep(2)

v2.2

while True:
    driver.execute_script('arguments[0].scrollTop = arguments[0].scrollTop + arguments[0].offsetHeight;', pop_up_window)
    sleep(2)

v2.3

num_chains = 10
followers_step = 10
for _ in range(num_chains):
   driver.execute_script("arguments[0].scrollTop = arguments[1]", pop_up_window, followers_step)
   followers_step += followers_step
   sleep(3)

finally i get followers pop-up window by which i should make scroll down but the slider for scrolling to the right doesn’t appear and the user-list does not scroll, on the output i always get the same result that was immediately opening at followers pop-up list (noticed that when the scrolling process starts – the closing button is highlighted always):
followers pop-up

and I always get the same result without scrolling:
result without scrolling

p.s.: answers: this and this – already discussed at stackoverflow did not help

How to properly cache SVG filters for constant reuse in a canvas element?

I have an SVG filter file in /filters/filter.svg that contains a SVG filter with ID filter-id.

I apply this filter in an HTML5 canvas by using context.filter = “url(/filters/filter.svg#filter-id)”; and it works.

However, the filter gets loaded every time it is used, which happens a lot since I have to constantly animate many individually filtered images in the canvas.

What is the best way, using Javascript only, to cache the filter file once, then retrieve the filter from cache every time it is used, instead of having it loaded again?

It would be preferable that the filter not be embedded or even declared in the HTML file that loads the canvas, since there are actually many filters stored in the filters folder and used simultaneously, to keep the whole process cleaner and more dynamic.

Some basic sample code showing how to cache and retrieve would be highly appreciated.

Adding points to a plot uPlot

I have a website displaying live data using uplot, because of its low resource usage even with many points per second being added. The issue is that I want the newest datapoint to have a big red point on it so it ‘traces’ the line. My opts section for the u plot looks like this:

const opts = {
    title: "Live-Data",
    width: window.innerWidth * 0.65,
    height: window.innerHeight * 0.8,
    cursor: {
        drag: { setScale: false },
        sync: { key: 'shift' },
    },
    scales: {
        x: { time: true },
        y: { auto: true}  
    },
    series: [
        {},  
        {
            label: "Sensor 1 (mm)",
            stroke: "blue",
            value: (u, v) => v == null ? null : v.toFixed(2) + " mm"
        },
        {
            label: "Sensor 2 (mm)",
            stroke: "red",
            value: (u, v) => v == null ? null : v.toFixed(2) + " mm"
        }
    ],
    axes: [
        {
            scale: "x",
            grid: { show: true },
            values: (u, vals) => vals.map(v => new Date(v).toLocaleTimeString())
        },
        {
            scale: 'y',
            values: (u, vals, space) => vals.map(v => v.toFixed(2) + " mm"),
            grid: { show: true }
        }
    ]
};

But I haven’t found a way to add a point that also disappears (I guess that is the logical for a tracing point) when a new data point is added.

Thanks for any help

Array.shift function removing item at random index

I am trying to create a blackjack game. This is the shuffling and dealing part of the game. The deck array contains all the 52 cards of the deck (suits for the cards will be added).
This array is passed into the shuffle function which uses the Fisher Yates shuffle algorithm.

let deck = [{face:'2',value:2},{face:'3',value:3},{face:'4',value:4},{face:'5',value:5},{face:'6',value:6},{face:'7',value:7},{face:'8',value:8},{face:'9',value:9},{face:'10',value:10},{face:'J',value:10},{face:'Q',value:10},{face:'K',value:10},{face:'A',value:10,value2:11,value3:1},{face:'2',value:2},{face:'3',value:3},{face:'4',value:4},{face:'5',value:5},{face:'6',value:6},{face:'7',value:7},{face:'8',value:8},{face:'9',value:9},{face:'10',value:10},{face:'J',value:10},{face:'Q',value:10},{face:'K',value:10},{face:'A',value:10,value2:11,value3:1},{face:'2',value:2},{face:'3',value:3},{face:'4',value:4},{face:'5',value:5},{face:'6',value:6},{face:'7',value:7},{face:'8',value:8},{face:'9',value:9},{face:'10',value:10},{face:'J',value:10},{face:'Q',value:10},{face:'K',value:10},{face:'A',value:10,value2:11,value3:1},{face:'2',value:2},{face:'3',value:3},{face:'4',value:4},{face:'5',value:5},{face:'6',value:6},{face:'7',value:7},{face:'8',value:8},{face:'9',value:9},{face:'10',value:10},{face:'J',value:10},{face:'Q',value:10},{face:'K',value:10},{face:'A',value:10,value2:11,value3:1}]
let userHand = []
let dealerHand = []
function shuffle(array) {
    let currentIndex = array.length;
  
    // While there remain elements to shuffle...
    while (currentIndex != 0) {
  
      // Pick a remaining element...
      let randomIndex = Math.floor(Math.random() * currentIndex);
      currentIndex--;
  
      // And swap it with the current element.
      [array[currentIndex], array[randomIndex]] = [
        array[randomIndex], array[currentIndex]];
    }
  }
  
shuffle(deck)
console.log(deck)

function deal(){
    userHand.push(deck.shift()); // Removes and gives first card
    dealerHand.push(deck.shift()); // Removes and gives second card
    userHand.push(deck.shift()); // Removes and gives third card
    dealerHand.push(deck.shift()); // Removes and gives fourth card
    console.log(userHand, dealerHand)
    console.log(deck)
}
deal()

The deal function is where I’m having problems. The goal is for the card at index[0] of the shuffled deck to be taken out and added to the userHand array. Then the next card at index [0] to be added to dealerHand array, until user and dealer have 2 cards each.

Instead, this is not happening. Instead of deck.shift() removing the index[0] obj from the deck, it removes an object at a random index and adds it to the userHand/dealerHand arrays.

When I read message in a perticular chat it’s working but I can not read all chat list

import {
  collection,
  addDoc,
  serverTimestamp,
  query,
  orderBy,
  onSnapshot,
  getDocs,
} from "firebase/firestore";
import { db } from "./firebase";

export const getMessages = () => {
  const messagesRef = collection(
    db,
    "envs/PROD/hotels/Hotel_001/chats/Chat_001/messages" '// this is working
  );
  const q = query(messagesRef);
  return onSnapshot(q, (snapshot: any) => {
    const messages = snapshot.docs.map((doc: any) => ({
      id: doc.id,
      ...doc.data(),
    }));
    console.log(messages); // [{text: 'hi'}, {text: 'great'}...]
  });
};

export const getChats = () => {
  const messagesRef = collection(
    db,
    "envs/PROD/hotels/Hotel_001/chats" '// this is not working
  );
  const q = query(messagesRef);
  return onSnapshot(q, (snapshot: any) => {
    const messages = snapshot.docs.map((doc: any) => ({
      id: doc.id,
      ...doc.data(),
    }));
    console.log(messages); // []
  });
};

Can’t draw any function graph [closed]

I have this math application that’s supposed to help me improve in my understanding of functions and their graphs but unfortunately I know but too little programming skills to help me get the job done.

I can pretty much get the screen and the grid with X and Y axis to display but when I touch my phone through Expo Go (I’m on VS Code) nothing shows (not a single point or curve).

I’ve tried ChatGPT, Copilot and bolt.new but nothing worked so far.

import React, { useState, useRef, useEffect } from 'react';
import { View, Text, StyleSheet, Dimensions, PanResponder, Alert } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import Svg, { Line, Circle } from 'react-native-svg';
import Animated, { useAnimatedStyle, withSpring, useSharedValue } from 'react-native-reanimated';
import { GestureHandlerRootView } from 'react-native-gesture-handler';

// Liste des fonctions mathématiques disponibles
const FUNCTIONS = [
  { name: 'f(x) = x', formula: (x: number) => x },
  { name: 'f(x) = -x', formula: (x: number) => -x },
  { name: 'f(x) = x²', formula: (x: number) => x * x },
  { name: 'f(x) = x³', formula: (x: number) => x * x * x },
  { name: 'f(x) = exp(x)', formula: (x: number) => Math.exp(x) },
  { name: 'f(x) = 1/x', formula: (x: number) => (x !== 0 ? 1 / x : NaN) }, // Avoid division by zero
  { name: 'f(x) = ln(x)', formula: (x: number) => (x > 0 ? Math.log(x) : NaN) }, // Avoid log of non-positive numbers
];

// Dimensions de l'écran
const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');

// Paramètres pour le quadrillage
const GRID_SIZE = 20; // Nombre de lignes/colonnes dans le quadrillage
const SCALE = 40; // Échelle pour convertir les coordonnées

export default function PracticeScreen() {
  // État pour les fonctions mélangées
  const [shuffledFunctions, setShuffledFunctions] = useState(FUNCTIONS);

  // Index de la fonction mathématique actuellement sélectionnée
  const [currentFunction, setCurrentFunction] = useState(0);

  // Points tracés par l'utilisateur
  const [points, setPoints] = useState<{ x: number; y: number }[]>([]);

  // Indique si l'utilisateur est en train de dessiner
  const [drawing, setDrawing] = useState(false);

  // Temps restant pour le round (en secondes)
  const [timeLeft, setTimeLeft] = useState(120); // 2 minutes

  // Score de l'utilisateur (utilise Reanimated pour les animations)
  const score = useSharedValue(0);

  const [showCorrectCurve, setShowCorrectCurve] = useState(false);

  // Timer pour gérer les rounds
  useEffect(() => {
    const interval = setInterval(() => {
      setTimeLeft((prev) => {
        if (prev <= 1) {
          clearInterval(interval); // Arrête le timer quand le temps est écoulé
          endRound(); // Termine le round
          return 0;
        }
        return prev - 1; // Diminue le temps restant
      });
    }, 1000); // Décrémente toutes les secondes

    return () => clearInterval(interval); // Nettoie le timer quand le composant est démonté
  }, []);

  // Fonction pour terminer un round
  const endRound = () => {
    setShowCorrectCurve(true); // Affiche la courbe correcte
    Alert.alert('Round terminé', `Votre score : ${Math.round(score.value)}`);
    setPoints([]); // Réinitialise les points
    setCurrentFunction((prev) => (prev + 1) % shuffledFunctions.length); // Passe à la fonction suivante
    setTimeLeft(120); // Réinitialise le timer
  };

  // Style animé pour le score (agrandit le texte si le score est élevé)
  const scoreStyle = useAnimatedStyle(() => {
    return {
      transform: [{ scale: withSpring(score.value > 80 ? 1.2 : 1) }],
    };
  });

  // Gestion des interactions tactiles (PanResponder)
  const panResponder = useRef(
    PanResponder.create({
      onStartShouldSetPanResponder: () => true, // Active le PanResponder au début du toucher
      onPanResponderGrant: () => {
        setDrawing(true); // Commence le dessin
        setPoints([]); // Réinitialise les points pour un nouveau tracé
        setShowCorrectCurve(false); // Cache la courbe correcte
      },
      onPanResponderMove: (_, gestureState) => {
        if (drawing) {
          const { moveX, moveY } = gestureState;
          const canvasX = moveX - SCREEN_WIDTH / 2; // Décalage pour centrer sur l'axe X
          const canvasY = SCREEN_HEIGHT / 2 - moveY; // Décalage pour centrer sur l'axe Y

          const newPoint = { x: canvasX / SCALE, y: canvasY / SCALE }; // Convertit en coordonnées du graphe
          setPoints((prev) => [...prev, newPoint]);
        }
      },
      onPanResponderRelease: () => {
        setDrawing(false); // Arrête le dessin
        if (points.length >= 5) {
          setShowCorrectCurve(true); // Affiche la courbe correcte
          calculateScore(); // Calcule le score après le tracé
        }
      },
    })
  ).current;

  // Fonction pour calculer le score
  const calculateScore = () => {
    if (points.length === 0) {
      score.value = 0; // No points, score is 0
      return;
    }
  
    const actualPoints = points.map((p) => ({
      x: p.x,
      y: shuffledFunctions[currentFunction].formula(p.x),
    }));
  
    const distances = points.map((p, i) => {
      const actualY = actualPoints[i]?.y; // Ensure the point exists
      if (actualY === undefined || isNaN(actualY)) return Infinity; // Avoid errors
      return Math.abs(p.y - actualY);
    });
  
    const avgDistance = distances.reduce((a, b) => a + b, 0) / distances.length;
    const newScore = Math.max(0, 100 - avgDistance * 20);
    score.value = newScore;
  };

  // Fonction pour dessiner le quadrillage
  const renderGrid = () => {
    const lines = [];

    // Lignes verticales
    for (let x = -GRID_SIZE; x <= GRID_SIZE; x++) {
      lines.push(
        <Line
          key={`v-${x}`}
          x1={x * SCALE + SCREEN_WIDTH / 2}
          y1={0}
          x2={x * SCALE + SCREEN_WIDTH / 2}
          y2={SCREEN_HEIGHT}
          stroke="#e0e0e0"
          strokeWidth="1"
        />
      );
    }

    // Lignes horizontales
    for (let y = -GRID_SIZE; y <= GRID_SIZE; y++) {
      lines.push(
        <Line
          key={`h-${y}`}
          x1={0}
          y1={y * SCALE + SCREEN_HEIGHT / 2}
          x2={SCREEN_WIDTH}
          y2={y * SCALE + SCREEN_HEIGHT / 2}
          stroke="#e0e0e0"
          strokeWidth="1"
        />
      );
    }

    // Axe X
    lines.push(
      <Line
        key="x-axis"
        x1={0}
        y1={SCREEN_HEIGHT / 2}
        x2={SCREEN_WIDTH}
        y2={SCREEN_HEIGHT / 2}
        stroke="#ff0000"
        strokeWidth="2"
      />
    );

    // Axe Y
    lines.push(
      <Line
        key="y-axis"
        x1={SCREEN_WIDTH / 2}
        y1={0}
        x2={SCREEN_WIDTH / 2}
        y2={SCREEN_HEIGHT}
        stroke="#ff0000"
        strokeWidth="2"
      />
    );

    return lines;
  };

  // Fonction pour afficher les points tracés par l'utilisateur
  const renderPoints = () => {
    return points.map((point, index) => (
      <Circle
        key={index}
        cx={point.x * SCALE + SCREEN_WIDTH / 2} // Convert to screen coordinates
        cy={SCREEN_HEIGHT / 2 - point.y * SCALE} // Convert to screen coordinates
        r="4" // Point size
        fill="#6366f1" // Point color
      />
    ));
  };

  const renderFunctionCurve = () => {
    const step = 0.1; // Step size for precision
    const path: { x: number; y: number }[] = [];
  
    for (let x = -GRID_SIZE; x <= GRID_SIZE; x += step) {
      const canvasX = x * SCALE + SCREEN_WIDTH / 2;
      const canvasY = SCREEN_HEIGHT / 2 - shuffledFunctions[currentFunction].formula(x) * SCALE;
  
      path.push({ x: canvasX, y: canvasY });
    }
  
    return path.map((point, index) => {
      if (index === 0) return null; // Skip the first point
      const prevPoint = path[index - 1];
      return (
        <Line
          key={`curve-${index}`}
          x1={prevPoint.x}
          y1={prevPoint.y}
          x2={point.x}
          y2={point.y}
          stroke="#10b981" // Curve color
          strokeWidth="2"
        />
      );
    });
  };

  console.log('Points:', points);

  return (
    <GestureHandlerRootView style={styles.container}>
      <SafeAreaView style={styles.container}>
        {/* En-tête avec le nom de la fonction, le score et le timer */}
        <View style={styles.header}>
          <Text style={styles.functionText}>
            {shuffledFunctions[currentFunction]?.name || ''}
          </Text>
          <Animated.Text style={[styles.score, scoreStyle]}>
            Score: {Math.round(score.value)}
          </Animated.Text>
          <Text style={styles.timer}>Temps restant : {timeLeft}s</Text>
        </View>

        {/* Zone de dessin */}
        <View style={styles.canvas} {...panResponder.panHandlers}>
          <Svg height="100%" width="100%">
            {renderGrid()} {/* Quadrillage */}
            {renderPoints()} {/* Points tracés par l'utilisateur */}
            {showCorrectCurve && renderFunctionCurve()} {/* Courbe correcte */}
          </Svg>
        </View>
      </SafeAreaView>
    </GestureHandlerRootView>
  );
}

// Styles pour les composants
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#ffffff',
  },
  header: {
    padding: 20,
    alignItems: 'center',
  },
  functionText: {
    fontSize: 24,
    fontWeight: 'bold',
    color: '#000',
    marginBottom: 10,
  },
  score: {
    fontSize: 20,
    color: '#6366f1',
    fontWeight: 'bold',
  },
  timer: {
    fontSize: 18,
    color: '#ff0000',
    marginTop: 10,
  },
  canvas: {
    flex: 1,
    backgroundColor: '#ffffff',
    borderRadius: 20,
    margin: 20,
    overflow: 'hidden',
  },
});

How to adapt WMS GetFeatureInfo ‘singleclick’ to use hover (‘pointermove’)

Using a piece of code from this OL example:

map.on('singleclick', function (evt) {
  document.getElementById('info').innerHTML = '';
  const viewResolution = /** @type {number} */ (view.getResolution());
  const url = wmsSource.getFeatureInfoUrl(
    evt.coordinate,
    viewResolution,
    'EPSG:3857',
    {'INFO_FORMAT': 'text/html'},
  );
  if (url) {
    fetch(url)
      .then((response) => response.text())
      .then((html) => {
        document.getElementById('info').innerHTML = html;
      });
  }
});

map.on('pointermove', function (evt) {
  if (evt.dragging) {
    return;
  }
  const data = wmsLayer.getData(evt.pixel);
  const hit = data && data[3] > 0; // transparent pixels have zero for data[3]
  map.getTargetElement().style.cursor = hit ? 'pointer' : '';
});

I am trying to get info from a single WMS layer (also multiple layers) on hover instead of using ‘singleclick’. Here it is the current code for this:

let layers;
let pixel;

map.on('pointermove', function (evt) {
  if (evt.dragging) {
    return;
  }
  //const data = ?????.getData(evt.pixel);
  //const hit = data && data[3] > 0; // transparent pixels have zero for data[3]
  //map.getTargetElement().style.cursor = hit ? 'pointer' : '';

  let pgPattern = "_pg";
  const layers = map.getAllLayers().filter(function (layer) {
    return layer.get('name');
  });
  const viewResolution = (view.getResolution());
  let url;
  let mygeojson;
  layers.forEach(function (layer, i, layers) {
    if (layer.getVisible() && layer.get('name').match(pgPattern)) {
      url = layer.getSource().getFeatureInfoUrl(
        evt.coordinate,
        viewResolution,
        'EPSG:4326',
        {'INFO_FORMAT': 'geojson'},
      );
    }
    if (url) {
      fetch(url)
      .then((response) => response.json())
      .then((mygeojson) => {
        if (mygeojson && mygeojson.features.length > 0) {
          const pixel = map.getEventPixel(evt.originalEvent);
          info.style.left = pixel[0] + 'px';
          info.style.top = pixel[1] + 'px';
          info.style.visibility = 'visible';
          info.innerText =
            "id_estacion: "+mygeojson.features[0].properties.id_estacion +"n"+
            "muestreo: "+mygeojson.features[0].properties.muestreo +"n"+
            "descripcion_abordo: "+mygeojson.features[0].properties.descripcion_abordo;
        }
      })
    }
  })
});

map.getTargetElement().addEventListener('pointerleave', function () {
  info.style.visibility = 'hidden';
});

I am stuck about how to solve the erratic behavior of the pointer after info is retrieved from the WMS layer (ie. hover on features of WMS layer).

How could I adapt the code to solve this? Here an image of the problem:

enter image description here