Manipulando el Canvas HMLT5 Usando Konva: Parte 5, Eventos
Spanish (Español) translation by Rafael Chavarría (you can also view the original English article)
Si has estado siguiendo esta serie desde el principio, ahora deberías estar muy cómodo con formas, grupos, y capas. También deberías poder dibujar fácilmente algunas formas básicas y complejas sobre el lienzo usando Konva. Si planeas usar Konva para crear algunas aplicaciones interactivas o juegos, aprender como enlazar eventos a diferentes formas sobre el escenario es el siguiente paso lógico.
En este tutorial, aprenderás cómo enlazar eventos a cualquier forma usando Konva. También aprenderás sobre delegación y propagación de eventos. Algunas veces, podrías necesitar el control de la región de toque de una forma así como disparar eventos mediante programación. Estaremos discutiendo estos dos temas también.
Enlazando Eventos a una Forma
Puedes enlazar diferentes eventos a cualquier forma usando
Konva con la ayuda del método on(). Todo lo que tienes que hacer es pasar el
nombre del evento como el primer parámetro de la función para ser ejecutado
cuando el evento ocurra como el segundo parámetro. Puedes usar Konva para detectar mouseup, mousedown, mouseenter, mouseleave,
mouseover, mousemove, click, y dblclick. Adicionalmente, Konva te permite
detectar eventos wheel, dragstart, dragmove, y dragend.
Aquí está un ejemplo que detecta eventos mousedown y
mouseleave sobre un polígono regular (hexágono). De manera similar el círculo
más pequeño está atado a los eventos mouseover y mouseup y el círculo más
grande está atado a los eventos mouseenter, mouseleave, y mousemove.
1 |
var canvasWidth = 600; |
2 |
var canvasHeight = 400; |
3 |
|
4 |
var stage = new Konva.Stage({ |
5 |
container: "example", |
6 |
width: canvasWidth, |
7 |
height: canvasHeight |
8 |
});
|
9 |
|
10 |
var layerA = new Konva.Layer(); |
11 |
|
12 |
var polyA = new Konva.RegularPolygon({ |
13 |
x: 125, |
14 |
y: 125, |
15 |
sides: 6, |
16 |
radius: 80, |
17 |
fill: "yellow", |
18 |
stroke: "black", |
19 |
strokeWidth: 5 |
20 |
});
|
21 |
|
22 |
var circA = new Konva.Circle({ |
23 |
x: 275, |
24 |
y: 225, |
25 |
height: 100, |
26 |
fill: "orange", |
27 |
stroke: "black" |
28 |
});
|
29 |
|
30 |
var circB = new Konva.Circle({ |
31 |
x: 475, |
32 |
y: 275, |
33 |
radius: 100, |
34 |
fill: "red", |
35 |
stroke: "black" |
36 |
});
|
37 |
|
38 |
layerA.add(polyA, circA, circB); |
39 |
|
40 |
stage.add(layerA); |
41 |
|
42 |
polyA.on("mousedown", function() { |
43 |
polyA.sides(polyA.sides() + 1); |
44 |
layerA.draw(); |
45 |
});
|
46 |
|
47 |
polyA.on("mouseleave", function() { |
48 |
var totalSides = polyA.sides(); |
49 |
if(totalSides > 3) { |
50 |
polyA.sides(polyA.sides() - 1); |
51 |
}
|
52 |
layerA.draw(); |
53 |
});
|
54 |
|
55 |
circA.on("mouseover", function() { |
56 |
circA.strokeWidth(10); |
57 |
layerA.draw(); |
58 |
});
|
59 |
|
60 |
circA.on("mouseup", function() { |
61 |
circA.strokeWidth(5); |
62 |
layerA.draw(); |
63 |
});
|
64 |
|
65 |
circB.on("mouseenter", function() { |
66 |
stage.container().style.cursor = "crosshair"; |
67 |
});
|
68 |
|
69 |
circB.on("mouseleave", function() { |
70 |
stage.container().style.cursor = "default"; |
71 |
});
|
72 |
|
73 |
circB.on("mousemove", function() { |
74 |
var pointerPos = stage.getPointerPosition(); |
75 |
var r = pointerPos.x % 255; |
76 |
var g = pointerPos.y % 255; |
77 |
circB.fill("rgb(" + r + ", " + g + ", 100)"); |
78 |
layerA.draw(); |
79 |
});
|
Si un usuario presiona cualquier botón del ratón mientras el
cursor está dentro del polígono regular, incrementamos el número de lados del
polígono en 1. El método sides() puede ser usado sin parámetro para obtener
el número de lados para un polígono o usado con parámetro para establecer el
número de lados de un polígono. También puedes obtener el número de lados usando getSides()
y establecer el número de lados usando setSides(). Los lados del polígono son
reducidos en uno cada vez que el cursor del ratón abandona el polígono.
Para el círculo más pequeño, el evento mouseover es usado
para establecer el valor de ancho de trazo a 10. El evento mouseup cambia el
valor de ancho de trazo a 5. Ten en mente que el evento mouseup tiene que
ocurrir dentro del círculo mismo. Por ejemplo, el ancho de trazo no cambiará a 5 si presionas
el botón del ratón dentro del círculo y luego lo sueltas solo después de que el
cursor está fuera del círculo.
En el caso del círculo más grande, estamos usando el evento
mousemove para cambiar su color fill. También estamos cambiando el cursor del
círculo más grande usando stage.container().style.cursor siempre que el cursor
se mueva dentro y fuera del círculo.
Una cosa importante que deberías tener en mente es que
tienes que llamar al método draw() sobre la capa respectiva si los escuchadores
de evento para cualquier forma resultan en un cambio con atributos como color
de relleno, ancho de trazo, etc. De otro modo, los cambios no serán reflejados
en el lienzo.
No tienes que enlazar un evento a la vez a una forma.
También puedes pasar una cadena delimitada por espacio conteniendo múltiples
tipos de evento al método on(). Esto enlazará todos los eventos listados en la
cadena a esa forma particular.
Konva también soporta versiones móviles correspondientes a
todos esos eventos. Por ejemplo, puedes registrar touchstart, touchmove,
touchend, tap, dbltap, dragstart, dragmove, y dragend usando Konva en
dispositivos móviles.
También puedes disparar cualquiera de esos eventos para una
forma particular usando el método fire(). De manera similar, Konva te permite
disparar eventos personalizados como throwStones.
Removiendo Escuchadores de Evento
Puedes remover cualquier escuchador de evento adjunto a una
forma con la ayuda del método off() en Konva. Tienes que especificar el nombre
de evento que no quieres escuchar.
También puedes crear múltiples enlaces de evento para una
sola forma. Por ejemplo, digamos que tienes y círculo y quieres incrementar el
radio del círculo cada vez que el cursor pasa sobre este, hasta cierto límite. También podrías querer cambiar el color de relleno del
círculo en cada evento mouseover.
Una opción es hacer ambas tareas dentro de un solo
escuchador de evento mouseover y dejar de actualizar el radio después. Otra opción es crear dos escuchadores de evento mouseover con
diferentes nombres de espacio para identificarlos. De este modo, podrás
incrementar el radio y cambiar el color de relleno de forma independiente.
1 |
circA.on("mouseover.radius", function() { |
2 |
var curRadius = circA.radius(); |
3 |
if(curRadius < 150) { |
4 |
circA.radius(curRadius + 5); |
5 |
layerA.draw(); |
6 |
} else { |
7 |
circA.off('mouseover.radius'); |
8 |
}
|
9 |
});
|
10 |
|
11 |
circA.on("mouseover.fillcolor", function() { |
12 |
var h = Math.floor(Math.random()*360); |
13 |
var color = "hsl(" + h + ", 60%, 60%)"; |
14 |
circA.fill(color); |
15 |
layerA.draw(); |
16 |
});
|
Deberías notar que he agregado layerA.draw() dentro de ambos
escuchadores. Si no puedes agregarlo dentro del escuchador mouseover.fillcolor,
el color dejará de actualizarse tan pronto como el radio sea 150.
En vez de remover un escuchador de evento a la vez, también
puedes dejar de escuchar a todos los eventos atados a una forma usado el método
setListening(). Puedes pasar true o false a este método para encender o apagar
los escuchadores de eventos. Ten en mente que también tendrás que re-dibujar la gráfica
de hito de la capa afectada llamando al método drawHit() justo después de que
llames a setListening().
Delegación y Propagación de Evento
En vez de enlazar directamente a todas las formas presentes
sobre una capa, también puedes enlazar un evento a la capa misma. Después de eso, puedes determinar cuál forma disparó el
evento usando la propiedad target del objeto de evento. De este modo, Konva te
permite delegar efectivamente eventos desde el padre a sus hijos.
Digamos que estás escuchando eventos de clic sobre un
círculo dibujado en una capa en Konva. El mismo evento de clic se propaga al grupo
contenedor así como a la capa contenedora. Esto podría o no ser el comportamiento deseado. Si quieres
prevenir que el evento burbujee hacia adentro de una forma hasta la capa
contenedora, puedes establecer la propiedad cancelBubble para el objeto de
evento a true.
1 |
var canvasWidth = 600; |
2 |
var canvasHeight = 400; |
3 |
|
4 |
var stage = new Konva.Stage({ |
5 |
container: "example", |
6 |
width: canvasWidth, |
7 |
height: canvasHeight |
8 |
});
|
9 |
|
10 |
var layerA = new Konva.Layer(); |
11 |
|
12 |
var circA = new Konva.Circle({ |
13 |
x: 300, |
14 |
y: 200, |
15 |
height: 100, |
16 |
fill: "orange", |
17 |
stroke: "black", |
18 |
name: "Orange Circle" |
19 |
});
|
20 |
|
21 |
var starA = new Konva.Star({ |
22 |
x: 125, |
23 |
y: 125, |
24 |
innerRadius: 25, |
25 |
outerRadius: 75, |
26 |
rotation: 90, |
27 |
fill: "blue", |
28 |
stroke: "black", |
29 |
name: "Blue Star" |
30 |
});
|
31 |
|
32 |
var ringA = new Konva.Ring({ |
33 |
x: 475, |
34 |
y: 275, |
35 |
innerRadius: 25, |
36 |
outerRadius: 75, |
37 |
fill: "brown", |
38 |
stroke: "black", |
39 |
name: "Brown Ring" |
40 |
});
|
41 |
|
42 |
var textA = new Konva.Text({ |
43 |
text: "", |
44 |
fontFamily: "Calibri", |
45 |
fontSize: 24, |
46 |
fill: "black", |
47 |
x: 10, |
48 |
y: 10 |
49 |
});
|
50 |
|
51 |
layerA.add(circA, starA, ringA, textA); |
52 |
|
53 |
stage.add(layerA); |
54 |
|
55 |
layerA.on("click", function(e) { |
56 |
var shapeName = e.target.attrs.name; |
57 |
textA.setText(shapeName); |
58 |
layerA.draw(); |
59 |
});
|
He usado la propiedad name para asignar un nombre a cada una
de nuestras formas. El método setText() es después usado para cambiar el texto
dentro de textA al nombre de la forma que acabamos de dar clic.
Regiones de Toque Personalizadas
En el ejemplo de arriba, el anillo registró un clic sobre él
cuando el clic ocurrió entre el círculo interior y exterior. ¿Qué pasa si
quisieras registrar el clic dentro del círculo pequeño también? Konva te permite definir regiones de toque personalizadas
usando la propiedad hitFunc. Esta propiedad acepta una función como su valor, y
esta función es usada para dibujar la región personalizada.
El siguiente ejemplo te muestra cómo crear regiones de toque personalizadas. Ahora deberías poder dar clic en el área entre los picos de la estrella y aún registrar un clic. Con la ayuda de las regiones de toque personalizadas, puedes asegurar que los usuarios no tienen que dar clic en ubicaciones exactas para registrar un evento de clic. Esto puede resultar en una mejor experiencia de usuario cuando se trata con formas más pequeñas o complejas.
1 |
var starA = new Konva.Star({ |
2 |
x: 125, |
3 |
y: 125, |
4 |
innerRadius: 25, |
5 |
outerRadius: 75, |
6 |
rotation: 90, |
7 |
fill: "blue", |
8 |
stroke: "black", |
9 |
name: "Blue Star", |
10 |
hitFunc: function(context) { |
11 |
context.beginPath(); |
12 |
context.arc(0, 0, this.getOuterRadius(), 0, Math.PI * 2, true); |
13 |
context.closePath(); |
14 |
context.fillStrokeShape(this); |
15 |
}
|
16 |
});
|
17 |
|
18 |
var ringA = new Konva.Ring({ |
19 |
x: 475, |
20 |
y: 275, |
21 |
innerRadius: 25, |
22 |
outerRadius: 75, |
23 |
fill: "brown", |
24 |
stroke: "black", |
25 |
name: "Brown Ring", |
26 |
hitFunc: function(context) { |
27 |
context.beginPath(); |
28 |
context.arc(0, 0, this.getOuterRadius(), 0, Math.PI * 2, true); |
29 |
context.closePath(); |
30 |
context.fillStrokeShape(this); |
31 |
}
|
32 |
});
|
Ideas Finales
En este tutorial. Hemos cubierto diferentes eventos de móvil
y escritorio que puedes enlazar a cualquier forma en Konva. Puedes adjuntar estos eventos uno a la vez o muchos de ellos
al mismo tiempo. Konva también te permite disparar tus propios eventos
personalizados con programación usando el método fire(). La última sección de este turorial te mostró cómo definir
tus propias regiones de toque para poder detectar toques sobre un área que
podría ser más grande o pequeña que la forma original.
Combinando el conocimiento de este tutorial con otros en la serie, ahora deberías poder dibujar una variedad de formas sobre el lienzo y permitir a tus usuarios interactuar con ellos.
Si tienes cualquier pregunta relacionada con este tutorial, siéntete libre de hacérmelo saber en los comentarios.



