javascript - tomografia - ¿Simular el temblor(de, por ejemplo, la enfermedad de Parkinson) con el mouse en una página web?
parkinson transmision (13)
Estoy trabajando para una fundación que crea conciencia sobre la accesibilidad en Internet. Para una presentación, queremos ofrecer un pequeño taller que simule diferentes discapacidades / discapacidades para las personas. Esto se hace a través de un sitio web creado especialmente para esta presentación.
Uno de los impedimentos demostrados es tener un temblor, lo que significa experimentar movimientos inestables y difíciles de controlar con las manos. Con este impedimento, es muy difícil mover el cursor del mouse exactamente y presionar el botón del mouse mientras el mouse está sobre un enlace. Tanto los ancianos como las personas con enfermedades, como el Parkinson, pueden sufrir temblores.
Ahora me gustaría mover de alguna manera el cursor del mouse de una manera impredecible, para que sea muy difícil para las personas hacer clic en un botón pequeño. Debido a que JavaScript no permite mover el cursor del mouse directamente, estoy buscando otras formas de lograrlo. Se me ocurrieron las siguientes ideas:
- Usando un controlador / utilidad del mouse que simula el movimiento del mouse.
- Oculte el cursor del mouse a través de CSS, coloque una animación GIF de un cursor de mouse en movimiento en el lugar del cursor original (con JavaScript), y luego haga clic en el enlace de destino cada pocos segundos durante un segundo aproximadamente. Al menos, esto daría la sensación de que uno siempre hace clic en el momento equivocado.
Si bien la primera idea sería genial, no pude encontrar una herramienta como esta, ya sea para Mac o para Windows. Y yo no tengo ninguna habilidad para programar algo así.
La segunda idea parece un poco torpe, pero creo que lograría el efecto deseado.
¿Alguien tiene otra idea?
Forma no javascript
En realidad, me gustan las soluciones, que pueden estar basadas en javascript, ya que es más probable que estén relacionadas con la web, y las buenas posibilidades son independientes del sistema operativo. Sin embargo, estaba pensando en cómo resolver su problema para todos los navegadores, ya que las soluciones de JavaScript, en este caso, serán difíciles de ajustar para todos los navegadores posibles (no estoy seguro de que sea posible).
Entonces, como ha mencionado, hay otra forma, es decir, emular el comportamiento en el nivel del sistema operativo. Esto también tiene otra ventaja: puede estar seguro de que para el navegador se ve 100% como humano (porque, bueno, es el controlador el que envía la señal). Por lo tanto, puede utilizar soluciones basadas en controladores / dispositivos con cualquier navegador (o incluso en una situación en la que el javascript esté deshabilitado).
Linux
Desafortunadamente, la participación del controlador / dispositivo provoca inmediatamente la dependencia del sistema operativo. Así que para cada SO necesitarás una solución propia. En este post estoy enfocado en una solución basada en Linux (por lo tanto, funcionará con Linux), y Mac OS un poco. Con Linux, es posible escribir eventos en el dispositivo explícitamente, así que a continuación se muestra una función con el bucle principal:
int main()
{
struct input_event event, event_end;
int fd = open("/dev/input/event4", O_RDWR);
long ma = getInteger("Enter max amplitude [points, 0..50]: ", 0, 50);
long ta = getInteger("Enter max wait time [usecs , 0..200000]: ", 0, 200000);
if (fd < 0)
{
printf("Mouse access attempt failed:%s/n", strerror(errno));
return -1;
}
memset(&event, 0, sizeof(event));
memset(&event, 0, sizeof(event_end));
gettimeofday(&event.time, NULL);
event.type = EV_REL;
gettimeofday(&event_end.time, NULL);
event_end.type = EV_SYN;
event_end.code = SYN_REPORT;
event_end.value = 0;
while(1)
{
event.code = rand() % 2 ? REL_X : REL_Y;
event.value = (rand() % 2 ? -1 : 1) * randomTill(ma);
write(fd, &event, sizeof(event));
write(fd, &event_end, sizeof(event_end));
usleep(randomTill(ta));
}
close(fd);
return 0;
}
Mi código completo para el problema se encuentra here . El programa solicitará la amplitud del "temblor" y su frecuencia (por lo tanto, la cantidad de tiempo en micro segundos entre los "temblores"). Para emular la situación, forzará al ratón a moverse aleatoriamente por 0..X
puntos en dirección aleatoria (arriba-abajo-izquierda-abajo) y esperará aleatoriamente 0..Y
micro segundos hasta el siguiente "temblor", X
es la amplitud de "temblor" e Y
es la frecuencia de "temblor"
Otra cosa puede ser adaptar el programa a su sistema. El programa es "ficticio" y no puede detectar el mouse por sí mismo, por lo que "/dev/input/event4"
está codificado. Para darse cuenta de lo que puede ser un identificador para su sistema, puede intentar:
user@host:/path$ cat /proc/bus/input/devices | grep mouse
H: Handlers=mouse0 event3
H: Handlers=mouse1 event4
Y, por lo tanto, las posibilidades son "event3"
y "event4"
, pero para su sistema puede tener otros valores. Entonces, si eso es diferente de lo que se usa actualmente en el código C, simplemente cambie la línea correspondiente (así, línea con int fd = open("/dev/input/event4", O_RDWR);
y coloque su dispositivo en lugar de event4
)
Una demostración gif para este programa (baja velocidad de cuadros, desafortunadamente, así que mantén la imagen no demasiado grande) here .
Una pequeña nota al margen (si no sabes qué hacer con el código C): para compilar el programa anterior, solo usa:
user@host:/path$ gcc -std=gnu99 file.c -o m
donde file.c
es el nombre de su archivo de código fuente C, luego obtendrá un ejecutable, llamado m
en su directorio. Lo más probable es que necesite permisos para escribir en el dispositivo del mouse directamente, por lo que puede usar sudo
:
user@host:/path$ sudo ./m
Otro sistema operativo
La lógica seguirá siendo la misma:
- Encuentra una forma de acceder a tu dispositivo de ratón
- Escribe evento de mover el mouse
- Aplica aleatorización a tu evento.
Eso es. Por ejemplo, Mac OS tiene su propia forma de trabajar con el mouse (no como Linux, Mac tampoco tiene procfs
), está bien descrito here .
Como conclusión
Lo que es mejor, javascript o soluciones orientadas a dispositivos, depende de usted, porque ciertas condiciones (como el navegador cruzado o el sistema operativo cruzado) pueden decidir todo en este caso. Por lo tanto, proporcioné directrices junto con cierto ejemplo práctico de cómo implementar eso en el nivel del sistema operativo. El beneficio aquí es que la solución es para varios navegadores, pero como costo tenemos un programa vinculado al sistema operativo.
¿Por qué no usar una solución de hardware? Hay ciertos ratones que pueden poner pesos, como el Logitech G500. En lugar de poner un peso, ponga un pequeño motor oscilante que haga que el mouse se agite ligeramente. Esto también simuló más el desorden real: no es solo el cursor el que tiembla, sino toda la mano y el mouse. Y también significa que puede mostrar otro software que los sitios web.
En lugar de un mouse con una ranura de peso, también puede pegar algo al mouse, pero eso es más evidente.
Aquí hay una versión para Windows de mi script xdotool que usa AutoIt . Este fue mi primer script de AutoIt, y me tomó solo un par de minutos para escribir, así que estoy seguro de que se puede mejorar. Simplemente guarde con la extensión .au3 y ejecútelo con AutoIt (Run Script x86).
HotKeySet("{HOME}", "GStart")
HotKeySet("{PAUSE}", "Gpause")
HotKeySet("{ESC}", "Gexit")
While 1
Sleep(100)
WEnd
Func Gstart()
While 1
sleep(100)
$pos = MouseGetPos()
$x = $pos[0] + 10 - Random(0, 20, 1)
$y = $pos[1] + 10 - Random(0, 20, 1)
MouseMove($x,$y)
Wend
Endfunc
Func Gpause()
While 1
sleep(100)
Wend
Endfunc
Func Gexit()
MsgBox(0, "exit box", "Script exited")
Exit 0
EndFunc
Controles
- Inicio: Iniciar simulación.
- pausa: pausa simulación.
- Esc: Simulación de salida.
O usa mi versión compilada desde here .
En lugar de intentar mover el puntero, puede mover la aplicación (página web) en su lugar. Escribí un formulario html simple que tiene algunos campos de entrada. Cuando mueves el ratón sobre el formulario, el formulario se mueve.
Puede ver una demostración de la forma en movimiento en jsfiddle . Intente hacer clic en uno de los campos de entrada para ver el efecto.
Usé el efecto jquery shake para lograr esto. El javascript para el efecto de vibración se ve así, y solo hace que el formulario se mueva hacia arriba y hacia abajo cuando se mueve el mouse sobre él:
<script type="text/javascript">
$(document).ready(function() {
$("#toggle").hover(function () {
$(this).effect("shake", { direction: "up", times: 1, distance: 40}, 1000);
});
});
</script>
Aunque la forma solo se mueve hacia arriba y hacia abajo, creo que tiene el efecto deseado. Puedes jugar con los parámetros (dirección, tiempos, distancia, así como el "1000" sin nombre arriba) para modificar el movimiento de la forma.
Hice esto como una broma una vez, en el Foro de Puppy Linux y obtuve el comentario de que:
¡La gente con Parkinson no pensará que es divertido!
Cura aquí es simplemente cntrl-C, por suerte.
Aquí está el script de shell que requiere xdotool
#!/bin/sh
while :; do
xdotool mousemove_relative -- -$(($RANDOM % 10)) $(($RANDOM % 10))
xdotool mousemove_relative -- $(($RANDOM % 10)) -$(($RANDOM % 10))
sleep ${1:-.1} #adjust this as necessary for effect
done
Nombre como parkinson_sim y ejecute con argumento opcional para el tiempo entre temblores que puede ser de 0.001 a 999.0.
parkinson_sim [time_between_tremors_in_seconds] #default es 0.1
Cometí el error de hacer clic en él en lugar de ejecutarlo desde la línea de comandos y rápidamente descubrí lo frustrante que debe ser. Me tomó varios intentos para abrir una ventana de terminal para matarlo.
Hice una demostración rápida de algo en lo que, con suerte, deberías poder basar tu código, utilizando la API de bloqueo del puntero .
Bifurqué este repositorio de puntero-bloqueo-demostración y lo modifiqué para agregar un elemento de movimiento aleatorio.
Aquí está el enlace a mi página de GitHub: https://aristocrates.github.io/pointer-lock-demo
Y aquí está el enlace a mi repositorio: https://github.com/aristocrates/pointer-lock-demo
El código javascript de importancia está contenido en app.js
, en el canvasLoop(e)
.
Lo único que cambié de la demo original fue después de las líneas.
x += movementX * 2;
y += movementY * 2;
Agregué dos líneas para representar movimiento aleatorio:
x += Math.floor(Math.random()*3 - 1);
y += Math.floor(Math.random()*3 - 1);
Todavía hay muchas cosas que podría mejorar, pero espero que esto pueda ayudarlo a comenzar.
Mientras pensabas en hacerlo con un controlador de mouse personalizado, ¿supongo que un pequeño programa que se ejecuta en la PC funcionaría bien? Si este es el caso, aquí hay un pequeño fragmento de C #, que mueve el cursor infinitamente al azar dentro de un rango de más menos 5px alrededor de la posición actual del cursor. Después de cada desplazamiento, el programa espera de 50 ms a 100 ms (¡no es exacto!). El temblor se puede configurar adaptando los valores para el desplazamiento y las pausas. Ejecuté esto en una aplicación de consola y, dependiendo de los valores, me dio bastante dificultad para detener el programa.
Random rand = new Random();
while(true)
{
Cursor.Position = new Point() { X = Cursor.Position.X + rand.Next(11)-5, Y = Cursor.Position.Y + rand.Next(11)-5 };
Thread.Sleep(rand.Next(50) + 50);
}
No puedes esperar que alguien pueda sostener sus manos perfectamente firmes, por lo que una cosa que puedes considerar es,
- Explica a los usuarios lo que estás haciendo y,
- Haga que los elementos seleccionables en la página de demostración sean mucho más pequeños de lo normal.
- Aumentar al máximo la sensibilidad del ratón en el sistema de ejemplo.
Mi razonamiento es (advertencia, no soy un experto en ux o medicina) al hacer más pequeños los elementos que se pueden hacer clic, usted crea un problema similar para la mayoría de las personas a los que una persona afectada de la enfermedad de Parkinson se enfrentaría con un sitio web cotidiano.
Puede usar un traje de simulación para la vejez, como el que se describe en this artículo ... Sospecho que la parte de temblores de las manos es solo un motor vibrante atado alrededor de la muñeca, además de unos guantes gruesos para hacer que las manos sean generalmente torpes.
Solo una idea para obtener el temblor "correcto", puede registrar el movimiento del mouse de un paciente real, esto lo hace más auténtico cuando le dice a las personas de dónde provienen los datos.
Hay una secuencia de comandos para permitir que un gato siga el cursor del mouse, usted podría ajustar uno para permitir que un segundo cursor siga (salte) su cursor. La página está calculando la posición del segundo cursor, por lo que también puede determinar si un evento de clic es exitoso o no.
Si puede, por favor haga que esté basado en la web, llegará a mucha más gente de esta manera que pidiéndoles que instalen un programa o activen flash o lo que sea que haya.
Su segunda idea (ocultar el cursor) está a medio camino de una que creo que puede funcionar bien para usted:
- Oculta el cursor del mouse a través de CSS, como sugieres. (
cursor:none
IIRC) - En lugar de un GIF de cursor tembloroso, use alguna imagen + posicionamiento absoluto CSS + JS para emular el puntero del mouse; es decir, siga el mouse alrededor de la página y coloque la imagen del cursor donde se ubicaría el cursor del mouse de forma nativa.
Luego, agregue un poco de temblor matemático a su código de cursor, para "sacudir" el cursor. Depende de usted determinar cuáles son las curvas correctas para simular correctamente la entrada de temblor.
Finalmente: para cualquier control que estés programando (enlaces, etc.):
- capturar eventos de clics, empujarlos a la ubicación actual de "temblor" en función del estado de sus curvas de temblor y límites: verifique sus elementos para ver si el usuario ha salido del elemento deseado o, posiblemente, en un elemento que no fue diseñado .
Una ventaja importante con esta implementación: su ''cursor tembloroso'' se mostrará en los dispositivos táctiles, que para empezar no tendría un cursor.
Editar:
Basado en la base JSFiddle de los comentarios de Michael Theriot (¡muy limpio y útil!), Aquí hay uno que tiembla constantemente con un barrido normalmente distribuido alrededor de la ubicación actual del cursor: http://jsfiddle.net/benmosher/0x4mc64v/4/
(La matriz normal
es el resultado de llamar a rnorm(100)
en la consola de mi R. Es la forma más simple en la que puedo pensar en JS para muestrear un entero aleatorio de distribución normal).
Las partes de bajo nivel de simular el temblor ya están bien contestadas. Agregaré algo que se centre en el tipo de temblor para simular:
La mayoría de las respuestas implementan un cursor de ratón que se mueve en una ruta aleatoria con un ancho de paso máximo fijo en la dirección X e Y.
Esto debería funcionar lo suficientemente bien para el caso de uso, haciendo que sea difícil golpear un área específica como un botón.
Para el problema más general de simular los problemas de IU causados por un temblor de la enfermedad de Parkinson, sería al menos interesante simular realmente los movimientos de la mano de este tipo de temblor.
Sospecho que la caminata aleatoria puede no ser una muy buena aproximación .
Puede ser difícil obtener datos reales de trazas de la mano del movimiento del temblor, por supuesto, pero ciertamente hay documentos sobre el análisis de este tipo de temblor:
El documento Representación paramétrica del movimiento de las manos en la enfermedad de Parkinson trata sobre la mejor manera de trazar los trazos de movimientos de las manos en 3D.
El papel está en blanco, pero la vista previa en la parte superior derecha, con la etiqueta "Mirar hacia adentro>" en la imagen del libro, muestra algunos gráficos interesantes de diferentes representaciones de datos de trazas de mano .
- En su
DIV
, CSS-oculte el cursor usando elcursor:none;
- Cree una imagen de cursor .png y muévala (
left
,top
) con jQ enmousemove
- Asigne aleatoriamente el
margin-left
.pngmargin-left
y elmargin-top
utilizando unsetTimeout
(para que la reubicación de CSS3 sea fácil de usar o hacerlo con jQ.animate()
).
Nota: el script no puede saber si la mano aún está en el mouse;)
function rand(min, max) {return Math.random() * (max - min) + min;}
var $cursor = $(''div img'');
$(''div'').mousemove(function(e) { // Make .png follow the mouse coordinates
$cursor.css({
left: e.pageX,
top:e.pageY
});
}).hover(function(e){
$cursor.toggle(); // Show .png cursor as we enter the DIV
});
(function tremor(){ // Add tremor to .png image
$cursor.css({
marginLeft: rand(-15,15), // arm tremor
marginTop: rand(-30,30) // hand contractions
});
setTimeout(tremor, rand(50,100));
}());
div{
position:absolute;
background:#eee;
height:100%;
width:100%;
cursor:none;
}
div img{
display:none;
position:absolute;
transition: margin 0.2s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div><img src="http://i.stack.imgur.com/KwMGA.png"></div>