Cuando un mouse se mueve sobre una imagen. Se detecta por esta declaración if:
if ((distance(circles[this.index].x, circles[this.index].y, mouse.x, mouse.y)) < circles[this.index].radius)También quiero detectar cuando un mouse está fuera de una imagen. Después de esa declaración if anterior que no puedo usar, la razón es porque:
Cuando genero varias imágenes en la pantalla y cuando mi mouse se mueve sobre 1 imagen. Se desplaza sobre esa imagen y el código la detecta, pero tampoco sobre todas las demás imágenes. Esa es la razón por la que se muestra 4 veces "círculo exterior" y 1 vez "círculo interior"
Como se ve en el registro:
Salida de Console.log:
Mouse inside circle 
Mouse outside circle 4 
Mouse inside circle 
Mouse outside circle 4 Estoy buscando una forma de detectar cuándo el mouse está saliendo de un círculo.
Puede encontrar el código con el que estoy trabajando a continuación:
PD: es importante que detecte en qué círculo (índice) está el mouse y se va. Quiero crear una gran cantidad de imágenes, pero en el código a continuación utilicé 5 para los propósitos de demostración.
var mouse = {
    x: innerWidth / 2,
    y: innerHeight / 2
};
// Mouse Event Listeners
addEventListener('mousemove', event => {
    mouse.x = event.clientX;
    mouse.y = event.clientY;
});
//Calculate distance between 2 objects
function distance(x1, y1, x2, y2) {
    let xDistance = x2 - x1;
    let yDistance = y2 - y1;
    return Math.sqrt(Math.pow(xDistance, 2) + Math.pow(yDistance, 2));
}
// Sqaure to circle
function makeCircleImage(radius, src, callback) {
    var canvas = document.createElement('canvas');
    canvas.width = canvas.height = radius * 2;
    var ctx = canvas.getContext("2d");
    var img = new Image();
    img.src = src;
    img.onload = function() {
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
        // we use compositing, offers better antialiasing than clip()
        ctx.globalCompositeOperation = 'destination-in';
        ctx.arc(radius, radius, radius, 0, Math.PI*2);
        ctx.fill();
        callback(canvas);
    };
}
function Circle( x, y, radius, index ) {
    //Give var for circle
    this.x = x;
    this.y = y;
    this.dx = 1;
    this.dy = 1;
    this.radius = radius;
    this.index = index;
}
// use prototyping if you wish to make it a class
Circle.prototype = {
//Draw circle on canvas
    draw: function () {
        var
            x = (this.x - this.radius),
            y = (this.y - this.radius);
        // draw is a single call
        c.drawImage( this.image, x, y );
    },
    //Updates position of images
    update: function () {
        var
            max_right = canvas.width + this.radius,
            max_left = this.radius * -1;
        this.x += this.dx;
        if( this.x > max_right ) {
            this.x += max_right - this.x;
            this.dx *= -1;
        }
        if( this.x < max_left ) {
            this.x += max_left - this.x;
            this.dx *= -1;
        }
        if ((distance(circles[this.index].x, circles[this.index].y, mouse.x, mouse.y)) < circles[this.index].radius) {
            // Mouse inside circle
            console.log("Mouse inside circle")
        } else{
            //The mouse is in one circle
            //And out of 4 other circles
            console.log("Mouse outside circle")
        }
    },
    init: function(callback) {
        var url = "https://t4.ftcdn.net/jpg/02/26/96/25/240_F_226962583_DzHr45pyYPdmwnjDoqz6IG7Js9AT05J4.jpg";
        makeCircleImage( this.radius, url, function(img) {
            this.image = img;
            callback();
        }.bind(this));
    }
};
//Animate canvas
function animate() {
    c.clearRect(0, 0, window.innerWidth, window.innerHeight);
    circles.forEach(function( circle ) {
        circle.update();
    });
    circles.forEach(function( circle ) {
        circle.draw();
    });
    requestAnimationFrame(animate);
}
//Init canvas
var canvas = document.querySelector('canvas');
var c = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
//init circle objects
var circles = [
    new Circle(10, 100, 50,0),
    new Circle(10, 200, 30,1),
    new Circle(10, 300, 50,2),
    new Circle(10, 400, 50,3),
    new Circle(10, 500, 50,4)
];
var ready = 0;
circles.forEach(function(circle) {
    circle.init(oncircledone);
});
function oncircledone() {
    if(++ready === circles.length) {
        animate()
    }
}<canvas></canvas>fuente

Ambigüedades
No está claro lo que necesita con respecto a los círculos y algún punto (en este punto de respuesta es un sustituto del mouse y solo requiere que tenga las propiedades
xyysea válido).La falta de información en su pregunta se refiere a los hechos.
que muchos círculos pueden estar debajo del punto al mismo tiempo.
y que más de un círculo puede moverse de abajo hacia afuera o hacia afuera debajo del punto por cuadro.
La redacción de la pregunta sugiere que está detrás de un solo círculo que está en conflicto con las dos preocupaciones anteriores.
Supuestos
Asumiré que la interacción con los círculos es más que un simple evento bajo como interacción. Que pueden incluir comportamientos relacionados con la animación que se desencadenan por el estado relacionado con el punto.
Supongo que el orden visual de los círculos determinará cómo seleccionar círculos de interés.
Que todos los círculos por cuadro cumplan las condiciones requeridas y se pueda acceder rápidamente.
Esa actuación es importante ya que deseas tener muchos círculos que interactúen con un punto.
Que solo hay un punto (mouse, touch, otra fuente) por cuadro que interactúa con los círculos
No hay requisitos para la interacción círculo-círculo.
Solución
El siguiente ejemplo cubre los supuestos anteriores y resuelve cualquier ambigüedad en la pregunta. Está diseñado para ser eficiente y flexible.
Los círculos se almacenan en una matriz que tiene sus propiedades extendidas llamadas
circlesRenderizado y conjuntos de estados
La función
circles.updateDraw(point)actualiza y dibuja todos los círculos. El argumentopointes un punto para verificar el círculo. Por defecto es elmouse.Todos los círculos se dibujan con un contorno. Los círculos debajo del punto (p. Ej., El mouse) se llenan de verde, los círculos que acaban de moverse debajo del punto (p. Ej., OnMouseOver) se llenan de amarillo, los círculos que acaban de salir de debajo se llenan de rojo.
Hay 3 matrices como propiedades de círculos que contienen círculos como se define ...
circles.underTodos los círculos debajo del puntocircles.outFromUnderTodos los círculos salen por debajo del puntocircles.newUnderTodos los círculos nuevos debajo del puntoEstas matrices están pobladas por la función
circles.updateDraw(point)Consulta todos los círculos del estado del punto
Los círculos también tienen 3 funciones que se refieren a las matrices anteriores como
setel conjunto predeterminadocircles.under.Las funciones son ..
circles.firstInSet(set)Devuelve el primer círculo (La parte inferior visual más) ensetoundefinedcircles.lastInSet(set)Devuelve el último círculo (La parte superior visual más) ensetoundefinedcircles.closestInSet(set)Devuelve el círculo más cercano al punto ensetoundefinedPor ejemplo, para obtener el círculo visual superior de la parte superior justo debajo del mouse al que llamaría
circles.lastInSet(circles.newUnder)o para obtener el círculo más cercano al mouse de todos los círculos debajo del mouse al que llamaríacircles.closestInSet(circles.newUnder)(o como el valor predeterminado es establecer launderllamadacircles.closestInSet())Circula estados adicionales
Cada círculo tiene algunas propiedades adicionales.
Circle.distSqres el cuadrado de la distancia desde el puntoCircle.rSqres el cuadrado del radio calculado cuando se construye.Circle.underCountEste valor se puede usar para aplicar animaciones al círculo en función de su estado relativo al punto.Ejecución de demostración
Usa el mouse para moverte sobre los círculos. El círculo más cercano y debajo del mouse está lleno de blanco con alfa = 0.5
fuente
Bueno, el mouse se está moviendo y simplemente puede crear un Conjunto que contendrá objetos circulares que almacenarán los círculos en los que se encuentra:
y luego en el bucle:
y el
update:fuente
Yo propondría lo siguiente:
Mantenga una pila de figuras con el orden de cómo se crearon (o cualquier otro orden significativo). Esto es necesario para detectar movimientos sobre figuras superpuestas.
Implemente una función / método que itere la pila y determine si el cursor está dentro de alguna de las figuras.
Recuerde el último estado, en la transición de estado dentro-> ouside desencadena un evento.
Ahora úsalo:
fuente