Está en la página 1de 180

Machine Translated by Google Lienzo HTML5

Notas para profesionales HTML5

Lienzo
Notas para profesionales

más de 100 páginas


de consejos y trucos profesionales

Descargo
GolKicker.com Libros de de responsabilidad Este es un libro gratuito no oficial creado con fines
educativos y no está asociado con grupos o empresas oficiales de HTML5 C
Programación Gratis Todas las marcas comerciales y marcas registradas
son propiedad de sus respectivos dueños
Machine Translated by Google

Contenido
Sobre .................................................... .................................................... .................................................... ............................. 1

Capítulo 1: Primeros pasos con HTML5 Canvas .................................................... .......................................... 2

Sección 1.1: Detectar la posición del mouse en el lienzo .................................................... ............................................................. 2


Sección 1.2: Tamaño y resolución del lienzo .................................................... .................................................... ................... 2
Sección 1.3: Rotar .................................................... .................................................... .................................................... ... 3

Sección 1.4: Guardar el lienzo en un archivo de .................................................... .................................................... ..................... 3

imagen Sección 1.5: Cómo agregar el elemento de lienzo Html5 a una página web ............... ............................................. 4
Sección 1.6: Un índice de las capacidades y usos de Html5 Canvas .................................................... ..................................... 5
Sección 1.7: Lienzo fuera de pantalla .................................................... .................................................... .................................... 6
Sección 1.8: Hola Mundo .................................................... .................................................... ............................................. 6

Capítulo 2: Texto .................................................... .................................................... .................................................... ......... 8


Sección 2.1: Texto justificado .................................................... .................................................... .......................................... 8

Sección 2.2: Párrafos justificados ............................................... .................................................... ............................... 13


Sección 2.3: Representación de texto a lo largo de .................................................... .................................................... .............. 17

un arco Sección 2.4: Texto en beziers curvos, cúbicos y cuadráticos .................................................... ............................................. 22
....................................................
Sección 2.5: Dibujo de texto Sección 2.6: Formateo de texto Sección .................................................... ............................................. 25

2.7: Ajuste de texto en .................................................... .................................................... .................................... 26

párrafos .............. .................................................... ............................................. 27


Sección 2.8: Dibujar párrafos de texto en formas irregulares ....................................... ............................................. 28
Sección 2.9: Rellenar texto con una imagen ........................................... .................................................... ............................. 30
Capítulo 3: Polígonos .............................................. .................................................... .................................................... 31

Sección 3.1: Renderizar un polígono redondeado ........................................... .................................................... ....................... 31


Sección 3.2: Estrellas .................................................... .................................................... .................................................... ... 32

Sección 3.3: Polígono regular ............................................... .................................................... ...................................... 33

Capítulo 4: Imágenes ............................................. .................................................... .................................................... ... 35

Sección 4.1: ¿"context.drawImage" no muestra la imagen en el lienzo? .................................................... . 35


Sección 4.2: El lienzo manchado .................................................... .................................................... ............................. 35

Sección 4.3: Recorte de imágenes usando el lienzo Sección .................................................... .................................................... ........... 36

4.4: Escalado de la imagen para ajustarla o rellenarla .................................................... .................................................... ................... 36

Capítulo 5: Ruta (solo sintaxis) ........................................... .................................................... .................................... 39

Sección 5.1: createPattern (crea un objeto de estilo de ruta) ....................................... ............................................... 39


Sección 5.2: trazo (un comando de ruta) ............................................. .................................................... .......................... 41
Sección 5.3: llenar (un comando de ruta) ....................................... .................................................... ............................. 45
Sección 5.4: clip (un comando de ruta) ............................................. .................................................... ............................... 45
Sección 5.5: Descripción general de los comandos básicos de trazado de rutas: líneas y curvas .................................................... 47

Sección 5.6: lineTo (un comando de ruta) ............... .................................................... .......................................... 49


Sección 5.7: arc (un comando de ruta) ........................................... .................................................... .......................... 50
Sección 5.8: quadraticCurveTo (un comando de ruta) ....................................... .................................................... ...... 52
Sección 5.9: bezierCurveTo (un comando de ruta) ....................................... .................................................... ............ 53
Sección 5.10: arcTo (un comando de ruta) ....................................... .................................................... .......................... 54
Sección 5.11: rect (un comando de ruta) ....................................... .................................................... ............................. 55
Sección 5.12: closePath (un comando de ruta) ....................................... .................................................... ................... 57
Sección 5.13: beginPath (un comando de ruta) ....................................... .................................................... .................... 58
Sección 5.14: lineCap (un atributo de estilo de ruta) .................................. .................................................... ............. 61
Sección 5.15: lineJoin (un atributo de estilo de ruta) .................................. .................................................... ............. 62
Sección 5.16: strokeStyle (un atributo de estilo de ruta) ........................................... .................................................... ........ 63
Sección 5.17: fillStyle (un atributo de estilo de ruta) ........................................... .................................................... ............... sesenta y cinco
Machine Translated by Google

Sección 5.18: ancho de línea (un atributo de estilo de ruta) .................................. .................................................... .......... 67
Sección 5.19: shadowColor, shadowBlur, shadowOÿsetX, shadowOÿsetY (atributos de estilo de ruta) ........... 68
Sección 5.20: createLinearGradient (crea un objeto de estilo de ruta) ....................................... ............................... 70
Sección 5.21: createRadialGradient (crea un objeto de estilo de ruta) ....................................... ............................. 73

Capítulo 6: Rutas Sección .................................................... .................................................... .................................................... .... 77

6.1: Elipse ........................................... .................................................... .................................................... .......... 77


Sección 6.2: Línea sin borrosidad ............................................... .................................................... ............................. 78

Capítulo 7: Navegación a lo largo de una ruta Sección .................................................... .................................................... ............. 80

.................................................... .................................................... ...............................


7.1: Búsqueda de un punto en una curva Sección 80

7.2: Búsqueda de la extensión de una curva cuadrática .................................................... .................................................... ... 81

Sección 7.3: Búsqueda de puntos a lo largo de una curva Bézier .................................................... ............................................. 82

cúbica Sección 7.4: Búsqueda de puntos a lo largo de una curva .................................................... ............................................. 83
....................................................
cuadrática Sección 7.5: Búsqueda de puntos a lo largo de una línea .................................................... .................. 84

Sección 7.6: Encontrar puntos a lo largo de todo un Camino que contenga curvas y líneas Sección .................................................... .... 84

7.7: Dividir curvas Bézier en la posición .................................. .................................................... .......................... 91


Sección 7.8: Recortar la curva de Bézier .................................................... .................................................... ................................ 94

Sección 7.9: Longitud de una Curva Bézier Cúbica (una aproximación cercana) .................................. ............................. 96
Sección 7.10: Longitud de una curva cuadrática .................................................... .................................................... ........... 97

Capítulo 8: Arrastrar formas e imágenes de rutas en el lienzo .................................................... ...................... 98

Sección 8.1: Cómo las formas e imágenes REALMENTE (!) se "mueven" en el lienzo .................................................... .................. 98
.................................................... ..........................
Sección 8.2: Arrastrar círculos y rectángulos alrededor del lienzo Sección 8.3: Arrastrar 99
....................................................
formas irregulares alrededor del lienzo Sección 8.4: Arrastrar imágenes alrededor del ............................... 103

lienzo .................................................... ............................................................. 106

Capítulo 9: Tipos de medios y el lienzo Sección 9.1: Carga y .................................................... .................................................... ... 109

reproducción básicas de un video en el lienzo Sección 9.2: Capturar lienzo y .................................................... .......................... 109

guardar como video webM Sección 9.3: Dibujar una imagen .................................................... ...................................... 111

svg ............. .................................................... .................................................... ........ 116


Sección 9.4: Cargar y mostrar una imagen ........................................... .................................................... .......... 117

Capítulo 10: Animación.................................................... .................................................... ........................................ 119

Sección 10.1: Use requestAnimationFrame() NO setInterval() para bucles de animación .................................. ..... 119
Sección 10.2: Animar una imagen en el lienzo Sección 10.3: .................................................... ............................................ 120

Establecer la velocidad de fotogramas mediante requestAnimationFrame ................................ ............................................... 121


Sección 10.4: Easing usando ecuaciones de Robert Penners ........................................... .................................................. 121
Sección 10.5: Animar en un intervalo específico (agregar un nuevo rectángulo cada 1 segundo) .................................. ....... 125
Sección 10.6: Animar a una hora específica (un reloj animado) .................................. ...................................... 126
Sección 10.7: No dibuje animaciones en sus controladores de eventos (una aplicación de boceto simple) .................................. ......... 127

Sección 10.8: Animación simple con contexto 2D y requestAnimationFrame ........................................... .......... 129
Sección 10.9: Animar de [x0,y0] a [x1,y1] .................................. .................................................... ...................... 129

Capítulo 11: Colisiones e intersecciones.................................................... .................................................... .... 131

Sección 11.1: ¿Están colisionando 2 círculos? .................................................... .................................................... ...................... 131


Sección 11.2: ¿Están colisionando 2 rectángulos? .................................................... .................................................... ............. 131
Sección 11.3: ¿Están chocando un círculo y un rectángulo? .................................................... .......................................... 131
Sección 11.4: ¿Se interceptan 2 segmentos de línea? .................................................... .................................................... .. 131
Sección 11.5: ¿Están chocando un segmento de línea y un círculo? .................................................... .......................................... 133
Sección 11.6: ¿Están chocando el segmento de línea y el rectángulo? .................................................... ...................................... 133
Sección 11.7: ¿Están colisionando 2 polígonos convexos? .................................................... .................................................... ... 134
Sección 11.8: ¿Están colisionando 2 polígonos? (se permiten polis cóncavos y convexos) ........................................... 135
Sección 11.9: ¿Está un punto X,Y dentro de un arco? .................................................... .................................................... .......... 136

Sección 11.10: ¿Está un punto X,Y dentro de una cuña? .................................................... .................................................... ..... 137

Sección 11.11: ¿Está un punto X,Y dentro de un círculo? .................................................... .................................................... ......... 138
Machine Translated by Google

Sección 11.12: ¿Está un punto X,Y dentro de un rectángulo? .................................................... .................................................... 138

Capítulo 12: Borrar la pantalla .................................................... .................................................... ..................... 139

Sección 12.1: Rectángulos ............................................... .................................................... ............................................. 139


Sección 12.2: Lienzo transparente con degradado ........................................... .................................................... ................... 139
Sección 12.3: Borrar lienzo usando la operación compuesta ........................................... ............................................... 139
Sección 12.4: Datos de imagen sin .................................................... .................................................... ............................... 140

procesar Sección 12.5: Formas complejas ....................................... .................................................... .......................................... 140

Capítulo 13: Diseño receptivo ............................................... .................................................... .......................... 141


Sección 13.1: Creación de un lienzo de página completa con .................................................... .......................................... 141

capacidad de respuesta Sección 13.2: Coordenadas del mouse después de cambiar el tamaño (o desplazarse) ............... ..................................................
141
Sección 13.3: Animaciones de lienzo sensibles sin eventos de cambio de .................................................... .................. 142

tamaño Capítulo 14: Sombras .................................................... .................................................... .......................................... 144

Sección 14.1: Efecto adhesivo usando sombras .................................................... .................................................... .......... 144

Sección 14.2: Cómo detener más sombras .................................. .................................................... ...................... 145
Sección 14.3: La sombra es computacionalmente costosa -- ¡Guarde esa sombra en caché! .................................................... 145

Sección 14.4: Agregar profundidad visual con sombras .................................................... .................................................... ..... 146
Sección 14.5: Sombras internas .................................................... .................................................... .................................. 146

Capítulo 15: Gráficos y diagramas ............................................... .................................................... .......................... 151


Sección 15.1: Gráfico circular con demostración .................................................... .................................................... ........................ 151
Sección 15.2: Línea con puntas de flecha .................................................... .................................................... ...................... 152

Sección 15.3: Curva Bézier cúbica y cuadrática con puntas de flecha .................................................... ......................... 153

Sección 15.4: Cuña .......................................... .................................................... .................................................... .......... 154


Sección 15.5: Arco con relleno y trazo .................................................... .................................................... ........... 155

Capítulo 16: Transformaciones .................................................... .................................................... .......................... 157

Sección 16.1: Rotar una Imagen o Ruta alrededor de su punto central ........................................... ...................................... 157
Sección 16.2: Dibujar muchas imágenes traducidas, escaladas y rotadas rápidamente .................................. ............... 158
Sección 16.3: Introducción a las Transformaciones .................................................... .................................................... ... 159

Sección 16.4: Una matriz de transformación para rastrear formas traducidas, rotadas y escaladas .................................. .... 160

Capítulo 17: Composición ............................................... .................................................... ............................................. 167

Sección 17.1: Dibujar detrás de formas existentes con "destino sobre" .................................................... ..................... 167

Sección 17.2: Borrar formas existentes con "destination-out" .................................................... .................................. 167

Sección 17.3: Composición predeterminada: las formas nuevas se dibujan sobre las formas existentes .................................. ........ 168
Sección 17.4: Recorte imágenes dentro de formas con "destino-en" .................................................... ............................. 168

Sección 17.5: Recorte imágenes dentro de formas con "fuente de entrada" .................................................... ..................................... 168

Sección 17.6: Sombras internas con "source-atop" ....................................... .................................................... ............ 169
Sección 17.7: Cambiar la opacidad con "globalAlpha" ....................................... .................................................... ........ 169
Sección 17.8: Invertir o Negar imagen con "diferencia" .................................................... ............................................. 170
Sección 17.9: Blanco y negro con "color" .................................................... .................................................... .............. 170
Sección 17.10: Aumentar el contraste de color con "saturación" .................................................... ............................... 171

Sección 17.11: Sepia FX con "luminosidad" .................................... .................................................... ....................... 171

Capítulo 18: Manipulación de píxeles con "getImageData" y "putImageData" ....................... 173


Sección 18.1: Introducción a "context.getImageData" ........................................ .................................................... .173
Créditos .................................................... .................................................... .................................................... ...................... 175

También te puede interesar.................................................... .................................................... .................................................... 176


Machine Translated by Google

Sobre

No dude en compartir este PDF con cualquier persona de forma


gratuita, la última versión de este libro se puede descargar desde:

https://goalkicker.com/HTML5CanvasBook

Este libro HTML5 Canvas Notes for Professionals está compilado a partir de Stack Overflow
Documentation, el contenido está escrito por la hermosa gente de Stack Overflow.
El contenido del texto se publica bajo Creative Commons BY-SA, vea los créditos al final de este libro

que contribuyeron a los diversos capítulos. Las imágenes pueden ser propiedad de sus respectivos
propietarios a menos que se especifique lo contrario

Este es un libro gratuito no oficial creado con fines educativos y no está afiliado con grupos o
compañías oficiales de HTML5 Canvas ni con Stack Overflow.
Todas las marcas comerciales y marcas comerciales registradas son propiedad de sus respectivos
dueños de la empresa

No se garantiza que la información presentada en este libro sea correcta ni precisa, utilícela bajo
su propio riesgo.

Envíe comentarios y correcciones a web@petercv.com

GoalKicker.com – Notas HTML5 Canvas para profesionales 1


Machine Translated by Google

Capítulo 1: Primeros pasos con HTML5


Lienzo
Sección 1.1: Detectar la posición del mouse en el lienzo
Este ejemplo mostrará cómo obtener la posición del mouse en relación con el lienzo, de modo que (0,0) sea la esquina superior izquierda del
lienzo HTML5. El e.clientX y e.clientY obtendrán las posiciones del mouse en relación con la parte superior del documento, para cambiar esto para
que se base en la parte superior del lienzo, restamos las posiciones izquierda y derecha del lienzo del cliente X e Y.

var canvas = document.getElementById("myCanvas"); var ctx =


lienzo.getContext("2d"); ctx.font = "16px Arial";

canvas.addEventListener("mousemove", function(e) { var cRect


= canvas.getBoundingClientRect(); var canvasX = // Obtiene CSS pos y ancho/ alto
Math.round(e.clientX - cRect.left); // Resta la 'izquierda' del lienzo var canvasY = Math.round(e.clientY -
cRect.top); // desde las posiciones X/ Y para hacer ctx.clearRect(0, 0, canvas.width, canvas.height); // (0,0) la
parte superior a la izquierda del lienzo ctx.fillText("X: "+lienzoX+", Y: "+lienzoY, 10, 20);

});

Ejemplo ejecutable

El uso de Math.round se debe a garantizar que las posiciones x, y sean números enteros, ya que es posible que el rectángulo delimitador del lienzo no
tenga posiciones enteras.

Sección 1.2: Tamaño y resolución del lienzo


El tamaño de un lienzo es el área que ocupa en la página y está definido por las propiedades de ancho y alto de CSS.

lienzo
{ ancho : 1000px;
altura : 1000px;
}

La resolución del lienzo define el número de píxeles que contiene. La resolución se establece configurando las propiedades de ancho y alto del
elemento del lienzo. Si no se especifica, el lienzo tiene un tamaño predeterminado de 300 por 150 píxeles.

El siguiente lienzo utilizará el tamaño CSS anterior, pero como no se especifica el ancho ni el alto , la resolución será de 300 por 150.

<lienzo id="mi-lienzo"></lienzo>

Esto dará como resultado que cada píxel se estire de manera desigual. El aspecto de píxel es 1:2. Cuando el lienzo se estire, el navegador
utilizará el filtrado bilineal. Esto tiene el efecto de desenfocar los píxeles que están estirados.

Para obtener los mejores resultados al usar el lienzo, asegúrese de que la resolución del lienzo coincida con el tamaño de la pantalla.

Siguiendo con el estilo CSS anterior para que coincida con el tamaño de la pantalla, agregue el lienzo con el ancho y la altura establecidos en el
mismo número de píxeles que define el estilo.

< id del lienzo = "mi-lienzo" ancho = "1000" alto = "1000"></lienzo>

GoalKicker.com – Notas HTML5 Canvas para profesionales 2


Machine Translated by Google

Sección 1.3: Rotar


El método de rotación (r) del contexto 2D gira el lienzo en la cantidad especificada r de radianes alrededor del origen.

HTML

<lienzo id="lienzo" ancho=240 alto=240 estilo="fondo-color:#808080;"> </lienzo>

<button type="button" onclick="rotate_ctx();">Rotar contexto</button>

JavaScript

var lienzo = documento.getElementById("lienzo"); var ctx


= lienzo.getContext("2d"); var buey = lienzo.ancho / 2;
var oy = lienzo.altura / 2; ctx.font = "42px serif";
ctx.textAlign = "centro"; ctx.textBaseline = "medio";
ctx.fillStyle = "#FFF"; ctx.fillText("Hola mundo", buey, oye);

rotar_ctx = function() { //
traducir para que el origen sea ahora (ox, oy) el centro del lienzo ctx.translate(ox, oy); //
convertir grados a radianes con radianes = (Math.PI/ 180)*grados. ctx.rotate((Math.PI /
180) * 15); ctx.fillText("Hola mundo", 0, 0); // traducir de nuevo ctx.translate(-ox, -oy); };

Demostración en vivo en JSfiddle

Sección 1.4: Guardar lienzo en archivo de imagen


Puede guardar un lienzo en un archivo de imagen utilizando el método canvas.toDataURL(), que devuelve el URI de datos para los datos
de imagen del lienzo.

El método puede tomar dos parámetros opcionales canvas.toDataURL(type, encoderOptions): type es el formato de la imagen (si se
omite, el valor predeterminado es image/png); encoderOptions es un número entre 0 y 1 que indica la calidad de la imagen (el valor
predeterminado es 0,92).

Aquí dibujamos un lienzo y adjuntamos el URI de datos del lienzo al enlace "Descargar a myImage.jpg".

HTML

<canvas id="canvas" width=240 height=240 style="background-color:#808080;"> </canvas>


<p></p> <a id="download" download="myImage.jpg" href=""
onclick="download_img(this);">Descargar a myImage.jpg</a>

JavaScript

var lienzo = documento.getElementById("lienzo");

GoalKicker.com – Notas HTML5 Canvas para profesionales 3


Machine Translated by Google

var ctx = lienzo.getContext("2d"); var buey =


lienzo.ancho / 2; var oy = lienzo.altura / 2;
ctx.font = "42px serif"; ctx.textAlign = "centro";
ctx.textBaseline = "medio"; ctx.fillStyle =
"#800"; ctx.fillRect(buey / 2, oy / 2, buey, oy);

download_img = function(el) { //
obtener el URI de la imagen del objeto canvas
var imageURI = canvas.toDataURL("image/jpg"); el.href =
imageURI; };

Demo en vivo en JSfiddle.

Sección 1.5: Cómo agregar el elemento Canvas Html5 a


una página web
Lienzo Html5 ...

Es un elemento Html5.

Es compatible con la mayoría de los navegadores modernos (Internet Explorer 9+).


Es un elemento visible que es transparente por defecto. Tiene
un ancho predeterminado de 300 px y una altura predeterminada de 150 px.
Requiere JavaScript porque todo el contenido debe agregarse mediante programación al lienzo.

Ejemplo: Cree un elemento Html5-Canvas utilizando el marcado Html5 y JavaScript:

<!doctype html>
<html> <head>
<style>
body{ background-
color:white; } #canvasHtml5{border:1px
rojo sólido; } #canvasJavascript{border:1px azul
sólido; } </estilo> <guión> ventana.onload=(función(){

// agregue un elemento de lienzo usando javascript var


canvas=document.createElement('canvas');
canvas.id='canvasJavascript' document.body.appendChild(canvas);

}); // finaliza $(función(){}); </script>


</head> <cuerpo>

<!-- agregue un elemento de lienzo usando html -->


<canvas id='canvasHtml5'></canvas>

</
cuerpo> </html>

GoalKicker.com – Notas HTML5 Canvas para profesionales 4


Machine Translated by Google

Sección 1.6: Un índice de las capacidades y usos de Html5 Canvas


Capacidades del lienzo

Canvas le permite dibujar programáticamente en su página web:

Imágenes,
Textos,
Líneas y Curvas.

Los dibujos en lienzo se pueden diseñar ampliamente:

ancho del trazo,


color del trazo,

color de relleno de
la forma, opacidad,
sombreado,
degradados lineales y degradados radiales, tipo
de fuente, tamaño de fuente, alineación del texto,
el texto se puede trazar, rellenar o trazar y

rellenar, cambiar el tamaño de la imagen, recortar


la imagen, componer

Usos del lienzo

Los dibujos se pueden combinar y colocar en cualquier parte del lienzo para que se puedan usar para crear:

Aplicaciones Paint / Sketch, juegos


interactivos de ritmo rápido, análisis de
datos como tablas, gráficos, imágenes
similares a Photoshop, publicidad similar a
Flash y contenido web Flashy.

Canvas le permite manipular los colores de los componentes rojo, verde, azul y alfa de las imágenes. Esto permite que el lienzo manipule imágenes con
resultados similares a los de Photoshop.

Vuelva a colorear cualquier parte de una imagen a nivel de píxel (si usa HSL, incluso puede volver a colorear una imagen mientras conserva la
iluminación y la saturación importantes para que el resultado no se vea como si alguien hubiera puesto pintura en la imagen), "Knockout" el
fondo alrededor una persona/elemento en una imagen, Detectar e Inundar parte de una imagen (p. ej., cambiar el color de un pétalo de flor en
el que el usuario hizo clic de verde a amarillo, ¡solo ese pétalo en el que se hizo clic!), Deformar la perspectiva (p. ej., envolver una imagen
alrededor la curva de una taza), Examinar el contenido de una imagen (por ejemplo, reconocimiento facial), Responder preguntas sobre una
imagen: ¿Hay un automóvil estacionado en esta imagen de mi lugar de estacionamiento?, Aplicar filtros de imagen estándar (escala de grises,
sepia, etc.)

Aplique cualquier filtro de imagen exótico que pueda imaginar (Detección de bordes Sobel),
Combine imágenes. Si la querida abuela Sue no pudo asistir a la reunión familiar, simplemente utilícela con "photoshop" en la imagen de la
reunión. No me gusta el primo Phil: simplemente "sáquelo con Photoshop, reproduzca un video / tome un cuadro de un video, exporte el
contenido del lienzo como una imagen .jpg | .png (incluso puede opcionalmente recortar o anotar la imagen y

GoalKicker.com – Notas HTML5 Canvas para profesionales 5


Machine Translated by Google

exportar el resultado como una nueva imagen),

Acerca de mover y editar dibujos en lienzo (por ejemplo, para crear un juego de acción):

Una vez que se ha dibujado algo en el lienzo, ese dibujo existente no se puede mover ni editar. Vale la pena aclarar
este concepto erróneo común de que los dibujos en lienzo se pueden mover: ¡los dibujos en lienzo existentes no se pueden
editar ni mover!
Canvas dibuja muy, muy rápido. Canvas puede dibujar cientos de imágenes, textos, líneas y curvas en una fracción de
segundo. Utiliza la GPU cuando está disponible para acelerar el dibujo.
Canvas crea la ilusión de movimiento al dibujar algo rápida y repetidamente y luego volver a dibujarlo en una nueva posición. Al
igual que la televisión, este rediseño constante le da al ojo la ilusión de movimiento.

Sección 1.7: Lienzo fuera de pantalla


Muchas veces, cuando trabaje con el lienzo, necesitará tener un lienzo para contener algunos datos de píxeles internos. Es fácil crear
un lienzo fuera de pantalla, obtener un contexto 2D. Un lienzo fuera de pantalla también utilizará el hardware de gráficos disponible
para renderizar.

El siguiente código simplemente crea un lienzo y lo llena con píxeles azules.

función createCanvas(ancho, alto){


var lienzo = documento.createElement("lienzo"); // crea un elemento de lienzo canvas.width
= ancho; lienzo.altura = altura; lienzo de retorno ;

var miCanvas = createCanvas(256,256); // crea un lienzo pequeño de 256 por 256 píxeles var ctx =
myCanvas.getContext("2d"); ctx.fillStyle = "azul"; ctx.fillRect(0,0,256,256);

Muchas veces, el lienzo fuera de la pantalla se utilizará para muchas tareas y es posible que tenga muchos lienzos. Para simplificar el
uso del lienzo, puede adjuntar el contexto del lienzo al lienzo.

function createCanvasCTX(ancho, alto){ var


canvas = document.createElement("canvas"); // crea un elemento de lienzo canvas.width =
ancho; lienzo.altura = altura; lienzo.ctx = lienzo.getContext("2d"); lienzo de retorno ;

} var miCanvas = createCanvasCTX(256,256); // crea un lienzo pequeño de 256 por 256 píxeles
myCanvas.ctx.fillStyle = "blue"; miLienzo.ctx.fillRect(0,0,256,256);

Sección 1.8: Hola Mundo


HTML

<lienzo id="lienzo" ancho=300 alto=100 estilo="fondo-color:#808080;"> </lienzo>

JavaScript

var lienzo = documento.getElementById("lienzo");

GoalKicker.com – Notas HTML5 Canvas para profesionales 6


Machine Translated by Google

var ctx = lienzo.getContext("2d"); ctx.font = "34px


serif"; ctx.textAlign = "centro";
ctx.textBaseline="medio"; ctx.fillStyle = "#FFF";
ctx.fillText("Hola Mundo",150,50);

Resultado

GoalKicker.com – Notas HTML5 Canvas para profesionales 7


Machine Translated by Google

Capítulo 2: Texto
Apartado 2.1: Texto Justificado
Este ejemplo muestra texto justificado. Agrega funcionalidad extra a CanvasRenderingContext2D extendiendo su prototipo o como un objeto global
addedText (opcional ver Nota A).

Representación de ejemplo.

El código para representar esta imagen se encuentra en los ejemplos de uso en la parte inferior.

El ejemplo

La función como una función anónima inmediatamente invocada.

(function()
{ const FILL = 0; // const para indicar renderizado de texto de relleno
const STROKE = 1;
const MEASURE = 2; var
renderType = FILL; // se usa internamente para configurar el texto de relleno o trazo

var maxSpaceSize = 3; // Multiplicador para tamaño de espacio máximo. Si es mayor, entonces no se aplica justificación var minSpaceSize =
0.5; // Multiplicador para tamaño de espacio mínimo var renderTextJustified = function(ctx,text,x,y,width){

var palabras, ancho de palabras, recuento, espacios, ancho de espacio, adjSpace, renderer, i, textAlign,
tamaño de uso, ancho total;
textAlign = ctx.textAlign; // obtener la configuración de alineación actual ctx.textAlign
= "left"; ancho de palabras = 0; palabras = texto.split(" ").mapa(palabra => {

var w = ctx.measureText(palabra).ancho; ancho de


palabras += w; volver { ancho : w, palabra : palabra,

GoalKicker.com – Notas HTML5 Canvas para profesionales 8


Machine Translated by Google

};
}); //
cuenta = número de palabras, espacios = número de espacios, ancho de espacio tamaño de espacio normal //
adjSpace nuevo tamaño de espacio >= tamaño mínimo. useSize Tamaño del espacio resultante utilizado para representar
count = words.length; espacios = cuenta - 1; espacioAncho = ctx.measureText(" ").ancho; adjSpace = Math.max(spaceWidth
* minSpaceSize, (ancho - WordsWidth) / espacios); useSize = adjSpace > spaceWidth * maxSpaceSize ? espacioAncho :
adjEspacio; totalWidth = wordsWidth + useSize * espacios if(renderType === MEASURE){ // si se mide el tamaño devuelto

ctx.textAlign = textAlign; devuelve


ancho total;

} renderizador = renderType === FILL ? ctx.fillText.bind(ctx) : ctx.strokeText.bind(ctx); // relleno o trazo

switch(textAlign){ case
"right": x -= totalWidth;
descanso; caso "fin":

x += ancho - ancho total; descanso;

case "center": // caída intencional al predeterminado x -= totalWidth / 2; por


defecto:

} if(useSize === spaceWidth){ // si el tamaño del espacio no cambia


renderer(text,x,y); } else { for(i = 0; i < contar; i += 1){

renderizador(palabras[i].palabra,x,y); x
+= palabras[i].ancho; x += usarTamaño;

} ctx.textAlign = textAlign;
}
// Analizar veterinario y establecer el objeto de
configuración. varjustifiedTextSettings = función ( configuración){
var mín, máx;
var vetNumber = (num, defaultNum) => { num = num !==
null && num !== null && !isNaN(num) ? número : número predeterminado; if(num < 0){ numero =
defaultNum;

} devuelve num;

} if(configuración === indefinido || configuración === nulo){


regreso;

} max = vetNumber(settings.maxSpaceSize, maxSpaceSize); min =


vetNumber(settings.minSpaceSize, minSpaceSize); si (mínimo > máximo)
{ retorno;

} minSpaceSize = min;
maxSpaceSize = max;

} // define el texto de relleno


var fillJustifyText = function(text, x, y, width, settings){
justifiedTextSettings(configuraciones);

GoalKicker.com – Notas HTML5 Canvas para profesionales 9


Machine Translated by Google

renderType = LLENAR;
renderTextJustified(este, texto, x, y, ancho);

} // define el texto del trazo var


strokeJustifyText = function(text, x, y, width, settings){
justifiedTextSettings(configuraciones);
renderType = STROKE ; renderTextJustified(este,
texto, x, y, ancho);

} // definir texto de medida var


medidaJustifiedText = function(text, width, settings){
justifiedTextSettings(configuraciones);
renderType = MEDIDA; return
renderTextJustified(este, texto, 0, 0, ancho);

} // punto de código
A // establece los prototipos
CanvasRenderingContext2D.prototype.fillJustifyText = fillJustifyText;
CanvasRenderingContext2D.prototype.strokeJustifyText = trazoJustifyText;
CanvasRenderingContext2D.prototype.measureJustifiedText = medidaJustifiedText; // punto de código B

// código opcional si no desea extender el prototipo CanvasRenderingContext2D / * Descomente desde aquí hasta la ventana
de comentarios de cierre.justifiedText = {

relleno: función (ctx, texto, x, y, ancho, configuración) {justifiedTextSettings


(configuración); renderType = LLENAR; renderTextJustified(ctx,
texto, x, y, ancho);

},
trazo: función (ctx, texto, x, y, ancho, configuración) {justifiedTextSettings
(configuración); renderType = STROKE; renderTextJustified(ctx, texto,
x, y, ancho);

},
medida: función (ctx, texto, ancho, configuración) {justifiedTextSettings
(configuración); renderType = MEDIDA; volver
renderTextJustified(ctx, texto, 0, 0, ancho);

} hasta
aquí*/ })();

Nota A: si no desea extender el prototipo CanvasRenderingContext2D , elimine del ejemplo todo el código entre //
el punto de código A y // el punto de código B y elimine el comentario del código marcado /*
Descomentar desde aquí hasta el comentario de cierre.

Cómo utilizar

Se agregan tres funciones a CanvasRenderingContext2D y están disponibles para todos los objetos de contexto 2D creados.

ctx.fillJustifyText(texto, x, y, ancho, [configuración]);


ctx.strokeJustifyText(texto, x, y, ancho, [configuración]);
ctx.measureJustifiedText(texto, ancho, [configuración]);

Rellenar y trazar la función de texto Rellenar o trazar texto y usar los mismos argumentos. MeasureJustifiedText devolverá el

GoalKicker.com – Notas HTML5 Canvas para profesionales 10


Machine Translated by Google

ancho real en el que se representaría el texto. Esto puede ser igual, menor o mayor que el ancho del argumento según la configuración actual.

Nota: Los argumentos dentro de [ y ] son opcionales.

Argumentos de función

texto: Cadena que contiene el texto a representar.

x, y: Coordenadas para representar el texto.

ancho: Ancho del texto justificado. El texto aumentará/disminuirá los espacios entre las palabras para ajustarse al ancho. Si el espacio entre

palabras es mayor que maxSpaceSize (predeterminado = 6) veces se usará el espacio normal y el texto no llenará el ancho requerido. Si el espaciado

es menor que minSpaceSize (predeterminado = 0,5), el espaciado normal de tiempo se usa el tamaño de espacio mínimo y el texto sobrepasará el

ancho solicitado

ajustes: Opcional. Objeto que contiene tamaños de espacio mínimo y máximo.

El argumento de configuración es opcional y, si no se incluye, la representación de texto utilizará la última configuración definida o la predeterminada (que se
muestra a continuación).

Tanto min como max son los tamaños mínimo y máximo para el carácter [espacio] que separa las palabras. El maxSpaceSize predeterminado
= 6 significa que cuando el espacio entre caracteres es > 63 * ctx.measureText("
justificar
").width
tiene espacios
el texto noinferiores
se justificará.
a minSpaceSize
Si el texto que
= 0,5
se(valor
va a
*
predeterminado 0,5), el espaciado se establecerá en minSpaceSize * ctx.measureText(" ").width y elctx.measureText(" ").ancho el el ancho
texto resultante sobrepasará
de justificación.

Se aplican las siguientes reglas, min y max deben ser números. De lo contrario, los valores asociados no se cambiarán. Si minSpaceSize es mayor

que maxSpaceSize, ambas configuraciones de entrada no son válidas y min max no se cambiará.

Objeto de configuración de ejemplo con valores predeterminados

configuración = {
maxSpaceSize : 6; // Multiplicador para tamaño de espacio máximo.
minSpaceSize : 0.5; // Multiplicador para tamaño de espacio mínimo
};

NOTA: Estas funciones de texto introducen un cambio de comportamiento sutil para la propiedad textAlign del contexto 2D.
'Izquierda', 'derecha', 'centro' e 'inicio' se comportan como se espera, pero 'final' no se alineará desde la derecha del argumento
de función x sino desde la derecha de x + ancho

Nota: la configuración (tamaño de espacio mínimo y máximo) es global para todos los objetos de contexto 2D.

Ejemplos de USO

var i = 0;
text[i++] = "Este texto está alineado desde la izquierda del lienzo."; text[i++] = "Este texto
está cerca del tamaño de espacio máximo"; text[i++] = "Este texto es demasiado corto.";
text[i++] = "Este texto es demasiado largo para el espacio proporcionado y se desbordará#";

GoalKicker.com – Notas HTML5 Canvas para profesionales 11


Machine Translated by Google

text[i++] = "Este texto se alinea usando 'fin' y comienza en x + ancho"; text[i++] = "Este texto está cerca del
tamaño de espacio máximo"; text[i++] = "Este texto es demasiado corto."; text[i++] = "#Este texto es
demasiado largo para el espacio proporcionado y se desbordará"; text[i++] = "Esto está alineado con el
'centro' y se coloca desde el centro"; text[i++] = "Este texto está cerca del tamaño de espacio máximo"; text[i++] =
"Este texto es demasiado corto."; text[i++] = "Este texto es demasiado largo para el espacio proporcionado y se
desbordará";

// ctx es el contexto 2d // canvas es


el lienzo

ctx.clearRect(0,0,w,h); ctx.font =
"25px arial"; ctx.textAlign = "centro"
var izquierda = 20; centro var =
canvas.width / 2; var ancho =
lienzo.ancho-izquierda*2; var y = 40; tamaño
variable = 16; var i = 0; ctx.fillText(" Ejemplos
de texto justificado .",center,y); y+= 40; ctx.font
= "14px arial"; ctx.textAlign = "izquierda" var ww
= ctx.measureJustifiedText(text[0], ancho);
configuración de var = { maxSpaceSize : 6, minSpaceSize : 0.5

} ctx.strokeStyle = "rojo"
ctx.beginPath();
ctx.moveTo(izquierda,y - tamaño * 2);
ctx.lineTo(izquierda, y + tamaño * 15);
ctx.moveTo(canvas.width - izquierda,y - tamaño * 2);
ctx.lineTo(canvas.width - izquierda, y + tamaño * 15); ctx.stroke();
ctx.textAlign = "izquierda"; ctx.fillStyle = "rojo"; ctx.fillText("< 'izquierda'
alineado",izquierda,y - tamaño) ctx.fillStyle = "negro";
ctx.fillJustifyText(texto[i++], izquierda, y, ancho, ajuste); // se recuerda
la configuración ctx.fillJustifyText(text[i++], left, y+=size, width);
ctx.fillJustifyText(texto[i++], izquierda, y+=tamaño, ancho);
ctx.fillJustifyText(texto[i++], izquierda, y+=tamaño, ancho); y += 2,3*tamaño; ctx.fillStyle = "rojo"; ctx.fillText("< 'final' alineado
desde x más el ancho -------------------->",izquierda,y - tamaño) ctx.fillStyle = "negro "; ctx.textAlign = "fin"; ctx.fillJustifyText(texto[i+
+], izquierda, y, ancho); ctx.fillJustifyText(texto[i++], izquierda, y+=tamaño, ancho); ctx.fillJustifyText(texto[i++], izquierda,
y+=tamaño, ancho); ctx.fillJustifyText(texto[i++], izquierda, y+=tamaño, ancho);

y += 40;
ctx.strokeStyle = "rojo"
ctx.beginPath(); ctx.moveTo(centro,y
- tamaño * 2); ctx.lineTo(centro, y + tamaño *
5); ctx.stroke(); ctx.textAlign = "centro";

GoalKicker.com – Notas HTML5 Canvas para profesionales 12


Machine Translated by Google

ctx.fillStyle = "rojo";
ctx.fillText("'centro' alineado",centro,y - tamaño) ctx.fillStyle = "negro";
ctx.fillJustifyText(texto[i++], centro, y, ancho); ctx.fillJustifyText(texto[i+
+], centro, y+=tamaño, ancho); ctx.fillJustifyText(texto[i++], centro,
y+=tamaño, ancho); ctx.fillJustifyText(texto[i++], centro, y+=tamaño, ancho);

Sección 2.2: Párrafos justificados


Representa el texto como párrafos justificados. REQUIERE el ejemplo Texto justificado

Ejemplo de renderizado

El párrafo superior tiene setting.compact = true y el inferior false y el interlineado es 1.2 en lugar del predeterminado 1.5.

Representado por ejemplo de uso de código en la parte inferior de este ejemplo.

Código de ejemplo

// Requiere extensiones de texto justificadas (función()


{ // punto de código A if(typeof
CanvasRenderingContext2D.prototype.fillJustifyText !
== "función"){ throw new ReferenceError(" Falta la extensión de párrafo justificado requerida Extensión de texto
justificado CanvasRenderingContext2D");

} var maxSpaceSize = 3; // Multiplicador para tamaño de espacio máximo. Si es mayor, entonces no se aplica justificación var minSpaceSize
= 0.5; // Multiplicador para tamaño de espacio mínimo var compact = true; // si es cierto, intenta encajar tantas palabras como sea posible. Si
es falso, intente
obtener el espaciado lo más cerca posible de lo normal
var lineSpacing = 1.5; // espacio entre líneas const noJustifySetting
= { // Esta configuración fuerza el texto justificado fuera. Se utiliza para representar la última línea del párrafo. minSpaceSize : 1,
maxSpaceSize : 1,

GoalKicker.com – Notas HTML5 Canvas para profesionales 13


Machine Translated by Google

// Analizar veterinario y establecer el objeto de


configuración. varjustifiedTextSettings = función ( configuración){
var min, max; var
vetNumber = (num, defaultNum) => { num = num !== null
&& num !== null && !isNaN(num) ? número : número predeterminado; devolver número < 0 ? número
predeterminado : número;

} if(configuración === indefinido || configuración === nulo){ return; } compacto =


configuración.compacto === verdadero ? verdadero : configuración.compacto === falso ? falso : compacto; max =
vetNumber(settings.maxSpaceSize, maxSpaceSize); min = vetNumber(settings.minSpaceSize, minSpaceSize); lineSpacing =
vetNumber(settings.lineSpacing, lineSpacing); si (mínimo > máximo){ retorno; } minSpaceSize = min; maxSpaceSize = max;

} var getFontSize = function(font){ // obtener el tamaño de fuente. var numBuscar


= /[0-9]+/; var numero = numFind.exec(fuente)[0]; if(isNaN(number)){ throw
new ReferenceError("justifiedPar No se puede encontrar el tamaño de
fuente");

} número de retorno (número);

} función justificadoPar(ctx, texto, x, y, ancho, configuración, trazo){ var spaceWidth, minS,


maxS, palabras, recuento, líneas, lineWidth, lastLineWidth, lastSize, i,
renderer, fontSize, adjSpace, espacios, word, lineWords, lineFound; espacioAncho =
ctx.measureText(" ").ancho; minS = spaceWidth * minSpaceSize; maxS = spaceWidth
* maxSpaceSize; palabras = texto.split(" ").map(palabra => { // mide todas las palabras.

var w = ctx.measureText(palabra).ancho; volver


{ ancho : w, palabra : palabra,

};
}); //
cuenta = número de palabras, espacios = número de espacios, ancho de espacio tamaño de espacio normal //
adjSpace nuevo tamaño de espacio >= tamaño mínimo. useSize Tamaño del espacio resultante utilizado para representar
el conteo = 0; líneas = []; // crea líneas cambiando palabras de la matriz de palabras hasta que el espaciado sea óptimo. Si

compacto
// verdadero entonces será verdadero y cabrá tantas palabras como sea posible. De lo contrario, intentará obtener el
espaciado // lo más cerca posible del espaciado normal while(words.length > 0){ lastLineWidth = 0; últimoTamaño = -1; línea encontrada =
falso; // cada línea debe tener al menos una palabra. palabra = palabras.shift(); lineWidth = word.width; lineWords =
[palabra.palabra]; cuenta = 0; while(lineWidth < ancho && palabras.longitud > 0){ // Agregar palabras a la línea

palabra = palabras.shift();
lineWidth += word.width;
lineWords.push(palabra.palabra);
cuenta += 1; espacios = cuenta - 1;
adjSpace = (ancho - ancho de línea) /
espacios;

GoalKicker.com – Notas HTML5 Canvas para profesionales 14


Machine Translated by Google

if(minS > adjSpace){ // si el espacio es menor que el mínimo, elimina la última palabra y termina la línea lineFound = true;
palabras.unshift(palabra); lineaPalabras.pop(); }else{ if(!compacto){ // si el modo compacto

if(adjSpace < spaceWidth){ // si es menor que el ancho normal del espacio if(lastSize ===
-1){ lastSize = adjSpace;

} // comprueba si con la última palabra está más cerca del ancho del espacio
if(Math.abs(spaceWidth - adjSpace) < Math.abs(spaceWidth - lastSize)){ lineFound = true; // sí, mantenlo }
else{ palabras.unshift(palabra); // no encaja mejor si la última palabra elimina lineWords.pop();
línea encontrada = verdadero;

}
}
}

} lastSize = adjSpace; // recuerda el espaciado

} lineas.push(lineWords.join(" ")); // y la linea

} // las líneas se han resuelto obtener el tamaño de fuente, renderizar y renderizar todas las líneas. Es posible que la
última // línea deba representarse normalmente para que quede fuera del ciclo. TamañoFuente =
obtenerTamañoFuente(ctx.fuente); renderizador = trazo === verdadero ? ctx.strokeJustifyText.bind(ctx) :
ctx.fillJustifyText.bind(ctx); for(i = 0; i < líneas.longitud - 1; i ++){

renderer (líneas [i], x, y, ancho, configuración); y += espacio


entre líneas * tamaño de fuente;

} if(lines.length > 0){ // última línea si se alinea a la izquierda o al comienzo sin justificar if(ctx.textAlign ===
"left" || ctx.textAlign === "start"){
renderer(lines[lines.length - 1], x, y, width, noJustifySetting); ctx.measureJustifiedText("", ancho,
configuración); }else{ renderer(lines[lines.length - 1], x, y, width);

} // devuelve detalles sobre el párrafo. y += espacio


entre líneas * tamaño de fuente; return { nextLine : y,
fontSize : fontSize, lineHeight : lineSpacing * fontSize,

};

} // definir relleno
var fillParagraphText = función (texto, x, y, ancho, configuración) {
justifiedTextSettings(configuraciones);
configuración = {
minSpaceSize : minSpaceSize,
maxSpaceSize : maxSpaceSize,
};
return justificadoPar(esto, texto, x, y, ancho, configuraciones);

} // define el trazo var


strokeParagraphText = función (texto, x, y, ancho, configuración) {
justifiedTextSettings(configuraciones);

GoalKicker.com – Notas HTML5 Canvas para profesionales 15


Machine Translated by Google

configuración = {
minSpaceSize : minSpaceSize,
maxSpaceSize : maxSpaceSize,
};
return justificadoPar(this, text, x, y, width, settings,true);
}
CanvasRenderingContext2D.prototype.fillParaText = fillParagraphText;
CanvasRenderingContext2D.prototype.strokeParaText = strokeParagraphText; })();

NOTA : esto amplía el prototipo CanvasRenderingContext2D . Si no desea que esto suceda, use el ejemplo de Texto justificado para

averiguar cómo cambiar este ejemplo para que forme parte del espacio de nombres global.

NOTA Lanzará un ReferenceError si este ejemplo no puede encontrar la función


CanvasRenderingContext2D.prototype.fillJustifyText

Cómo utilizar

ctx.fillParaText(texto, x, y, ancho, [configuración]); ctx.strokeParaText(texto,


x, y, ancho, [configuración]);

Consulte Texto justificado para obtener detalles sobre los argumentos. Los argumentos entre [ y ] son opcionales.

El argumento de configuración tiene dos propiedades adicionales.

compacto: predeterminado verdadero. Si es verdadero, intenta empaquetar tantas palabras como sea posible por línea. Si es falso, intenta
que el espacio entre palabras se acerque lo más posible al espacio normal. Espaciado de líneas Predeterminado 1.5. Espacio por línea por
defecto 1.5 la distancia de una línea a la siguiente en términos de tamaño de fuente

Las propiedades que faltan en el objeto de configuración volverán a sus valores predeterminados o a los últimos valores válidos. Las propiedades
solo se cambiarán si los nuevos valores son válidos. Para compactos , los valores válidos son solo valores booleanos verdaderos o falsos . Los valores
verdaderos no se consideran válidos.

Objeto devuelto

Las dos funciones devuelven un objeto que contiene información para ayudarlo a colocar el siguiente párrafo. El objeto contiene las siguientes
propiedades.

nextLine Posición de la siguiente línea después de los píxeles del párrafo.

fontSize Tamaño de la fuente. (Tenga en cuenta que solo use fuentes definidas en píxeles, por ejemplo, 14px arial)

lineHeight Distancia en píxeles de una línea a la siguiente

Este ejemplo usa un algoritmo simple que trabaja una línea a la vez para encontrar el mejor ajuste para un párrafo. Esto no significa que sea el mejor
ajuste (más bien el mejor del algoritmo). Es posible que desee mejorar el algoritmo creando un algoritmo de línea de paso múltiple sobre las líneas
generadas. Mover palabras desde el final de una línea hasta el principio de la siguiente, o desde el principio hasta el final. El mejor aspecto se logra cuando
el espaciado de todo el párrafo tiene la menor variación y es el más cercano al espaciado normal del texto.

Como este ejemplo depende del ejemplo de texto justificado, el código es muy similar. Es posible que desee mover los dos en una sola función. Reemplace
la función "justifiedTextSettings" en el otro ejemplo con la utilizada en este ejemplo. A continuación, copie todo el resto del código de este ejemplo en el
cuerpo de la función anónima del ejemplo de texto justificado . Ya no necesitará probar las dependencias que se encuentran en // Punto de código A Se

puede eliminar.

GoalKicker.com – Notas HTML5 Canvas para profesionales dieciséis


Machine Translated by Google

ejemplo de uso

ctx.font = "25px arial";


ctx.textAlign = "centro"

var izquierda =
10; centro var = canvas.width / 2; var
ancho = lienzo.ancho-izquierda*2; var y
= 20; tamaño variable = 16; var i = 0;
ctx.fillText(" Ejemplos de párrafo
justificado .",center,y); y+= 30; ctx.font =
"14px arial"; ctx.textAlign = "izquierda" // establecer parámetros de
configuración var setting = { maxSpaceSize : 6, minSpaceSize : 0.5,
lineSpacing : 1.2, compact : true,

}
// Muestra los límites izquierdo y derecho.
ctx.strokeStyle = "rojo" ctx.beginPath();
ctx.moveTo(izquierda,y - tamaño * 2);
ctx.lineTo(izquierda, y + tamaño * 15);
ctx.moveTo(canvas.width - izquierda,y -
tamaño * 2); ctx.lineTo(canvas.width - izquierda, y +
tamaño * 15); ctx.stroke(); ctx.textAlign = "izquierda";
ctx.fillStyle = "negro";

// Dibujar párrafo var


line = ctx.fillParaText(para, left, y, width, setting); // se recuerda la configuración

// Siguiente párrafo y
= line.nextLine + line.lineHeight; ajuste.compacto
= falso; ctx.fillParaText(para, izquierda, y,
ancho, configuración);

Nota: Para el texto alineado a la izquierda o al inicio , la última línea de ese párrafo siempre tendrá un espaciado normal. Para todas
las demás alineaciones, la última línea se trata como todas las demás.

Nota: Puede insertar espacios al comienzo del párrafo. Aunque esto puede no ser consistente de un párrafo a otro. Siempre
es bueno aprender qué está haciendo una función y modificarla. Un ejercicio sería agregar una configuración a la configuración
que sangra la primera línea por una cantidad fija. Indique que el bucle while deberá hacer que la primera palabra parezca más
grande temporalmente (+ sangría) palabras[0].ancho += ? y luego, al renderizar líneas, sangra la primera línea.

Sección 2.3: Representación de texto a lo largo de un arco

Este ejemplo muestra cómo representar texto a lo largo de un arco. Incluye cómo puede agregar funcionalidad a
CanvasRenderingContext2D extendiendo su prototipo.

Este ejemplo se deriva de la respuesta de stackoverflow Circular Text.

GoalKicker.com – Notas HTML5 Canvas para profesionales 17


Machine Translated by Google

Representación de ejemplo

Código de ejemplo

El ejemplo agrega 3 nuevas funciones de representación de texto al prototipo de contexto 2D.

ctx.fillCircleText(texto, x, y, radio, inicio, final, adelante);

ctx.strokeCircleText(texto, x, y, radio, inicio, final, adelante);


ctx.measureCircleText(texto, radio);

(función(){ const
FILL = 0; const // const para indicar renderizado de texto de relleno
STROKE = 1; var
renderType = FILL; // se usa internamente para establecer el texto de relleno o trazo const
multiplicarCurrentTransform = true; // si es verdadero Usar la transformación actual al renderizar
// si es falso usa coordenadas absolutas que es un poco
más rápido
// después de renderizar, el currentTransform se restaura a
transformación predeterminada

// mide el texto del círculo // ctx:


contexto del lienzo // texto: cadena
de texto a medir // r: radio en píxeles // // devuelve las
métricas de tamaño del texto // // ancho: ancho de
píxel del texto // angularWidth : ancho angular del
texto en radianes // pixelAngularSize : ancho angular de un
píxel en radianes var medida = función(ctx, texto, radio){ var
textoAncho = ctx.measureText(texto).ancho; // obtener el
ancho de todo el texto return { ancho

: ancho del texto,

GoalKicker.com – Notas HTML5 Canvas para profesionales 18


Machine Translated by Google

angularWidth : (1 / radio) * textWidth, : 1 / radio


pixelAngularSize
};
}

// muestra el texto a lo largo de un círculo //


ctx: contexto del lienzo // texto: cadena de
texto a medir // x,y: posición del centro del círculo //
r: radio del círculo en píxeles // inicio: ángulo en
radianes para comenzar . // [fin]: opcional. Si se
ignora la alineación del texto incluido y el texto es // //
[adelante]: opcional predeterminado verdadero. si la dirección del texto verdadero es hacia adelante, si
la dirección falsa es haciapara
escalado atrás var circleText
encajar = función
entre el inicio (ctx, texto, x, y, radio, inicio, final, adelante)
y el final;
{ var i, textWidth, pA, pAS, a, aw, wScale, alineado , dir, tamaño de fuente; if(text.trim() === || ctx.globalAlpha === 0){ // no mostrar cadena vacía
o transparente

""

regreso;

} if(isNaN(x) || isNaN(y) || isNaN(radio) || isNaN(inicio) || (final !== indefinido && final !==
null && isNaN(end))){ // throw
TypeError(" los argumentos de texto circular requieren un número para x, y, radio, inicio y
final.")

} alineado = ctx.textAlign; // guarda el textAlign actual para que pueda ser restaurado en
final
dir = adelante ? 1 : adelante === falso ? -1 : 1; // establecer dir si no es verdadero o falso establecer
adelante como verdadero

pAS = 1 / radio; // obtener el tamaño angular de un píxel en radianes textWidth = ctx.measureText(text).width; //


obtiene el ancho de todo el texto if (end !== indefinido && end !== null) { // si se proporciona end, ajuste el texto
entre start
y punto
pA = ((fin - inicio) / textWidth) * dir; wEscala = (pA / pAS) *
dir; } else { // si no se proporciona un final, el inicio y el final
correctos para la alineación // si no se cambie
proporciona el avance,
el texto de la parte superior del círculo para leer la dirección correcta if (adelante
=== nulo || adelante === indefinido){

if(((inicio % (Math.PI * 2)) + Math.PI * 2) % (Math.PI * 2) > Math.PI){


dirección = -1;
}

} pA = -pAS * dir ; wEscala


= -1 * dir; switch (alineado)
{ case "center":
// si está centrado, muévase alrededor de la mitad del ancho
inicio -= (pA * textWidth )/2; final = inicio +
pA * ancho de texto; descanso;

case "right":// cae intencionalmente en case "end" case "end":

fin = inicio; inicio


-= pA * ancho del texto; descanso;

case "left": // cae intencionalmente en case "start" case "start":

final = inicio + pA * ancho de texto;


}
}

ctx.textAlign = "centro"; // alinear para renderizar

GoalKicker.com – Notas HTML5 Canvas para profesionales 19


Machine Translated by Google

a = inicio; for (var i = 0; i < text.length; i += 1) { // para cada carácter aw


// establecer
= ctx.measureText(text[i]).width
el ángulo de inicio
* pA; // obtener el ancho angular del texto var xDx = Math.cos(a + aw / 2);

// obtener el vector yAxies desde el centro x,y


fuera

var xDy = Math.sin(a + aw / 2);


if(multiplyCurrentTransform){ // transforma multiplicando la transformación actual
ctx.guardar(); if
(xDy < 0) { // es el texto al revés. Si es voltearlo ctx.transform(-xDy * wScale, xDx *
wScale, -xDx, -xDy, xDx * radio + x, xDy *
radio + y);
} else
{ ctx.transform(-xDy * wScale, xDx * wScale, xDx, xDy, xDx * radio + x, xDy *
radio + y);

} }else{ if
(xDy < 0) { // es el texto al revés. Si es voltearlo ctx.setTransform(-xDy * wScale, xDx *
wScale, -xDx, -xDy, xDx * radio + x, xDy
* radio + y);
} else
{ ctx.setTransform(-xDy * wScale, xDx * wScale, xDx, xDy, xDx * radio + x, xDy *
radio + y);
}

} if(renderType === FILL)


{ ctx.fillText(texto[i], 0, 0); } // renderiza el caracter
else{ ctx.strokeText(texto[i], 0, 0); // renderiza
el caracter

} if(multiplyCurrentTransform){ // restaurar la transformación actual ctx.restore();

} a += aw; // paso al siguiente ángulo

} // todo hecho limpieza. if(!


multiplicarTransformaciónActual){
ctx.setTransform(1, 0, 0, 1, 0, 0); // restaurar la transformación

} ctx.textAlign = alineado; // restaurar la alineación del texto

} // define el texto de relleno


var fillCircleText = función (texto, x, y, radio, inicio, fin, adelante) { renderType = FILL; circleText(este,
texto, x, y, radio, inicio, fin, adelante);

} // define el texto del trazo var


strokeCircleText = función (texto, x, y, radio, inicio, fin, adelante) { renderType = STROKE ; circleText(este,
texto, x, y, radio, inicio, fin, adelante);

} // definir texto de medida var


medidaCircleTextExt = function(texto,radio){ return medida(este, texto,
radio);

} // establecer los prototipos


CanvasRenderingContext2D.prototype.fillCircleText = fillCircleText;
CanvasRenderingContext2D.prototype.strokeCircleText = strokeCircleText;
CanvasRenderingContext2D.prototype.measureCircleText = medirCircleTextExt; })();

Descripciones de funciones

GoalKicker.com – Notas HTML5 Canvas para profesionales 20


Machine Translated by Google

Este ejemplo agrega 3 funciones al prototipo CanvasRenderingContext2D . fillCircleText, strokeCircleText y MeasureCircleText

CanvasRenderingContext2D.fillCircleText(texto, x, y, radio, inicio, [final, [adelante]]);

CanvasRenderingContext2D.strokeCircleText(texto, x, y, radio, inicio, [final, [adelante]]);

texto: texto para representar como


cadena. x,y: Posición del centro del círculo como
Números. radio: radio del círculo en píxeles inicio:
ángulo en radianes para comenzar. [fin]: opcional.
Si se incluye , se ignora ctx.textAlign y se escala el texto para que quepa entre el inicio y el final. [adelante]: valor predeterminado
opcional 'verdadero'. si la dirección del texto verdadero es hacia adelante, si la dirección 'falsa' es hacia atrás.

Ambas funciones usan textBaseline para colocar el texto verticalmente alrededor del radio. Para obtener los mejores resultados, utilice
ctx.TextBaseline.

Las funciones arrojarán un TypeError en cualquiera de los argumentos numéricos como NaN.

Si el argumento de texto se reduce a una cadena vacía o ctx.globalAlpha = 0 , la función simplemente pasa y no hace nada.

CanvasRenderingContext2D.measureCircleText(texto, radio);

- **texto:** Cadena de texto a medir. - **radio:**


radio del círculo en píxeles.

Devuelve un objeto que contiene varias métricas de tamaño para representar texto circular

- **width:** ancho de píxel del texto como se representaría normalmente -


**angularWidth:** ancho angular del texto en radianes. - **pixelAngularSize:**
anchura angular de un píxel en radianes.

Ejemplos de uso

const rad = canvas.height * 0.4; const


texto = "¡Hola círculo TEXTO!"; const
fontSize = 40; const centX = canvas.width /
2; const centY = canvas.height / 2;
ctx.clearRect(0,0,lienzo.ancho,lienzo.alto)

ctx.font = fontSize + "px verdana"; ctx.textAlign


= "centro"; ctx.textBaseline = "abajo";
ctx.fillStyle = "#000"; ctx.strokeStyle = "#666";

// Texto bajo estirado de Math.PI a 0 (180 - 0 grados)


ctx.fillCircleText(text, centX, centY, rad, Math.PI, 0);

// texto sobre la parte superior centrado en Math.PI * 1.5 (270


grados) ctx.fillCircleText(text, centX, centY, rad, Math.PI * 1.5);

// texto debajo de la parte superior centrado en Math.PI * 1.5 (270


grados) ctx.textBaseline = "arriba"; ctx.fillCircleText(texto, centX,
centY, rad, Math.PI * 1.5);

GoalKicker.com – Notas HTML5 Canvas para profesionales 21


Machine Translated by Google

// texto sobre la parte superior centrado en Math.PI * 1.5 (270 grados)


ctx.textBaseline = "medio"; ctx.fillCircleText(texto, centX, centY, rad, Math.PI
* 1.5);

// Usar MeasureCircleText para obtener el tamaño angular var


circleTextMetric = ctx.measureCircleText("Texto a medir", rad); consola.log(circleTextMetric.width); //
ancho del texto si se representa normalmente console.log(circleTextMetric.angularWidth);
angular del texto // ancho
console.log(circleTextMetric.pixelAngularSize); // tamaño angular de un píxel

// Usar texto de medida para dibujar un arco alrededor del texto


ctx.textBaseline = "middle"; var ancho = ctx.measureCircleText(texto,
rad).angularWidth; ctx.fillCircleText(texto, centX, centY, rad, Math.PI * 1.5);

// representar el arco alrededor del texto


ctx.strokeStyle= "red"; ctx.lineWidth = 3;
ctx.beginPath(); ctx.arc(centX, centY, rad +
fontSize / 2,Math.PI * 1.5 - ancho/2,Math.PI*1.5
+ ancho/2); ctx.arc(centX, centY, rad - fontSize / 2,Math.PI * 1.5 + ancho/2,Math.PI*1.5 - ancho/2,true); ctx.closePath(); ctx.stroke();

NOTA: El texto representado es solo una aproximación del texto circular. Por ejemplo, si se representan dos l, las
dos líneas no serán paralelas, pero si representa una "H", los dos bordes serán paralelos. Esto se debe a que cada
carácter se representa lo más cerca posible de la dirección requerida, en lugar de que cada píxel se transforme
correctamente para crear texto circular.

NOTA: const multiplicarCurrentTransform = true; definido en este ejemplo se utiliza para establecer el método de
transformación utilizado. Si es falso , la transformación para la representación de texto circular es absoluta y no depende del estado de
transformación actual. El texto no se verá afectado por ninguna transformación anterior de escala, rotación o traducción. Esto aumentará
el rendimiento de la función de representación, después de llamar a la función, la transformación se establecerá en el valor
predeterminado setTransform (1,0,0,1,0,0)

SimultipleCurrentTransform = true (establecido como predeterminado en este ejemplo), el texto usará la transformación actual
para que el texto se pueda escalar, traducir, sesgar, rotar, etc. pero modificando la transformación actual antes de llamar a las
funciones fillCircleText y strokeCircleText . Dependiendo del estado actual del contexto 2D, esto puede ser un poco más lento que
multiplicarCurrentTransform = false

Sección 2.4: Texto sobre beziers curvos, cúbicos y cuadráticos

GoalKicker.com – Notas HTML5 Canvas para profesionales 22


Machine Translated by Google

textoEnCurva(texto,desplazamiento,x1,y1,x2,y2,x3,y3,x4,y4)

Representa texto en curvas cuadráticas y cúbicas.

text es el texto a renderizar


distancia de compensación desde el inicio de la curva hasta el texto >= 0

x1,y1 - x3,y3 puntos de curva cuadrática o


x1,y1 - x4,y4 puntos de curva cúbica o

Ejemplo de uso:
textOnCurve("¡Hola mundo!",50,100,100,200,200,300,100); // dibuja texto en curva cuadrática // 50 píxeles desde
el inicio de la curva

textOnCurve("¡Hola mundo!",50,100,100,200,200,300,100,400,200); // dibuja el


texto en una curva cúbica // 50
píxeles desde el inicio de la curva

La función y la función de ayudante curver

// pasa 8 valores para bezier cúbico //


pasa 6 valores para cuadrático
// Representa el texto desde el inicio de la
curva var textOnCurve = function(text,offset,x1,y1,x2,y2,x3,y3,x4,y4){
ctx.guardar();
ctx.textAlign = "centro"; var
anchos = []; for(var i = 0; i <
texto.longitud; i ++){
anchos[anchos.longitud] = ctx.measureText(texto[i]).ancho;

} var ch = curveHelper(x1,y1,x2,y2,x3,y3,x4,y4); var pos =


desplazamiento; var cpos = 0;

for(var i = 0; i < texto.longitud; i ++){ pos +=


anchos[i] / 2; cpos = ch.forward(pos);
ch.tangente(cpos); ctx.setTransform(ch.vect.x,
ch.vect.y, -ch.vect.y, ch.vect.x, ch.vec.x,
ch.vec.y); ctx.fillText(texto[i],0,0);

pos += anchos[i] / 2;

} ctx.restaurar();
}

GoalKicker.com – Notas HTML5 Canvas para profesionales 23


Machine Translated by Google

La función de asistente de curvas está diseñada para aumentar el rendimiento de la búsqueda de puntos en el Bézier.

// la función auxiliar localiza puntos en curvas bezier. función


curveHelper(x1, y1, x2, y2, x3, y3, x4, y4){ var tx1, ty1, tx2, ty2, tx3,
ty3, tx4, ty4; var a,b,c,u; var vec,posActual,vec1,vect; vec =
{x:0,y:0}; vec1 = {x:0,y:0}; vector = {x:0,y:0}; cuádruple = falso;
actualPos = 0; actualDist = 0; if(x4 === indefinido || x4 ===
nulo){ quad = verdadero; x4 = x3; y4 = y3;

} var estLen = Math.sqrt((x4 - x1) * (x4 - x1) + (y4 - y1) * (y4 - y1)); var onePix = 1 /
estLen; función posAtC(c){ tx1 = x1; ty1 = y1; tx2 = x2; ty2 = y2; tx3 = x3; ty3 = y3; tx1
+= (tx2 - tx1) * c; ty1 += (ty2 - ty1) * c; tx2 += (tx3 - tx2) * c; ty2 += (ty3 - ty2) * c; tx3 +=
(x4 - tx3) * c; ty3 += (y4 - ty3) * c; tx1 += (tx2 - tx1) * c; ty1 += (ty2 - ty1) * c; tx2 +=
(tx3 - tx2) * c; ty2 += (ty3 - ty2) * c; vec.x = tx1 + (tx2 - tx1) * c; vec.y = ty1 + (ty2 -
ty1) * c; volver vec;

} función posAtQ(c)
{ tx1 = x1; ty1 = y1; tx2
= x2; ty2 = y2; tx1 +=
(tx2 - tx1) * c; ty1 += (ty2 -
ty1) * c; tx2 += (x3 - tx2) * c;
ty2 += (y3 - ty2) * c; vec.x =
tx1 + (tx2 - tx1) * c; vec.y =
ty1 + (ty2 - ty1) * c; volver vec;

} function adelante(dist){ var


paso;
helper.posAt(actualPos);

while(currentDist < dist){ vec1.x


= vec.x; vec1.y = vec.y;
actualPos += onePix;
helper.posAt(actualPos);
currentDist += paso =
Math.sqrt((vec.x - vec1.x) * (vec.x - vec1.x) + (vec.y - vec1.y)
* (vec.y - vec1.y));

GoalKicker.com – Notas HTML5 Canvas para profesionales 24


Machine Translated by Google

currentPos -= ((currentDist - dist) / paso) * onePix currentDist -= paso;


helper.posAt(actualPos); currentDist += Math.sqrt((vec.x - vec1.x) * (vec.x -
vec1.x) + (vec.y - vec1.y) * (vec.y - vec1.y)); volver actualPos;

función tangenteQ(pos){ a = (1-


pos) * 2; b = posición * 2;
vect.x = a * (x2 - x1) + b *
(x3 - x2); vect.y = a * (y2 - y1) + b * (y3 - y2); u =
Math.sqrt(vect.x * vect.x + vect.y * vect.y); vect.x /= u;
vect.y /= u;

} función tangenteC(pos){ a = (1-


pos) b = 6 * a * pos; un *= 3
* un; c = 3 * posición *
posición; vect.x = -x1 * a +
x2 * (a - b) + x3 * (b - c) +
x4 * c; vect.y = -y1 * a + y2 * (a - b) + y3 * (b - c) + y4 * c; u = Math.sqrt(vect.x * vect.x
+ vect.y * vect.y); vect.x /= u; vect.y /= u;

} var ayudante =
{ vec : vec,
vect : vect,
adelante : adelante,

} if(quad)
{ ayudante.posAt = posAtQ ;
ayudante.tangente = tangenteQ; }
else{ ayudante.posAt = posAtC ;
ayudante.tangente = tangenteC;

} ayudante de retorno
}

Sección 2.5: Dibujo de texto


Dibujar en lienzo no se limita solo a formas e imágenes. También puede dibujar texto en el lienzo.

Para dibujar texto en el lienzo, obtenga una referencia al lienzo y luego llame al método fillText en el contexto.

var canvas = document.getElementById('canvas'); var ctx =


lienzo.getContext('2d'); ctx.fillText("Mi texto", 0, 0);

Los tres argumentos necesarios que se pasan a fillText son:

1. El texto que le gustaría mostrar 2. La


posición horizontal (eje x) 3. La posición
vertical (eje y)

Además, hay un cuarto argumento opcional , que puede usar para especificar el ancho máximo de su texto en

GoalKicker.com – Notas HTML5 Canvas para profesionales 25


Machine Translated by Google

píxeles En el siguiente ejemplo, el valor de 200 restringe el ancho máximo del texto a 200 px:

ctx.fillText("Mi texto", 0, 0, 200);

Resultado:

También puede dibujar texto sin relleno y, en su lugar, solo un contorno, utilizando el método strokeText :

ctx.strokeText("Mi texto", 0, 0);

Resultado:

Sin ninguna propiedad de formato de fuente aplicada, el lienzo representa el texto a 10 px en sans-serif de forma predeterminada, lo que dificulta
ver la diferencia entre el resultado de los métodos fillText y strokeText . Consulte el ejemplo de formato de texto para obtener detalles sobre cómo
aumentar el tamaño del texto y aplicar otros cambios estéticos al texto.

Sección 2.6: Formato de texto


El formato de fuente predeterminado proporcionado por los métodos fillText y strokeText no es muy atractivo estéticamente.
Afortunadamente, la API de lienzo proporciona propiedades para dar formato al texto.

Usando la propiedad de fuente puede especificar:

estilo de fuente
variante de fuente

font-weight
font-size / line-height font-
family

Por ejemplo:

GoalKicker.com – Notas HTML5 Canvas para profesionales 26


Machine Translated by Google

ctx.font = "cursiva minúscula negrita 40px Helvetica, Arial, sans-serif"; ctx.fillText("Mi texto", 20, 50);

Resultado:

Usando la propiedad textAlign también puede cambiar la alineación del texto a:

izquierda

centrar

extremo

derecho (igual que la derecha)

comienzo (igual que la izquierda)

Por ejemplo:

ctx.textAlign = "centro";

Sección 2.7: Envolver texto en párrafos


Native Canvas API no tiene un método para ajustar el texto a la siguiente línea cuando se alcanza el ancho máximo deseado. Este ejemplo envuelve el texto en párrafos.

function envolverTexto(texto, x, y, maxWidth, fontSize, fontFace){ var firstY=y; var


palabras = texto.split(' '); var linea = ''; var lineHeight=tamaño de fuente*1.286; // un
buen aproximado para tamaños de 10-18px

ctx.font=TamañoFuente+" "+CaraFuente;
ctx.textBaseline='superior';

for(var n = 0; n < palabras.longitud; n++) { var testLine =


línea + palabras[n] + ' '; var métricas =
ctx.measureText(testLine); var testWidth = metrics.width ;
if(testWidth > maxWidth) { ctx.fillText(line, x, y);
if(n<palabras.longitud-1){ linea = palabras[n] + y +=
alturalinea;

' ';

} más
{ línea = línea de prueba;
}

GoalKicker.com – Notas HTML5 Canvas para profesionales 27


Machine Translated by Google

} ctx.fillText(línea, x, y);
}

Sección 2.8: Dibujar párrafos de texto en formas irregulares


Este ejemplo dibuja párrafos de texto en cualquier parte del lienzo que tenga píxeles opacos.

Funciona buscando el siguiente bloque de píxeles opacos que sea lo suficientemente grande como para contener la siguiente palabra especificada y
llenando ese bloque con la palabra especificada.

Los píxeles opacos pueden provenir de cualquier fuente: Comandos de dibujo de rutas y/o imágenes.

<!doctype html>
<html> <head> <style>
body{ background-
color:white; relleno:
10px; } #canvas{border:1px solid red;} </style> <script>
window.onload=(función(){

var canvas=document.getElementById("canvas"); var


ctx=canvas.getContext("2d"); var cw=lienzo.ancho; var
ch=lienzo.altura;

var tamaño de fuente


= 12; var fontface='verdana'; var
lineHeight=parseInt(tamaño de fuente*1.286);

var text='Era el mejor de los tiempos, era el peor de los tiempos, era la era de la sabiduría, era
la era de la locura, era la época de la fe, era la época de la incredulidad, era la

GoalKicker.com – Notas HTML5 Canvas para profesionales 28


Machine Translated by Google

era la estación de la Luz, era la estación de la Oscuridad, era la primavera de la esperanza, era el invierno de la desesperación, teníamos todo
por delante, no teníamos nada por delante, todos íbamos directos al Cielo, todos íbamos directos La otra manera; en resumen, el período se parecía
tanto al período actual, que algunas de sus autoridades más ruidosas insistieron en que fuera recibido, para bien o para mal, sólo en el grado
superlativo de comparación.'; var palabras=texto.split(' '); var anchosdepalabra=[]; ctx.font=tamaño de fuente+'px '+tipo de letra; for(var
i=0;i<words.length;i++){ wordWidths.push(ctx.measureText(words[i]).width); } var spaceWidth=ctx.measureText(' ').width; var wordIndex=0

var datos=[];

// Demostración: dibujar
corazón // Nota: la forma puede ser CUALQUIER dibujo opaco, incluso una imagen
ctx.scale(3,3); ctx.beginPath(); ctx.moveTo(75,40); ctx.bezierCurveTo(75,37,70,25,50,25);
ctx.bezierCurveTo(20,25,20,62.5,20,62.5); ctx.bezierCurveTo(20,80,40,102,75,120);
ctx.bezierCurveTo(110,102,130,80,130,62.5); ctx.bezierCurveTo(130,62.5,130,25,100,25);
ctx.bezierCurveTo(85,25,75,37,75,40); ctx.fillStyle='rojo'; ctx.llenar();
ctx.setTransform(1,0,0,1,0,0);

// llenar el corazón con texto


ctx.fillStyle='white'; var
imgDataData=ctx.getImageData(0,0,cw,ch).data; for(var
i=0;i<imgDataData.length;i+=4){ data.push(imgDataData[i+3]);

} colocarPalabras();

// dibujar palabras secuencialmente en el siguiente bloque disponible de //


función de píxeles
y=0; opacos disponibles
var índicePalabras=0; placeWords(){ var sx=0; while(y<ch
ctx.textBaseline='superior'; var sy=0; var
&&
wordIndex<palabras.longitud){ sx=0;

sy=y;
var índiceInicial=ÍndicePalabras;
while(sx<cw && wordIndex<palabras.longitud){ var
x=getRect(sx,sy,lineHeight); var disponible=x-sx;
var espaciador=ancho del espacio; // spacer=0 para
no tener margen izquierdo var w=spacer+wordWidths[wordIndex]; while(disponible>=w)
{ ctx.fillText(palabras[wordIndex],spacer+sx,sy); sx+=w; disponible-=w;
espaciador=ancho del espacio; índicePalabras++;
w=espaciador+anchosdepalabra[índicedepalabra];

} sx=x+1;

GoalKicker.com – Notas HTML5 Canvas para profesionales 29


Machine Translated by Google

} y=(wordIndex>startingIndex)?y+lineHeight:y+1;
}
}

// busca un bloque rectangular de píxeles opacos function


getRect(sx,sy,height){
varx =sx;
var y=sy;
var ok=verdadero;
while(ok)
{ if(data[y*cw+x]<250){ok=false;} y++;
si(y>=sy+altura){

y=sy;
x++;
si(x>=cw){ok=falso;}
}

} retorno(x);
}

}); // finaliza $(función(){}); </script> </


head> <body> <h4>Nota: la forma
debe estar cerrada y alfa>=250
adentro</h4> <canvas id="canvas"
width=400 height=400></canvas> < /cuerpo> </html>

Sección 2.9: Rellenar texto con una imagen


Este ejemplo llena el texto con una imagen específica.

¡Importante! La imagen especificada debe estar completamente cargada antes de llamar a esta función o el dibujo fallará. Use image.onload
para asegurarse de que la imagen esté completamente cargada.

función drawImageInsideText(canvas,x,y,img,text,font){ var c=canvas.cloneNode();


var ctx=c.getContext('2d'); ctx.font=fuente; ctx.fillText(texto,x,y);
ctx.globalCompositeOperation='fuente encima'; ctx.drawImage(img,0,0);
lienzo.getContext('2d').drawImage(c,0,0);

GoalKicker.com – Notas HTML5 Canvas para profesionales 30


Machine Translated by Google

Capítulo 3: Polígonos
Sección 3.1: Renderizar un polígono redondeado
Crea una ruta a partir de un conjunto de puntos [{x:?,y:?},{x:?,y:?},...,{x:?,y:?}] con esquinas redondeadas de radio. Si el ángulo de la esquina es demasiado
pequeño para adaptarse al radio o la distancia entre las esquinas no deja espacio, el radio de las esquinas se reduce al mejor ajuste.

Ejemplo de uso

triángulo var = [ { x:
200, y : 50 }, { x: 300,
y : 200 }, { x: 100, y : 200 }

];
var cornerRadius = 30;
ctx.lineWidth = 4; ctx.fillStyle
= "Verde"; ctx.strokeStyle =
"negro"; ctx.beginPath(); //
comienza una nueva ruta roundedPoly(triangle,
cornerRadius); ctx.llenar(); ctx.stroke();

Función de renderizado

var roundedPoly = función (puntos, radio) {


var i, x, y, len, p1, p2, p3, v1, v2, sinA, sinA90, radDirection, drawDirection, angle, halfAngle, cRadius, lenOut;

var asVec = function (p, pp, v) { // convertir puntos a una línea con len y normalizados
vx = pp.x - px; // x,y como vec vy = pp.y
- py; v.len = Math.sqrt(vx * vx + vy *
vy); // longitud de vec v.nx = vx / v.len; // normalizado v.ny = vy / v.len;
v.ang = Math.atan2(v.ny, v.nx); // dirección del vec

} v1 = {};
GoalKicker.com – Notas HTML5 Canvas para profesionales 31
Machine Translated by Google

v2 = {}; len
= puntos.longitud; p1 = // numerar puntos //
puntos[len - 1]; for (i = 0; i < comenzar al final del camino //
len; i++) { p2 = puntos[(i) % len]; p3 = hacer cada esquina // el punto de
puntos[(i + 1) % len]; // sacar la la esquina que se está redondeando
esquina como vectores fuera de la
esquina asVec(p2, p1, v1); // vec de vuelta desde el punto de esquina
asVec(p2, p3, v2); // vec hacia adelante desde el punto de la esquina
obtiene //el producto cruzado de las
esquinas (como seno del ángulo) sinA = v1.nx * v2.ny - v1.ny *lav2.nx;
primera
// obtener
línea y la
el segunda
producto línea
cruzado
perpendicular
de
sinA90 = v1.nx * v2.nx - v1.ny * -v2.ny; // producto cruzado a la normal de la línea 2 ángulo = Math.asin(sinA); //
// producto
obtener el ángulo radDirection = 1; // puede ser necesario invertir el radio cruzado
drawDirection = false; // puede ser
necesario dibujar el arco en sentido contrario a las agujas del reloj // encontrar el cuadrante correcto para el
centro del círculo if (sinA90 < 0) { if (angle < 0) { angle = Math.PI + angle; // suma 180 para movernos al cuadrante
3 } else { angle = Math.PI - angle; // regresa al segundo cuadrante radDirection = -1; dibujarDirección = verdadero;

} } else { if
(ángulo > 0)
{ radDirection = -1;
dibujarDirección = verdadero;
}

} halfAngle = ángulo / 2; //
obtener la distancia desde la esquina hasta el punto donde la esquina redonda toca la línea lenOut
= Math.abs(Math.cos(halfAngle) * radius / Math.sin(halfAngle)); if (lenOut > Math.min(v1.len / 2, v2.len /
2)) { // corrige si tiene más de la mitad de la longitud de la línea
lenOut = Math.min(v1.len / 2, v2.len / 2); // ajusta el radio del
redondeo de la esquina para que encaje cRadius = Math.abs(lenOut
* Math.sin(halfAngle) / Math.cos(halfAngle)); } else { cRadius = radio;

} x = p2.x + v2.nx * lenOut; // salir de la esquina a lo largo de la segunda línea hasta el punto donde se redondea
círculo toca
y = p2.y + v2.ny * lenOut; x += -v2.ny
* cRadius * radDirection; // alejarse de la línea al centro del círculo y += v2.nx * cRadius * radDirection; // x,y es el
centro del círculo de la esquina redondeada ctx.arc(x, y, cRadius, v1.ang + Math.PI / 2 * radDirection, v2.ang -
Math.PI / 2 * radDirection, drawDirection); // dibujar el arco en el sentido de las agujas del reloj p1 = p2; p2 = p3;

} ctx.closePath();
}

Sección 3.2: Estrellas


Dibuja estrellas con un estilo flexible (tamaño, colores, número de puntos).

GoalKicker.com – Notas HTML5 Canvas para profesionales 32


Machine Translated by Google

// Uso:
drawStar(75,75,5,50,25,'mediumseagreen','gray',9);
dibujarEstrella(150,200,8,50,25,'azul cielo','gris',3);
dibujarEstrella(225,75,16,50,20,'coral','transparente',0);
dibujarEstrella(300,200,16,50,40,'dorado','gris',3);

// centroX, centroY: el punto central de la estrella // puntos: el número


de puntos en el exterior de la estrella // interior: el radio de los puntos interiores de la
estrella // exterior: el radio de los puntos exteriores de la estrella // relleno, trazo: los
colores de relleno y trazo a aplicar // línea: el ancho de línea del trazo

función dibujarEstrella(centroX, centroY, puntos, exterior, interior, relleno, trazo, línea) {


// definir la estrella
ctx.beginPath();
ctx.moveTo(centroX, centroY+exterior); for (var i=0;
i < 2*puntos+1; i++) {
var r = (i%2 == 0)? exterior : interior; var a =
Math.PI * i/puntos; ctx.lineTo(centerX +
r*Math.sin(a), centerY + r*Math.cos(a));
};
ctx.closePath(); //
dibujar ctx.fillStyle=relleno;
ctx.llenar();
ctx.strokeStyle=trazo;
ctx.lineWidth=línea; ctx.carrera()

Sección 3.3: Polígono Regular


Un polígono regular tiene todos los lados de igual longitud.

// Uso:
dibujarPolígonoRegular(3,25,75,50,6,'gris','rojo',0);
dibujarPolígonoRegular(5,25,150,50,6,'gris','dorado',0);

GoalKicker.com – Notas HTML5 Canvas para profesionales 33


Machine Translated by Google

dibujarPolígonoRegular(6,25,225,50,6,'gris','azul claro',0);
dibujarPolígonoRegular(10,25,300,50,6,'gris','verde claro',0);

function
drawRegularPolygon(sideCount,radius,centerX,centerY,strokeWidth,strokeColor,fillColor,rotationRadia ns){ var angles=Math.PI*2/sideCount;
ctx.translate(centroX,centroY); ctx.rotate(rotaciónRadianes); ctx.beginPath(); ctx.moveTo(radio,0); for(var i=1;i<sideCount;i++){ ctx.rotate(ángulos);
ctx.lineTo(radio,0);

} ctx.closePath();
ctx.fillStyle=fillColor; ctx.strokeStyle
= color del trazo; ctx.lineWidth = trazoAncho;
ctx.stroke(); ctx.llenar(); ctx.rotate(ángulos*-
(sideCount-1)); ctx.rotate(-rotaciónradianes);
ctx.translate(-centerX,-centerY);

GoalKicker.com – Notas HTML5 Canvas para profesionales 34


Machine Translated by Google

Capítulo 4: Imágenes
Sección 4.1: ¿"context.drawImage" no muestra la imagen en
el lienzo?
Asegúrese de que su objeto de imagen esté completamente cargado antes de intentar dibujarlo en el lienzo con context.drawImage.
De lo contrario, la imagen no se mostrará en silencio.

En JavaScript, las imágenes no se cargan inmediatamente. En cambio, las imágenes se cargan de forma asíncrona y, durante el tiempo
que tardan en cargarse, JavaScript continúa ejecutando cualquier código que siga a image.src. Esto significa que context.drawImage puede
ejecutarse con una imagen vacía y, por lo tanto, no mostrará nada.

Ejemplo asegurándose de que la imagen esté completamente cargada antes de intentar dibujarla con .drawImage

var img=nueva imagen();


img.onload=inicio;
img.onerror=función(){alerta(img.src+' falló');}
img.src="algunaImagen.png"; function start(){ // start() se llama
DESPUÉS de que la imagen esté completamente cargada
independientemente de // la posición de inicio en el código

Ejemplo cargando múltiples imágenes antes de intentar dibujar con alguna de ellas

Hay más cargadores de imágenes con funciones completas, pero este ejemplo ilustra cómo hacerlo.

// primera imagen
var img1=nueva Imagen();
img1.onload=inicio;
img1.onerror=function(){alert(img1.src+' no se pudo cargar.');};
img1.src="imagenUno.png"; // segunda imagen var img2=nueva Imagen();
img2.onload=inicio; img1.onerror=function(){alert(img2.src+' no se pudo cargar.');};
img2.src="imagenDos.png"; // var imgCount=2; // se llama a start cada vez que se carga
una imagen function start(){ // cuenta atrás hasta que se cargan todas las imágenes if(--
imgCount>0){return;}

// Todas las imágenes ahora se cargaron con éxito //


context.drawImage dibujará con éxito cada una context.drawImage(img1,0,0);
context.drawImage(img2,50,0);

Sección 4.2: El lienzo manchado


Al agregar contenido de fuentes fuera de su dominio, o del sistema de archivos local, el lienzo se marca como corrupto. Intentar acceder
a los datos de píxeles o convertirlos a una URL de datos generará un error de seguridad.

imagen vr = nueva imagen ();


imagen.src = "archivo://miImagenLocal.png";

GoalKicker.com – Notas HTML5 Canvas para profesionales 35


Machine Translated by Google

imagen.cargar = función(){
ctx.drawImage(esto,0,0);
ctx.getImageData(0,0,canvas.width,canvas.height); // arroja un error de seguridad
}

Este ejemplo es solo un trozo para atraer a alguien con una comprensión detallada y elaborada.

Sección 4.3: Recorte de imágenes usando lienzo

Este ejemplo muestra una función de recorte de imagen simple que toma una imagen y las coordenadas de recorte y devuelve
la imagen recortada.

función cropImage(imagen, croppingCoords ) { var cc =


croppingCoords; var workCan =
document.createElement("canvas"); // crea un lienzo workCan.width = Math.floor(cc.width); //
establecer la resolución del lienzo al tamaño de la imagen recortada workCan.height = Math.floor(cc.height); var ctx =
workCan.getContext("2d"); // obtener una interfaz de renderizado 2D ctx.drawImage(image, -Math.floor(cc.x), -Math.floor(cc.y)); // dibujar
el desplazamiento de la imagen para colocar

correctamente en la región recortada


imagen.src = workCan.toDataURL(); imagen de // establece la fuente de la imagen en el lienzo como una URL de datos
retorno ;
}

Usar

var imagen = nueva imagen();


image.src = "URL de la imagen"; // carga la imagen
image.onload = function () { // cuando se carga cropImage( this,
{ x : this.width / 4, y : this.height / 4, width : this.width / 2,
height : this. altura / 2,
// recortar manteniendo el centro

});
documento.cuerpo.appendChild(esto); // Agrega la imagen al DOM
};

Sección 4.4: Escalado de la imagen para ajustar o rellenar

Escalado para adaptarse

Significa que toda la imagen será visible, pero puede haber algún espacio vacío en los lados o en la parte superior e inferior
si la imagen no tiene el mismo aspecto que el lienzo. El ejemplo muestra la imagen escalada para ajustarla. El azul de los
laterales se debe a que la imagen no tiene el mismo aspecto que el lienzo.

GoalKicker.com – Notas HTML5 Canvas para profesionales 36


Machine Translated by Google

Escalar para llenar

Significa que la imagen se escala para que todos los píxeles del lienzo queden cubiertos por la imagen. Si el aspecto de la imagen no es el mismo que el del
lienzo, se recortarán algunas partes de la imagen. El ejemplo muestra la imagen escalada para rellenar.
Observe cómo la parte superior e inferior de la imagen ya no son visibles.

Ejemplo Escalar para


ajustar var image = new
Image(); imagen.src =
"URLimg"; image.onload =
function(){ scaleToFit(this);
}

function scaleToFit(img){ //
obtener la escala var scale
= Math.min(canvas.width / img.width, canvas.height / img.height); // obtener la posición superior
izquierda de la imagen var x = (canvas.width / 2) - (img.width / 2) * scale; var y = (lienzo.altura /
2) - (img.altura / 2) * escala; ctx.drawImage(img, x, y, img.ancho * escala, img.altura * escala);

Ejemplo Escala para llenar

var imagen = nueva imagen();


imagen.src = "URLimg";
image.onload = function()
{ scaleToFill(this);
}

función scaleToFill(img){

GoalKicker.com – Notas HTML5 Canvas para profesionales 37


Machine Translated by Google

// obtener la escala var


scale = Math.max(canvas.width / img.width, canvas.height / img.height); // obtener la posición superior izquierda
de la imagen var x = (canvas.width / 2) - (img.width / 2) * scale; var y = (lienzo.altura / 2) - (img.altura / 2) *
escala; ctx.drawImage(img, x, y, img.ancho * escala, img.altura * escala);

La única diferencia entre las dos funciones es obtener la escala. El ajuste usa la escala de ajuste mínima y el relleno usa la
escala de ajuste máxima.

GoalKicker.com – Notas HTML5 Canvas para profesionales 38


Machine Translated by Google

Capítulo 5: Ruta (solo sintaxis)


Sección 5.1: createPattern (crea un objeto de estilo de ruta)
var patrón = createPattern(imagenObjeto,repetir)

Crea un patrón reutilizable (objeto).

El objeto se puede asignar a cualquier estilo de trazo y/o estilo de relleno.

Luego, stroke() o fill() pintarán la ruta con el patrón del objeto.

Argumentos:

imageObject es una imagen que se utilizará como patrón. La fuente de la imagen puede ser:

HTMLImageElement --- un elemento img o una nueva imagen (),


HTMLCanvasElement --- un elemento de lienzo, HTMLVideoElement ---

un elemento de video (tomará el cuadro de video actual)


ImagenBitmap,
Blob.

repetir determina cómo se repetirá el objeto de imagen en el lienzo (muy parecido a un fondo CSS).
Este argumento debe estar delimitado por comillas y los valores válidos son:

"repetir" --- el patrón llenará horizontal y verticalmente el lienzo "repetir-x" --- el


patrón solo se repetirá horizontalmente (1 fila horizontal) "repetir-y" --- el patrón solo se
repetirá verticalmente (1 fila vertical) "repetir ninguno" --- el patrón aparece solo una vez (en
la parte superior izquierda)

El objeto de patrón es un objeto que puede usar (¡y reutilizar!) para hacer que los trazos y rellenos de su ruta se conviertan en patrones.

Nota al margen: el objeto de patrón no es interno al elemento Canvas ni es Contexto. Es un objeto JavaScript separado y reutilizable que puede
asignar a cualquier ruta que desee. Incluso puede usar este objeto para aplicar un patrón a una ruta en un elemento de lienzo diferente (!)

¡Consejo importante sobre los patrones de Canvas!

Cuando crea un objeto de patrón, todo el lienzo se rellena de forma "invisible" con ese patrón (sujeto al argumento de repetición ).

Cuando acaricias() o rellenas() una ruta, el patrón invisible se revela, pero solo se revela sobre la ruta que se está acariciando o rellenando.

1. Comienza con una imagen que quieras usar como patrón. Importante (!): asegúrese de que su imagen se haya cargado completamente
(usando patternimage.onload) antes de intentar usarlo para crear su patrón.

GoalKicker.com – Notas HTML5 Canvas para profesionales 39


Machine Translated by Google

2. Creas un patrón como este:

// crea un patrón var


pattern = ctx.createPattern(patternImage,'repeat'); ctx.fillStyle=patrón;

3. Luego, Canvas verá "invisiblemente" la creación de su patrón de esta manera:

4. Pero hasta que acaricie() o rellene() con el patrón, no verá nada del patrón en el lienzo.

5. Finalmente, si traza o rellena una ruta usando el patrón, el patrón "invisible" se vuelve visible en el lienzo, pero solo donde se dibuja ...
la ruta.

<!doctype html>
<html>
<cabeza>
<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </
estilo> <guión> ventana.onload=(función(){

// variables relacionadas con el


lienzo var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");

GoalKicker.com – Notas HTML5 Canvas para profesionales 40


Machine Translated by Google

// rellenar usando un patrón var


patternImage=new Image(); // ¡IMPORTANTE!

// Utilice siempre .onload para asegurarse de que la imagen se haya


patternImage.onload=function(){
cargado por completo //antes
cree de
un usarla
objeto en
de .createPattern
patrón var pattern
// =
ctx.createPattern(patternImage,'repeat'); // establece el estilo de relleno en
ese patrón ctx.fillStyle=pattern; // rellena un rectángulo con el patrón
ctx.fillRect(50,50,150,100); // solo demo, traza el rect para mayor claridad
ctx.strokeRect(50,50,150,100);

} patternImage.src='http://i.stack.imgur.com/K9EZl.png';

}); // fin de ventana.onload </script>


</head> <body> <canvas
id="canvas" width=325 height=250></
canvas> </body> </html>

Sección 5.2: trazo (un comando de ruta)


contexto.stroke()

Hace que el perímetro de Path se trace de acuerdo con el context.strokeStyle actual y el Path trazado se dibuja visualmente en el lienzo.

Antes de ejecutar context.stroke (o context.fill) , la ruta existe en la memoria y aún no se dibuja visualmente en el
lienzo.

La forma inusual en que se dibujan los trazos

Considere este ejemplo de Ruta que dibuja una línea negra de 1 píxel de [0,5] a [5,5]:

// dibuja una línea negra de 1 píxel de [0,5] a [5,5]


context.strokeStyle='black'; contexto.lineWidth=1; contexto.beginPath();
contexto.moverA(0,5); contexto.lineTo(5,5); contexto.stroke();

Pregunta: ¿Qué dibuja realmente el navegador en el lienzo?

Probablemente espera obtener 6 píxeles negros en y = 5

GoalKicker.com – Notas HTML5 Canvas para profesionales 41


Machine Translated by Google

Pero(!) ... ¡Canvas siempre dibuja trazos a la mitad de cada lado de la ruta definida!

Entonces, dado que la línea está definida en y==5.0 , Canvas quiere dibujar la línea entre y==4.5 e y==5.5

Pero otra vez(!) ... ¡La pantalla de la computadora no puede dibujar medios píxeles!

Entonces, ¿qué se debe hacer con los medios píxeles no deseados (que se muestran en azul a continuación)?

GoalKicker.com – Notas HTML5 Canvas para profesionales 42


Machine Translated by Google

La respuesta es que Canvas en realidad ordena a la pantalla que dibuje una línea de 2 píxeles de ancho desde 4.0 a 6.0. También colorea la línea
más clara que el negro definido. Este extraño comportamiento de dibujo es "anti-aliasing" y ayuda a Canvas a evitar dibujar trazos que se ven
irregulares.

Un truco de ajuste que SÓLO funciona para trazos exactamente horizontales y verticales

Puede obtener una línea negra sólida de 1 píxel especificando que la línea se dibuje en el medio píxel:

contexto.moveTo(0,5.5);
contexto.linea(5,5.5);

GoalKicker.com – Notas HTML5 Canvas para profesionales 43


Machine Translated by Google

Código de ejemplo que usa context.stroke() para dibujar una ruta trazada en el lienzo:

<!doctype html>
<html> <head>
<style>
body{ background-
color:white; } #canvas{border:1px rojo
sólido; } </estilo> <guión>
ventana.onload=(función(){

// variables relacionadas con el


lienzo var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");

ctx.beginPath();
ctx.moveTo(50,30);
ctx.lineTo(75,55);
ctx.lineTo(25,55);
ctx.lineTo(50,30);
ctx.lineWidth=2;
ctx.stroke();

}); // fin de ventana.onload </


script> </head> <body>
<canvas id="canvas"
width=100 height=100></
canvas> </body> </html>

GoalKicker.com – Notas HTML5 Canvas para profesionales 44


Machine Translated by Google

Sección 5.3: llenar (un comando de ruta)


contexto.llenar()

Hace que el interior de la ruta se rellene de acuerdo con el context.fillStyle actual y la ruta rellena se dibuja visualmente en el lienzo.

Antes de ejecutar context.fill (o context.stroke) , la ruta existe en la memoria y aún no se dibuja visualmente en el
lienzo.

Código de ejemplo que usa context.fill() para dibujar una ruta completa en el lienzo:

<!doctype html>
<html>

<cabeza>
<estilo> cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </
estilo> <guión> ventana.onload=(función(){

// variables relacionadas con el


lienzo var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");

ctx.beginPath();
ctx.moveTo(50,30);
ctx.lineTo(75,55);
ctx.lineTo(25,55);
ctx.lineTo(50,30);
ctx.fillStyle='azul';
ctx.llenar();

}); // fin de ventana.onload </


script> </head> <body>
<canvas id="canvas" width=100
height=100></canvas> </
body> </html>

Sección 5.4: clip (un comando de ruta)


contexto.clip

Limita cualquier dibujo futuro para que se muestre solo dentro de la Ruta actual.

Ejemplo: recortar esta imagen en un camino triangular

GoalKicker.com – Notas HTML5 Canvas para profesionales 45


Machine Translated by Google

<!doctype html>
<html> <título>

<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){

// variables relacionadas con el lienzo


var canvas=document.getElementById("canvas"); var
ctx=canvas.getContext("2d");

var img=nueva imagen();


img.onload=inicio;
img.src='http://i.stack.imgur.com/1CqWf.jpg'

function start(){ // dibuja


un camino triangular
ctx.beginPath(); ctx.moveTo(75,50);
ctx.lineTo(125,100);
ctx.lineTo(25,100); ctx.lineTo(75,50);

// recortar futuros dibujos para que aparezcan solo en el triángulo ctx.clip();

// dibujar una imagen


ctx.drawImage(img,0,0);
}

}); // fin de ventana.onload </script>


</head> <body> <canvas
id="canvas" width=150 height=150></
canvas> </body> </html>

GoalKicker.com – Notas HTML5 Canvas para profesionales 46


Machine Translated by Google

Sección 5.5: Descripción general de los comandos básicos de dibujo de rutas: líneas y curvas

Sendero

Una ruta define un conjunto de líneas y curvas que se pueden dibujar visiblemente en el lienzo.

Una ruta no se dibuja automáticamente en el lienzo. Pero las líneas y curvas de la ruta se pueden dibujar en el lienzo usando un trazo que se pueda diseñar. Y la

forma creada por las líneas y curvas también se puede rellenar con un relleno que se pueda diseñar.

Las rutas tienen usos más allá de dibujar en el lienzo:

Hit testing si una coordenada x, y está dentro de la forma de la ruta.

Definición de una región de recorte donde solo serán visibles los dibujos dentro de la región de recorte. Cualquier dibujo fuera de la región de recorte no se

dibujará (==transparente), similar al desbordamiento de CSS.

Los comandos básicos de dibujo de ruta son:

comenzarPath
moveTo

línea a

arco

cuadráticaCurvaTo
bezierCurvaTo

arcto

rectificar

cerrarRuta

Descripción de los comandos básicos de dibujo:

comenzarRuta

contexto.beginPath()

Comienza a ensamblar un nuevo conjunto de comandos de ruta y también descarta cualquier ruta ensamblada previamente.

El descarte es un punto importante ya menudo pasado por alto. Si no comienza una nueva ruta, cualquier comando de ruta emitido anteriormente se volverá a

dibujar automáticamente.

También mueve el "bolígrafo" de dibujo al origen superior izquierdo del lienzo (==coordenada[0,0]).

mover a

context.moveTo(startX, startY)

Mueve la ubicación actual del lápiz a la coordenada [startX,startY].

Por defecto, todos los dibujos de ruta están conectados entre sí. Entonces, el punto final de una línea o curva es el punto inicial de la siguiente línea o curva. Esto

puede hacer que se dibuje una línea inesperada que conecte dos dibujos adyacentes. El comando context.moveTo básicamente "toma el lápiz de dibujo" y lo coloca

en una nueva coordenada para que no se dibuje la línea de conexión automática.

línea a

GoalKicker.com – Notas HTML5 Canvas para profesionales 47


Machine Translated by Google

context.lineTo(endX, endY)

Dibuja un segmento de línea desde la ubicación actual del lápiz para coordinar [endX,endY]

Puede ensamblar varios comandos .lineTo para dibujar una polilínea. Por ejemplo, podría ensamblar 3 segmentos de línea para formar un triángulo.

arco

context.arc(centroX, centroY, radio, ángulo Radiante inicial, ángulo Radiante final)

Dibuja un arco circular dado un punto central, radio y ángulos inicial y final. Los ángulos se expresan en radianes.
Para convertir grados a radianes puedes usar esta fórmula: radianes = grados * Math.PI / 180;.

El ángulo 0 mira directamente hacia la derecha desde el centro del arco. Para dibujar un círculo completo, puede hacer un ángulo final = un ángulo inicial + 360

grados (360 grados == Math.PI2): `context.arc(10,10,20,0,Math.PI2);

De forma predeterminada, el arco se dibuja en el sentido de las agujas del reloj. Un parámetro opcional [verdadero|falso] indica que el arco se dibuje en el
sentido contrario a las agujas del reloj: context.arc(10,10,20,0,Math.PI*2,true)

curva cuadrática a

context.quadraticCurveTo(controlX, controlY, finalX, finalY)

Dibuja una curva cuadrática que comienza en la ubicación actual de la pluma hasta una coordenada final determinada. Otra coordenada de control

determinada determina la forma (curvatura) de la curva.

bezierCurveTo

context.bezierCurveTo(control1X, control1Y, control2X, control2Y, final X, final Y)

Dibuja una curva Bézier cúbica que comienza en la ubicación actual de la pluma hasta una coordenada final determinada. Otras 2 coordenadas de

control dadas determinan la forma (curvatura) de la curva.

arcto

context.arcTo(puntoX1, puntoY1, puntoX2, puntoY2, radio);

Dibuja un arco circular con un radio dado. El arco se dibuja en el sentido de las agujas del reloj dentro de la cuña formada por la ubicación actual de la

pluma y se le asignan dos puntos: Punto1 y Punto2.

Una línea que conecta la ubicación actual de la pluma y el inicio del arco se dibuja automáticamente antes del arco.

rectificar

context.rect(leftX, topY, ancho, alto)

Dibuja un rectángulo dada una esquina superior izquierda y un ancho y alto.

El context.rect es un comando de dibujo único porque agrega rectángulos desconectados. Estos rectángulos desconectados no se conectan automáticamente

mediante líneas.

cerrarRuta

GoalKicker.com – Notas HTML5 Canvas para profesionales 48


Machine Translated by Google

contexto.closePath()

Dibuja una línea desde la ubicación actual de la pluma hasta la coordenada de ruta inicial.

Por ejemplo, si dibuja 2 líneas que forman 2 catetos de un triángulo, closePath "cerrará" el triángulo dibujando el tercer cateto del
triángulo desde el punto final del 2º tramo hasta el punto inicial del primer tramo.

El nombre de este comando a menudo hace que se malinterprete. context.closePath NO es un delimitador final para
context.beginPath. Nuevamente, el comando closePath dibuja una línea, no "cierra" un beginPath.

Sección 5.6: lineTo (un comando de ruta)


context.lineTo(endX, endY)

Dibuja un segmento de línea desde la ubicación actual del lápiz para coordinar [endX,endY]

<!doctype html> <html>

<cabeza>
<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){

// obtener una referencia al elemento canvas y su contexto var


canvas=document.getElementById("canvas"); var ctx=canvas.getContext("2d");

// argumentos
var startX=25; var
inicioY=20; var
endX=125; var
endY=20;

// Dibujar un segmento de una sola línea usando los comandos "mover a" y "línea a" ctx.beginPath();
ctx.moveTo(inicioX,inicioY); ctx.lineTo(finX,finY); ctx.stroke();

}); // fin de ventana.onload </script>


</head> <body> <canvas
id="canvas" width=200 height=150></
canvas> </body>

GoalKicker.com – Notas HTML5 Canvas para profesionales 49


Machine Translated by Google
</html>

Puede ensamblar varios comandos .lineTo para dibujar una polilínea. Por ejemplo, podría ensamblar 3 segmentos de línea para
formar un triángulo.

<!doctype html>
<html> <head> <style>
body{ background-
color:white; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){

// obtener una referencia al elemento canvas y su contexto var


canvas=document.getElementById("canvas"); var ctx=canvas.getContext("2d");

// argumentos
var topVertexX=50; var
topVertexY=20; var vértice
derechoX=75; var
vérticederechoY=70; var
vérticeizquierdoX=25; var
vérticeizquierdoY=70;

// Un conjunto de segmentos de línea dibujados para formar un triángulo usando //


los comandos "moveTo" y múltiples "lineTo" ctx.beginPath();
ctx.moveTo(vérticesuperiorX,vérticesuperiorY);
ctx.lineTo(rightVertexX,rightVertexY); ctx.lineTo(leftVertexX,leftVertexY);
ctx.lineTo(vérticesuperiorX,vérticesuperiorY); ctx.stroke();

}); // fin de ventana.onload </script>


</head> <body> <canvas
id="canvas" width=200 height=150></
canvas> </body> </html>

Sección 5.7: arc (un comando de ruta)


context.arc(centroX, centroY, radio, ángulo Radiante inicial, ángulo Radiante final)

Dibuja un arco circular dado un punto central, radio y ángulos inicial y final. Los ángulos se expresan en radianes.

GoalKicker.com – Notas HTML5 Canvas para profesionales 50


Machine Translated by Google

Para convertir grados a radianes puedes usar esta fórmula: radianes = grados * Math.PI / 180;.

El ángulo 0 mira directamente hacia la derecha desde el centro del arco.

De forma predeterminada, el arco se dibuja en el sentido de las agujas del reloj. Un parámetro opcional [verdadero|falso] indica que el arco se dibuje en el
sentido contrario a las agujas del reloj: context.arc(10,10,20,0,Math.PI*2,true)

<!doctype html>
<html> <título>

<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </
estilo> <guión> ventana.onload=(función(){

// obtener una referencia al elemento canvas y su contexto var


canvas=document.getElementById("canvas"); var
ctx=canvas.getContext("2d");

// argumentos
var centerX=50;
var centroY=50;
radio var =30; var
comenzandoRadianAngle=Math.PI*2*; // comienza en 90 grados == centerY+radius var
endingRadianAngle=Math.PI*2*.75; // termina en 270 grados (==PI*2*.75 en radianes)

// Un círculo parcial (es decir, un arco) dibujado con el comando "arc"


ctx.beginPath(); ctx.arc(centroX, centroY, radio, ángulo Radiante inicial,
ángulo Radiante final); ctx.stroke();

}); // fin de ventana.onload </


script> </head> <body>
<canvas id="canvas" width=200
height=150></canvas> </
body> </html>

Para dibujar un círculo completo, puede hacer un ángulo final = un ángulo inicial + 360 grados (360 grados == Math.PI2).

GoalKicker.com – Notas HTML5 Canvas para profesionales 51


Machine Translated by Google

<!doctype html>
<html> <título>

<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){

// obtener una referencia al elemento canvas y su contexto var


canvas=document.getElementById("canvas"); var ctx=canvas.getContext("2d");

// argumentos
var centerX=50; var
centroY=50; radio var
=30; var
inicioRadianAngle=0; // comienza en 0 grados var endingRadianAngle=Math.PI*2; //
termina en 360 grados (==PI*2 en radianes)

// Un círculo completo dibujado usando el comando "arc" ctx.beginPath();


ctx.arc(centroX, centroY, radio, ángulo Radiante inicial, ángulo Radiante
final); ctx.stroke();

}); // fin de ventana.onload </script>


</head> <body> <canvas
id="canvas" width=200 height=150></
canvas> </body> </html>

Sección 5.8: quadraticCurveTo (un comando de ruta)


context.quadraticCurveTo(controlX, controlY, finalX, finalY)

Dibuja una curva cuadrática que comienza en la ubicación actual de la pluma hasta una coordenada final dada. Otra coordenada
de control determinada determina la forma (curvatura) de la curva.

GoalKicker.com – Notas HTML5 Canvas para profesionales 52


Machine Translated by Google

<!doctype html>
<html> <título>

<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){

// obtener una referencia al elemento canvas y su contexto var


canvas=document.getElementById("canvas"); var ctx=canvas.getContext("2d");

// argumentos
var startX=25; var
inicioY=70;
varcontrolX =75;
varcontrolY =25; var
endX=125; var endY=70;

// Una curva cuadrática dibujada usando los comandos "moveTo" y "quadraticCurveTo" ctx.beginPath();
ctx.moveTo(inicioX,inicioY); ctx.quadraticCurveTo(controlX,controlY,endX,endY); ctx.stroke();

}); // fin de ventana.onload </script>


</head> <body> <canvas
id="canvas" width=200 height=150></
canvas> </body> </html>

Sección 5.9: bezierCurveTo (un comando de ruta)


context.bezierCurveTo(control1X, control1Y, control2X, control2Y, final X, final Y)

Dibuja una curva Bézier cúbica que comienza en la ubicación actual de la pluma hasta una coordenada final dada. Otras 2
coordenadas de control dadas determinan la forma (curvatura) de la curva.

GoalKicker.com – Notas HTML5 Canvas para profesionales 53


Machine Translated by Google

<!doctype html> <html>

<cabeza>
<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){

// obtener una referencia al elemento canvas y su contexto var


canvas=document.getElementById("canvas"); var ctx=canvas.getContext("2d");

// argumentos
var startX=25; var
inicioY=50;
varcontrolX1 =75;
varcontrolY1 =10;
varcontrolX2 =75;
varcontrolY2 =90; var
endX=125; var endY=50;

// Una curva bezier cúbica dibujada usando los comandos "moveTo" y "bezierCurveTo" ctx.beginPath();
ctx.moveTo(inicioX,inicioY); ctx.bezierCurveTo(controlX1,controlY1,controlX2,controlY2,endX,endY);
ctx.stroke();

}); // fin de ventana.onload </script>


</head> <body> <canvas
id="canvas" width=200 height=150></
canvas> </body> </html>

Sección 5.10: arcTo (un comando de ruta)


context.arcTo(puntoX1, puntoY1, puntoX2, puntoY2, radio);

Dibuja un arco circular con un radio dado. El arco se dibuja en el sentido de las agujas del reloj dentro de la cuña formada por la ubicación
actual de la pluma y se le asignan dos puntos: Punto1 y Punto2.

Una línea que conecta la ubicación actual de la pluma y el inicio del arco se dibuja automáticamente antes del arco.

GoalKicker.com – Notas HTML5 Canvas para profesionales 54


Machine Translated by Google

<!doctype html>
<html> <título>

<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){

// obtener una referencia al elemento canvas y su contexto var


canvas=document.getElementById("canvas"); var ctx=canvas.getContext("2d");

// argumentos
var pointX0=25; var
puntoY0=80; var
puntoX1=75; var
puntoY1=0; var
puntoX2=125; var
puntoY2=80; radio var
=25;

// Un arco circular dibujado usando el comando "arcTo". La línea se dibuja automáticamente. ctx.beginPath();
ctx.moveTo(puntoX0,puntoY0); ctx.arcTo(puntoX1, puntoY1, puntoX2, puntoY2, radio); ctx.stroke();

}); // fin de ventana.onload </script>


</head> <body> <canvas
id="canvas" width=200 height=150></
canvas> </body> </html>

Sección 5.11: rect (un comando de ruta)


context.rect(leftX, topY, ancho, alto)

Dibuja un rectángulo dada una esquina superior izquierda y un ancho y alto.

GoalKicker.com – Notas HTML5 Canvas para profesionales 55


Machine Translated by Google

<!doctype html>
<html> <título>

<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){

// obtener una referencia al elemento canvas y su contexto var


canvas=document.getElementById("canvas"); var ctx=canvas.getContext("2d");

// argumentos
var leftX=25; var
topY=25; ancho de
variable = 40; var
altura=25;

// Un rectángulo dibujado usando el comando "rect". ctx.beginPath();


ctx.rect(leftX, topY, ancho, alto); ctx.stroke();

}); // fin de ventana.onload </script>


</head> <body> <canvas
id="canvas" width=200 height=150></
canvas> </body> </html>

El context.rect es un comando de dibujo único porque agrega rectángulos desconectados.

Estos rectángulos desconectados no se conectan automáticamente mediante líneas.

<!doctype html> <html>

<cabeza>

GoalKicker.com – Notas HTML5 Canvas para profesionales 56


Machine Translated by Google

<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){

// obtener una referencia al elemento canvas y su contexto var


canvas=document.getElementById("canvas"); var ctx=canvas.getContext("2d");

// argumentos
var leftX=25; var
topY=25; ancho de
variable = 40; var
altura=25;

// Múltiples rectángulos dibujados usando el comando "rect". ctx.beginPath();


ctx.rect(leftX, topY, ancho, alto); ctx.rect(leftX+50, topY+20, ancho, alto);
ctx.rect(leftX+100, topY+40, ancho, alto); ctx.stroke();

}); // fin de ventana.onload </script>


</head> <body> <canvas
id="canvas" width=200 height=150></
canvas> </body> </html>

Sección 5.12: closePath (un comando de ruta)


contexto.closePath()

Dibuja una línea desde la ubicación actual de la pluma hasta la coordenada de ruta inicial.

Por ejemplo, si dibuja 2 líneas que forman 2 catetos de un triángulo, closePath "cerrará" el triángulo dibujando el tercer cateto del triángulo
desde el punto final del 2º tramo hasta el punto inicial del primer tramo.

¡Un concepto erróneo explicado!

El nombre de este comando a menudo hace que se malinterprete.

context.closePath NO es un delimitador final para context.beginPath.

Nuevamente, el comando closePath dibuja una línea, no "cierra" un beginPath.

Este ejemplo dibuja 2 catetos de un triángulo y usa closePath para completar (¿cerrar?!) el triángulo dibujando el tercer cateto. Lo que
closePath está haciendo en realidad es dibujar una línea desde el punto final del segundo tramo hasta el punto inicial del primer tramo.

GoalKicker.com – Notas HTML5 Canvas para profesionales 57


Machine Translated by Google

<!doctype html>
<html> <título>

<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){

// obtener una referencia al elemento canvas y su contexto var


canvas=document.getElementById("canvas"); var ctx=canvas.getContext("2d");

// argumentos
var topVertexX=50; var
topVertexY=50; var vértice
derechoX=75; var
vérticederechoY=75; var
vérticeizquierdoX=25; var
vérticeizquierdoY=75;

// Un conjunto de segmentos de línea dibujados para formar un triángulo usando //


los comandos "moveTo" y múltiples "lineTo" ctx.beginPath();
ctx.moveTo(vérticesuperiorX,vérticesuperiorY);
ctx.lineTo(rightVertexX,rightVertexY); ctx.lineTo(leftVertexX,leftVertexY);

// closePath dibuja el tercer lado del triángulo ctx.closePath()

ctx.stroke();

}); // fin de ventana.onload </script>


</head> <body> <canvas
id="canvas" width=200 height=150></
canvas> </body> </html>

Sección 5.13: beginPath (un comando de ruta)


contexto.beginPath()

Comienza a ensamblar un nuevo conjunto de comandos de ruta y también descarta cualquier ruta ensamblada previamente.

También mueve el "bolígrafo" de dibujo al origen superior izquierdo del lienzo (==coordenada[0,0]).

GoalKicker.com – Notas HTML5 Canvas para profesionales 58


Machine Translated by Google

Aunque es opcional, SIEMPRE debe comenzar una ruta con beginPath

El descarte es un punto importante ya menudo pasado por alto. Si no comienza una nueva ruta con beginPath, cualquier comando
de ruta emitido anteriormente se volverá a dibujar automáticamente.

Estas 2 demostraciones intentan dibujar una "X" con un trazo rojo y un trazo azul.

Esta primera demostración utiliza correctamente beginPath para iniciar su segundo trazo rojo. El resultado es que la "X" tiene correctamente
un trazo rojo y uno azul.

<!doctype html>
<html> <título>

<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </
estilo> <guión> ventana.onload=(función(){

// obtener una referencia al elemento canvas y su contexto var


canvas=document.getElementById("canvas"); var
ctx=canvas.getContext("2d");

// dibujar una línea azul


ctx.beginPath();
ctx.moveTo(30,30);
ctx.lineTo(100,100);
ctx.strokeStyle='azul';
ctx.lineWidth=3; ctx.stroke();

// dibujar una línea roja


ctx.beginPath(); // ¡Importante para comenzar un nuevo camino!
ctx.moverA(100,30);
ctx.lineTo(30,100);
ctx.strokeStyle='rojo';
ctx.lineWidth=3; ctx.stroke();

}); // fin de ventana.onload </


script> </head> <body>
<canvas id="canvas" width=200
height=150></canvas> </
body> </html>

Esta segunda demostración omite incorrectamente beginPath en el segundo trazo. El resultado es que la "X" tiene incorrectamente

GoalKicker.com – Notas HTML5 Canvas para profesionales 59


Machine Translated by Google

ambos trazos rojos.

El segundo trazo() dibuja el segundo trazo rojo.

Pero sin un segundo beginPath, ese mismo segundo trazo() también vuelve a dibujar incorrectamente el primer trazo.

Dado que el segundo trazo() ahora tiene un estilo rojo, el primer trazo azul se sobrescribe con un trazo rojo de color incorrecto.

<!doctype html>
<html> <head>
<style>
body{ background-
color:white; } #canvas{border:1px rojo
sólido; } </estilo> <guión>
ventana.onload=(función(){

// obtener una referencia al elemento canvas y su contexto var


canvas=document.getElementById("canvas"); var
ctx=canvas.getContext("2d");

// dibujar una línea


azul ctx.beginPath();
ctx.moveTo(30,30);
ctx.lineTo(100,100);
ctx.strokeStyle='azul';
ctx.lineWidth=3; ctx.stroke();

// dibuja una línea


roja // Nota: ¡falta el 'beginPath' necesario!
ctx.moverA(100,30); ctx.lineTo(30,100);
ctx.strokeStyle='rojo'; ctx.lineWidth=3; ctx.stroke();

}); // fin de ventana.onload </


script> </head> <body>
<canvas id="canvas"
width=200 height=150></
canvas> </body> </html>

GoalKicker.com – Notas HTML5 Canvas para profesionales 60


Machine Translated by Google

Sección 5.14: lineCap (un atributo de estilo de ruta)


context.lineCap=capStyle // tope (predeterminado), redondo, cuadrado

Establece el estilo de tapa de los puntos de inicio y finalización de la línea.

butt, el estilo lineCap predeterminado, muestra mayúsculas cuadradas que no se extienden más allá de los puntos inicial y final de la
línea.

round, muestra extremos redondeados que se extienden más allá de los puntos inicial y final de la línea.

cuadrado, muestra extremos cuadrados que se extienden más allá de los puntos inicial y final de la línea.

<!doctype html>
<html> <título>

<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </
estilo> <guión> ventana.onload=(función(){

// variables relacionadas con el


lienzo var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d"); ctx.lineWidth=15;

// lineCap por defecto: butt


ctx.lineCap='butt';
dibujarLínea(50,40,200,40);

// lineCap: redondo
ctx.lineCap='redondo';
dibujarLínea(50,70,200,70);

// lineCap: cuadrado
ctx.lineCap='cuadrado';
dibujarLínea(50,100,200,100);

// función de utilidad para dibujar una línea


function drawLine(startX,startY,endX,endY)
{ ctx.beginPath(); ctx.moveTo(inicioX,inicioY);
ctx.lineTo(finX,finY); ctx.stroke();

// Solo para demostración,

GoalKicker.com – Notas HTML5 Canvas para profesionales 61


Machine Translated by Google

// Reglas para mostrar qué lineCaps se extienden más allá de los puntos finales
ctx.lineWidth=1; ctx.strokeStyle='rojo'; dibujarLínea(50,20,50,120);
DibujarLínea(200,20,200,120);

}); // fin de ventana.onload </script>


</head> <body> <canvas
id="canvas" width=300 height=200></
canvas> </body> </html>

Sección 5.15: lineJoin (un atributo de estilo de ruta)


context.lineJoin=joinStyle // inglete (predeterminado), redondo, bisel

Establece el estilo utilizado para conectar segmentos de línea contiguos.

miter, el valor predeterminado, une segmentos de línea con una junta


afilada. redondo, une segmentos de línea con una junta redondeada.
bisel, une segmentos de línea con una junta roma.

<!doctype html> <html>

<cabeza>
<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){

// variables relacionadas con el lienzo


var canvas=document.getElementById("canvas"); var
ctx=canvas.getContext("2d"); ctx.lineWidth=15;

// lineJoin: mitre (predeterminado)


ctx.lineJoin='miter'; dibujarPolyline(50,30);

// lineJoin: ronda
ctx.lineJoin='ronda';
dibujarPolyline(50,80);

// lineJoin: bisel

GoalKicker.com – Notas HTML5 Canvas para profesionales 62


Machine Translated by Google

ctx.lineJoin='bisel';
dibujarPolyline(50,130);

// utilidad para dibujar la función de


polilínea drawPolyline(x,y){ ctx.beginPath();
ctx.mover a(x,y);
ctx.líneaA(x+30,y+30);
ctx.lineTo(x+60,y);
ctx.líneaA(x+90,y+30); ctx.stroke();

}); // fin de ventana.onload </script>


</head> <body> <canvas
id="canvas" width=300 height=200></
canvas> </body> </html>

Sección 5.16: strokeStyle (un atributo de estilo de ruta)


context.strokeStyle=color

Establece el color que se utilizará para trazar el contorno de la ruta actual.

Estas son opciones de color (deben citarse):

Un CSS llamado color, por ejemplo context.strokeStyle='red'

Un color hexadecimal, por ejemplo context.strokeStyle='#FF0000'

Un color RGB, por ejemplo context.strokeStyle='rgb(red,green,blue)' donde rojo, verde y azul son números enteros
0-255 que indican la intensidad de cada color componente.

Un color HSL, por ejemplo context.strokeStyle='hsl(hue,saturation,lightness)' donde el tono es un número entero 0-360
en la rueda de color y la saturación y la luminosidad son porcentajes (0-100%) que indican la fuerza de cada componente .

Un color HSLA, por ejemplo context.strokeStyle='hsl(hue,saturation,lightness,alpha)' donde el tono es un número entero


0-360 en la rueda de color y la saturación y la luminosidad son porcentajes (0-100%) que indican la fuerza de cada componente
y alfa es un valor decimal 0.00-1.00 que indica la opacidad.

También puede especificar estas opciones de color (estas opciones son objetos creados por el contexto):

Un degradado lineal que es un objeto de degradado lineal creado con context.createLinearGradient

Un degradado radial que es un objeto de degradado radial creado con context.createRadialGradient

Un patrón que es un objeto de patrón creado con context.createPattern

GoalKicker.com – Notas HTML5 Canvas para profesionales 63


Machine Translated by Google

<!doctype html>
<html> <título>

<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){

// variables relacionadas con el lienzo


var canvas=document.getElementById("canvas"); var
ctx=canvas.getContext("2d"); ctx.lineWidth=15;

// trazo usando un color CSS: named, RGB, HSL, etc ctx.strokeStyle='red';


dibujarLínea(50,40,250,40);

// trazo usando un degradado lineal var


degradado = ctx.createLinearGradient(75,75,175,75);
degradado.addColorStop(0,'rojo'); degradado.addColorStop(1,'verde');
ctx.strokeStyle=gradiente; dibujarLínea(50,75,250,75);

// trazo usando un degradado radial var


degradado = ctx.createRadialGradient(100,110,15,100,110,45); degradado.addColorStop(0,'rojo');
degradado.addColorStop(1,'verde'); ctx.strokeStyle=gradiente; ctx.lineWidth=20;
DibujarLínea(50,110,250,110);

// trazo usando un patrón var


patternImage=new Image();
patternImage.onload=function(){ var pattern =
ctx.createPattern(patternImage,'repeat'); ctx.strokeStyle=patrón;
dibujarLínea(50,150,250,150);

} patternImage.src='https://dl.dropboxusercontent.com/u/139992952/stackoverflow/BooMu1.png';

// solo para demostración, dibujar etiquetas por cada trazo


ctx.textBaseline='middle'; ctx.font='14px arial'; ctx.fillText(' Color
CSS',265,40); ctx.fillText('Color de degradado lineal ' ,265,75);

GoalKicker.com – Notas HTML5 Canvas para profesionales 64


Machine Translated by Google

ctx.fillText('Color degradado radial ' ,265,110); ctx.fillText(' Color del


patrón',265,150);

// utilidad para dibujar una función


de línea drawLine(startX,startY,endX,endY){ ctx.beginPath();
ctx.moveTo(inicioX,inicioY); ctx.lineTo(finX,finY); ctx.stroke();

}); // fin de ventana.onload </script>


</head> <body> <canvas
id="canvas" width=425 height=200></
canvas> </body> </html>

Sección 5.17: fillStyle (un atributo de estilo de ruta)


context.fillStyle=color

Establece el color que se utilizará para rellenar el interior de la ruta actual.

Estas son opciones de color (deben citarse):

Un CSS llamado color, por ejemplo context.fillStyle='red'

Un color hexadecimal, por ejemplo context.fillStyle='#FF0000'

Un color RGB, por ejemplo context.fillStyle='rgb(red,green,blue)' donde rojo, verde y azul son números enteros 0-255
que indican la fuerza de cada color componente.

Un color HSL, por ejemplo context.fillStyle='hsl(hue,saturation,lightness)' donde el tono es un número entero 0-360 en la
rueda de color y la saturación y la luminosidad son porcentajes (0-100%) que indican la fuerza de cada componente .

Un color HSLA, por ejemplo context.fillStyle='hsl(hue,saturation,lightness,alpha)' donde el tono es un número entero 0-360 en
la rueda de color y la saturación y la luminosidad son porcentajes (0-100%) que indican la fuerza de cada componente y alfa
es un valor decimal 0.00-1.00 que indica la opacidad.

También puede especificar estas opciones de color (estas opciones son objetos creados por el contexto):

Un degradado lineal que es un objeto de degradado lineal creado con context.createLinearGradient

Un degradado radial que es un objeto de degradado radial creado con context.createRadialGradient

Un patrón que es un objeto de patrón creado con context.createPattern

GoalKicker.com – Notas HTML5 Canvas para profesionales sesenta y cinco


Machine Translated by Google

<!doctype html>
<html> <título>

<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){

// variables relacionadas con el lienzo


var canvas=document.getElementById("canvas"); var
ctx=canvas.getContext("2d");

// trazo usando un color CSS: named, RGB, HSL, etc ctx.fillStyle='red';


ctx.fillRect(50,50,100,50);

// trazo usando un degradado lineal var


degradado = ctx.createLinearGradient(225,50,300,50);
degradado.addColorStop(0,'rojo'); degradado.addColorStop(1,'verde');
ctx.fillStyle=gradiente; ctx.fillRect(200,50,100,50);

// trazo usando un degradado radial var


degradado = ctx.createRadialGradient(100,175,5,100,175,30); degradado.addColorStop(0,'rojo');
degradado.addColorStop(1,'verde'); ctx.fillStyle=gradiente; ctx.fillRect(50,150,100,50);

// trazo usando un patrón var


patternImage=new Image();
patternImage.onload=function(){ var pattern =
ctx.createPattern(patternImage,'repeat'); ctx.fillStyle=patrón;
ctx.fillRect(200,150,100,50);

} patternImage.src='http://i.stack.imgur.com/ixrWe.png';

// solo para demostración, dibujar etiquetas por cada trazo


ctx.fillStyle='black'; ctx.textAlign='centro'; ctx.textBaseline='medio';

GoalKicker.com – Notas HTML5 Canvas para profesionales 66


Machine Translated by Google

ctx.font='14px arial'; ctx.fillText('


Color CSS',100,40); ctx.fillText('Color de
degradado lineal ' ,250,40); ctx.fillText('Color degradado radial '
,100,140); ctx.fillText(' Color del patrón',250,140);

}); // fin de ventana.onload </script>


</head> <body> <canvas
id="canvas" width=350 height=250></
canvas> </body> </html>

Sección 5.18: ancho de línea (un atributo de estilo de ruta)


context.lineWidth=lineWidth

Establece el ancho de la línea que trazará el contorno del camino.

<!doctype html>
<html> <título>

<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){

// variables relacionadas con el lienzo


var canvas=document.getElementById("canvas"); var
ctx=canvas.getContext("2d");

ctx.lineWidth=1;
dibujarPolyline(50,50);

ctx.lineWidth=5;
dibujarPolyline(50,100);

ctx.lineWidth=10;
dibujarPolyline(50,150);

GoalKicker.com – Notas HTML5 Canvas para profesionales 67


Machine Translated by Google

// utilidad para dibujar una función de


polilínea drawPolyline(x,y){ ctx.beginPath();
ctx.mover a(x,y); ctx.líneaA(x+30,y+30);
ctx.lineTo(x+60,y);
ctx.líneaA(x+90,y+30); ctx.stroke();

}); // fin de ventana.onload </script>


</head> <body> <canvas
id="canvas" width=350 height=250></
canvas> </body> </html>

Sección 5.19: shadowColor, shadowBlur, shadowOÿsetX,


shadowOÿsetY (atributos de estilo de ruta)
shadowColor = color // color CSS //
shadowBlur = ancho ancho de desenfoque entero
shadowOffsetX = distancia // la sombra se mueve horizontalmente por este desplazamiento shadowOffsetY
= distancia // la sombra se mueve verticalmente por este desplazamiento

Este conjunto de atributos agregará una sombra alrededor de una ruta.

Tanto los trazados rellenos como los trazados trazados pueden tener una sombra.

La sombra es más oscura (opaca) en el perímetro de la ruta y se vuelve gradualmente más clara a medida que se aleja del perímetro de la ruta.

shadowColor indica qué color CSS se usará para crear la sombra.

shadowBlur es la distancia sobre la cual la sombra se extiende hacia afuera desde la ruta. shadowOffsetX es
una distancia por la cual la sombra se desplaza horizontalmente alejándose de la ruta. Una distancia positiva mueve la sombra hacia la
derecha, una distancia negativa mueve la sombra hacia la izquierda. shadowOffsetY es una distancia en la que la sombra se desplaza
verticalmente alejándose de la ruta. Una distancia positiva mueve la sombra hacia abajo, una distancia negativa mueve la sombra hacia
arriba.

Acerca de shadowOffsetX y shadowOffsetY

Es importante tener en cuenta que toda la sombra se desplaza en su totalidad. Esto hará que parte de la sombra se desplace por debajo de las
rutas rellenas y, por lo tanto, parte de la sombra no será visible.

Acerca de los trazos sombreados

Al sombrear un trazo, se sombrean tanto el interior como el exterior del trazo. La sombra es más oscura en el trazo y se aclara a medida que la sombra
se extiende hacia afuera en ambas direcciones desde el trazo.

Desactivar el sombreado cuando haya terminado

Una vez que haya dibujado sus sombras, es posible que desee desactivar las sombras para dibujar más caminos. Para desactivar el sombreado,
establezca shadowColor en transparente.

contexto.shadowColor = 'rgba(0,0,0,0)';

GoalKicker.com – Notas HTML5 Canvas para profesionales 68


Machine Translated by Google

Consideraciones de rendimiento

Las sombras (como los degradados) requieren cálculos extensos y, por lo tanto, debe usar las sombras con moderación.

Tenga especial cuidado al animar porque dibujar sombras muchas veces por segundo tendrá un gran impacto en el rendimiento. Una
solución si necesita animar rutas sombreadas es crear previamente la ruta sombreada en un segundo "lienzo de sombras". El lienzo de
sombra es un lienzo normal que se crea en la memoria con document.createElement ; no se agrega al DOM (es solo un lienzo de preparación).
Luego dibuje el lienzo de sombra en el lienzo principal. Esto es mucho más rápido porque no es necesario realizar los cálculos de sombras
muchas veces por segundo. Todo lo que está haciendo es copiar un lienzo preconstruido en su lienzo visible.

<!doctype html>
<html>

<cabeza>
<estilo> cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </
estilo> <guión> ventana.onload=(función(){

// variables relacionadas con el


lienzo var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");

// trazo sombreado
ctx.shadowColor='black';
ctx.shadowBlur=6;
ctx.strokeStyle='rojo';
ctx.strokeRect(50,50,100,50); //
oscurecer la sombra acariciando por segunda vez
ctx.strokeRect(50,50,100,50);

// relleno sombreado
ctx.shadowColor='black';
ctx.shadowBlur=10;
ctx.fillStyle='rojo';
ctx.fillRect(225,50,100,50); //
oscurecer la sombra acariciando por segunda vez
ctx.fillRect(225,50,100,50);

GoalKicker.com – Notas HTML5 Canvas para profesionales 69


Machine Translated by Google

// la sombra se desplaza hacia la derecha y hacia abajo


ctx.shadowColor='black'; ctx.shadowBlur=10;
ctx.shadowOffsetX=5; ctx.shadowOffsetY=5; ctx.fillStyle='rojo';
ctx.fillRect(50,175,100,50);

// un desenfoque más amplio (== se extiende más allá de la ruta)


ctx.shadowColor='black'; ctx.shadowBlur=35; ctx.fillStyle='rojo';
ctx.fillRect(225,175,100,50);

// ¡Siempre limpia! Desactive el sombreado


ctx.shadowColor='rgba(0,0,0,0)';

}); // fin de ventana.onload </script>


</head> <body> <canvas
id="canvas" width=400 height=300></
canvas> </body> </html>

Sección 5.20: createLinearGradient (crea un objeto de


estilo de ruta)
var gradiente = createLinearGradient( startX, startY, endX, endY )
gradiente.addColorStop(gradientPercentPosition, CssColor)
gradiente.addColorStop(gradientPercentPosition, CssColor) [opcionalmente agregue más paradas
de color para agregar a la variedad del degradado]

Crea un degradado lineal reutilizable (objeto).

El objeto se puede asignar a cualquier estilo de trazo y/o estilo de relleno.

Luego, stroke() o fill() colorearán la ruta con los colores degradados del objeto.

La creación de un objeto degradado es un proceso de 2 pasos:

1. Cree el propio objeto degradado. Durante la creación, define una línea en el lienzo donde comenzará y terminará el degradado. El objeto
degradado se crea con var degradado = context.createLinearGradient.
2. Luego agregue 2 (o más) colores que componen el degradado. Esto se hace agregando varias paradas de color al objeto
degradado con degradado.addColorStop.

Argumentos:

startX,startY es la coordenada del lienzo donde comienza el degradado. En el punto de inicio (y antes), el lienzo es
sólidamente del color de la posición de porcentaje de gradiente más baja.

endX,endY es la coordenada del lienzo donde termina el degradado. En el punto final (y después), el lienzo es sólidamente
del color del gradientePercentPosition más alto.

gradientePercentPosition es un número flotante entre 0,00 y 1,00 asignado a una parada de color. Es básicamente un punto
de ruta porcentual a lo largo de la línea donde se aplica esta parada de color en particular.

El degradado comienza en el porcentaje 0,00, que es [startX,startY] en el lienzo.

GoalKicker.com – Notas HTML5 Canvas para profesionales 70


Machine Translated by Google

El degradado termina en el porcentaje 1,00, que es [endX,endY] en el lienzo.


Nota técnica: El término "porcentaje" no es técnicamente correcto ya que los valores van de 0,00 a 1,00 en lugar de 0% a 100%.

CssColor es un color CSS asignado a esta parada de color en particular.

El objeto degradado es un objeto que puede usar (¡y reutilizar!) para hacer que los trazos y rellenos de su ruta se vuelvan de color degradado.

Nota al margen: el objeto degradado no es interno al elemento Canvas ni es Contexto. Es un objeto JavaScript separado y reutilizable que puede
asignar a cualquier ruta que desee. Incluso puede usar este objeto para colorear una ruta en un elemento de lienzo diferente (!)

Las paradas de color son puntos de ruta (porcentaje) a lo largo de la línea de degradado. En cada punto de ruta de parada de color, el degradado
está completamente (==opaco) coloreado con su color asignado. Los puntos intermedios a lo largo de la línea de degradado entre las paradas de color
se colorean como degradados de este color y el anterior.

¡Consejo importante sobre los degradados de Canvas!

Cuando crea un objeto de degradado, todo el lienzo se rellena de forma "invisible" con ese degradado.

Cuando acaricias() o rellenas() una ruta, el degradado invisible se revela, pero solo se revela sobre la ruta que se está acariciando o rellenando.

1. Si crea un degradado lineal de rojo a magenta como este:

// crea un gradiente lineal var


gradiente = ctx.createLinearGradient (100,0, lienzo. ancho-100,0);
degradado.addColorStop(0,'rojo'); degradado.addColorStop(1,'magenta');
ctx.fillStyle=gradiente;

2. Luego, Canvas verá "invisiblemente" su creación de degradado de esta manera:

3. Pero hasta que acaricie() o rellene() con el degradado, no verá ninguno de los degradados en el lienzo.

4. Finalmente, si traza o rellena una ruta con el degradado, el degradado "invisible" se vuelve visible en el lienzo, pero solo donde se dibuja la ...

ruta.

GoalKicker.com – Notas HTML5 Canvas para profesionales 71


Machine Translated by Google

<!doctype html>
<html> <head> <style>
body{ background-
color:white; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){

// variables relacionadas con el lienzo


var canvas=document.getElementById("canvas"); var
ctx=canvas.getContext("2d");

// Crear un gradiente lineal // Nota: No


aparece nada visualmente durante este proceso var
gradiente=ctx.createLinearGradient(100,0,canvas.width-100,0); degradado.addColorStop(0,'rojo');
degradado.addColorStop(1,'magenta');

// Crear una ruta de polilínea // Nota:


Nada aparece visualmente durante este proceso var x=20; var y=40;
ctx.lineCap='redondo'; ctx.lineJoin='ronda'; ctx.lineWidth=15; ctx.beginPath();
ctx.mover a(x,y); ctx.líneaA(x+30,y+50); ctx.lineTo(x+60,y); ctx.líneaA(x+90,y+50);
ctx.lineTo(x+120,y); ctx.líneaA(x+150,y+50); ctx.lineTo(x+180,y);
ctx.líneaA(x+210,y+50); ctx.lineTo(x+240,y); ctx.líneaA(x+270,y+50);
ctx.lineTo(x+300,y); ctx.líneaA(x+330,y+50); ctx.lineTo(x+360,y);

// Establecer el estilo de trazo para que sea el degradado //


Nota: No aparece nada visualmente durante este proceso ctx.strokeStyle=gradient;

// traza el camino // ¡POR


FIN! La ruta trazada con degradado es visible en el lienzo ctx.stroke();

}); // finaliza ventana.onload

GoalKicker.com – Notas HTML5 Canvas para profesionales 72


Machine Translated by Google

</script> </
head>
<cuerpo>
<lienzo id="lienzo" ancho=400 alto=150></lienzo> </cuerpo> </html>

Sección 5.21: createRadialGradient (crea un objeto de


estilo de ruta)
var gradiente = crearGradienteRadial( centroX1,
centroY1, radio1, centroX2, centroY2, radio2 // este es el círculo de "pantalla" // este es el
círculo de "emisión de luz"

) gradient.addColorStop(gradientPercentPosition, CssColor)
gradient.addColorStop(gradientPercentPosition, CssColor) [opcionalmente agregue
más paradas de color para agregar a la variedad del degradado]

Crea un degradado radial reutilizable (objeto). El objeto degradado es un objeto que puede usar (¡y reutilizar!) para hacer que los trazos y rellenos de su
ruta se vuelvan de color degradado.

Sobre...

El degradado radial de Canvas es extremadamente diferente de los degradados radiales tradicionales.

La definición "oficial" (¡casi indescifrable!) del degradado radial de Canvas se encuentra al final de esta publicación. ¡¡No lo mires si tienes una disposición
débil!!

En términos (casi comprensibles):

El degradado radial tiene 2 círculos: un círculo de "fundición" y un círculo de "visualización".


El círculo de lanzamiento proyecta luz en el círculo de visualización.
Esa luz es el gradiente.
La forma de ese gradiente de luz está determinada por el tamaño relativo y la posición de ambos círculos.

La creación de un objeto degradado es un proceso de 2 pasos:

1. Cree el propio objeto degradado. Durante la creación, define una línea en el lienzo donde comenzará y terminará el degradado. El objeto
degradado se crea con var degradado = context.radialLinearGradient.
2. Luego agregue 2 (o más) colores que componen el degradado. Esto se hace agregando varias paradas de color al objeto degradado con
degradado.addColorStop.

Argumentos:

centerX1,centerY1,radius1 define un primer círculo donde se mostrará el degradado.

centerX2,centerY2,radius2 define un segundo círculo que arroja luz degradada en el primer círculo.

gradientePercentPosition es un número flotante entre 0,00 y 1,00 asignado a una parada de color. Es básicamente un punto de referencia
porcentual que define dónde se aplica esta parada de color particular a lo largo del degradado.

El gradiente comienza en el porcentaje 0.00.


El gradiente termina en el porcentaje 1,00.
Nota técnica: El término "porcentaje" no es técnicamente correcto ya que los valores van de 0,00 a 1,00 en lugar de 0% a 100%.

GoalKicker.com – Notas HTML5 Canvas para profesionales 73


Machine Translated by Google

CssColor es un color CSS asignado a esta parada de color en particular.

Nota al margen: el objeto degradado no es interno al elemento Canvas ni es Contexto. Es un objeto JavaScript separado y reutilizable que puede
asignar a cualquier ruta que desee. Incluso puede usar este objeto para colorear una ruta en un elemento de lienzo diferente (!)

Las paradas de color son puntos de ruta (porcentaje) a lo largo de la línea de degradado. En cada punto de ruta de parada de color, el
degradado está completamente (==opaco) coloreado con su color asignado. Los puntos intermedios a lo largo de la línea de degradado entre las
paradas de color se colorean como degradados de este color y el anterior.

¡Consejo importante sobre los degradados de Canvas!

Cuando crea un objeto degradado, todo el degradado radial se proyecta "invisiblemente" sobre el lienzo.

Cuando acaricias() o rellenas() una ruta, el degradado invisible se revela, pero solo se revela sobre la ruta que se está acariciando o rellenando.

1. Si crea un degradado radial de verde a rojo como este:

// crea un gradiente radial var


x1=150; var y1=150; varx2
=280; var y2=150; var r1=100;
var r2=120; var

gradiente=ctx.createRadialGradient(x1,y1,r1,x2,y2,r2);
degradado.addColorStop(0,'rojo'); degradado.addColorStop(1,'verde');
ctx.fillStyle=gradiente;

2. Luego, Canvas verá "invisiblemente" su creación de degradado de esta manera:

3. Pero hasta que acaricie() o rellene() con el degradado, no verá ninguno de los degradados en el lienzo.

4. Finalmente, si traza o rellena una ruta con el degradado, el degradado "invisible" se vuelve visible en el lienzo. ...

GoalKicker.com – Notas HTML5 Canvas para profesionales 74


Machine Translated by Google

pero sólo donde se dibuja el camino.

<!doctype html>
<html> <título>

<estilo>
cuerpo{ color de fondo:blanco; relleno: 10px; }
#canvas{border:1px azul sólido; } </estilo> <guión>
ventana.onload=(función(){

// variables relacionadas
con el lienzo var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");

// crea un degradado radial var


x1=150; var y1=175; varx2 =350;
var y2=175; var r1=100; var
r2=40; x2=x1; var

gradiente=ctx.createRadialGradient(x1,y1,r1,x2,y2,r2);
degradado.addColorStop(0,'rojo'); degradado.addColorStop(1,'verde');
ctx.fillStyle=gradiente;

// llenar una ruta con el degradado


ctx.beginPath(); ctx.moveTo(150,0);
ctx.lineTo(300,150); ctx.lineTo(150,325);
ctx.lineTo(0,150); ctx.lineTo(150,0);
ctx.llenar();

}); // finaliza ventana.onload </


script>

GoalKicker.com – Notas HTML5 Canvas para profesionales 75


Machine Translated by Google
</
cabeza>
<cuerpo> <lienzo id="lienzo" ancho=300 alto=325></lienzo> </
cuerpo> </html>

Los aterradores detalles oficiales

¿Quién decide qué hace `createRadialGradient?

El W3C emite las especificaciones oficiales recomendadas que utilizan los navegadores para crear el elemento HTML5 Canvas.

La especificación W3C para crearGradienteRadial crípticamente se lee así:

¿Qué crea createRadialGradient?

createRadialGradient … crea efectivamente un cono, tocado por los dos círculos definidos en la creación del
degradado, con la parte del cono antes del círculo inicial (0.0) usando el color del primer desplazamiento, la parte del
cono después del círculo final (1.0) usando el color del último desplazamiento y las áreas fuera del cono que no han
sido tocadas por el degradado (negro transparente).

como funciona internamente

El método createRadialGradient(x0, y0, r0, x1, y1, r1) toma seis argumentos, los primeros tres representan el
círculo inicial con origen (x0, y0) y radio r0, y los últimos tres representan el círculo final con origen (x1 , y1) y
radio r1. Los valores están en unidades de espacio de coordenadas. Si r0 o r1 son negativos, se debe lanzar una
excepción IndexSizeError. De lo contrario, el método debe devolver un CanvasGradient radial inicializado con los
dos círculos especificados.

Los degradados radiales deben renderizarse siguiendo estos pasos:

1. Si x0 = x1 y y0 = y1 y r0 = r1, entonces el gradiente radial no debe pintar nada. Cancele estos pasos.
2. Sea x(ÿ) = (x1-x0)ÿ + x0; Sea y(ÿ) = (y1-y0)ÿ + y0; Sea r(ÿ) = (r1-r0)ÿ + r0 Sea el color en ÿ el color en esa posición en el
gradiente (con los colores provenientes de la interpolación y extrapolación descrita anteriormente).

3. Para todos los valores de ÿ donde r(ÿ) > 0, comenzando con el valor de ÿ más cercano al infinito positivo y terminando con el
valor de ÿ más cercano al infinito negativo, dibuje la circunferencia del círculo con radio r(ÿ) en posición (x(ÿ), y(ÿ)), con el
color en ÿ, pero solo pintando en las partes del lienzo que aún no han sido pintadas por círculos anteriores en este paso para
esta representación del degradado.

GoalKicker.com – Notas HTML5 Canvas para profesionales 76


Machine Translated by Google

Capítulo 6: Caminos
Sección 6.1: Elipse

Nota: Los navegadores están en el proceso de agregar un comando de dibujo context.ellipse incorporado , pero este comando no se adopta
universalmente (notablemente no en IE). Los métodos a continuación funcionan en todos los navegadores.

Dibuja una elipse dada su coordenada superior izquierda deseada:

// dibuja una elipse basada en x,y siendo la función de coordenadas


superior izquierda drawEllipse(x,y,width,height){
var
PI2=Matemáticas.PI*2;
relación var =alto/ancho; var
radio=Math.max(ancho,alto)/2; incremento de
var = 1 / radio; var cx=x+ancho/2; var
cy=y+altura/2;

ctx.beginPath(); var
x = cx + radio * Math.cos(0); var y = cy -
razón * radio * Math.sin(0); ctx.lineTo(x,y);

for(var radianes=incremento; radianes<PI2; radianes+=incremento){ var x =


cx + radio * Math.cos(radianes); var y = cy - ratio * radio *
Math.sin(radianes); ctx.lineTo(x,y);

ctx.closePath();
ctx.stroke();
}

Dibuja una elipse dada la coordenada del punto central deseado:

// dibuja una elipse basada en que cx,cy es la función de coordenadas del punto central
de la elipse drawEllipse2(cx,cy,width,height){
var
PI2=Matemáticas.PI*2;
relación var =alto/ancho; var
radio=Math.max(ancho,alto)/2; incremento de var = 1 / radio;

ctx.beginPath(); var
x = cx + radio * Math.cos(0); var y = cy -
razón * radio * Math.sin(0); ctx.lineTo(x,y);

for(var radianes=incremento; radianes<PI2; radianes+=incremento){ var x =


cx + radio * Math.cos(radianes); var y = cy - ratio * radio *
Math.sin(radianes); ctx.lineTo(x,y);

GoalKicker.com – Notas HTML5 Canvas para profesionales 77


Machine Translated by Google

ctx.closePath();
ctx.stroke();
}

Sección 6.2: Línea sin borrosidad


Cuando Canvas dibuja una línea, automáticamente agrega suavizado para curar visualmente la "irregularidad". El resultado es una línea menos
irregular pero más borrosa.

Esta función dibuja una línea entre 2 puntos sin suavizado utilizando el algoritmo Bresenham's_line. El resultado es una línea nítida sin irregularidades.

Nota importante: este método de píxel por píxel es un método de dibujo mucho más lento que context.lineTo.

// Uso:
bresenhamLine(50,50,250,250);

// inicio de línea x,y //


fin de línea xx,yy //
se dibujan el píxel al inicio y al final de línea function
bresenhamLine(x, y, xx, yy){
var oldFill = ctx.fillStyle; // guardar estilo de relleno antiguo
ctx.fillStyle = ctx.strokeStyle; // mueve el estilo del trazo para llenar xx =
Math.floor(xx); yy = Math.piso(yy); x = Matemáticas.piso(x); y =
Matemáticas.piso(y); // BRENSENHAM var dx = Math.abs(xx-x); var sx = x <
xx ? 1 : -1; var dy = -Math.abs(yy-y); var sy = y<yy ? 1 : -1; var error = dx+dy;
var errC; // valor de error var end = false; var x1 = x; var y1 = y;

while(!fin)
{ ctx.fillRect(x1, y1, 1, 1); // dibujar cada píxel como un rect

GoalKicker.com – Notas HTML5 Canvas para profesionales 78


Machine Translated by Google

if (x1 === xx && y1 === yy) { fin =


verdadero; }si no{ errC = 2*err; if
(errC >= dy) { err += dy; x1 += sx;

} if (errC <= dx) { err +=


dx; y1 += sy;

}
}

} ctx.fillStyle = oldFill; // restaurar el estilo de relleno antiguo


}

GoalKicker.com – Notas HTML5 Canvas para profesionales 79


Machine Translated by Google

Capítulo 7: Navegando a lo largo de un camino


Sección 7.1: Encuentra el punto en la curva
Este ejemplo encuentra un punto en una curva Bézier o cúbica en la posición donde la posición es la unidad de distancia en la
curva 0 <= posición <= 1. La posición se fija al rango, por lo tanto, si se pasan los valores < 0 o > 1, serán establecer 0,1
respectivamente.

Pasa la función 6 coordenadas para bezier cuadrática u 8 para cúbica.

El último argumento opcional es el vector devuelto (punto). Si no se da, se creará.

Ejemplo de uso
var p1 = {x : 10 , y : 100}; var p2 =
{x : 100, y : 200}; var p3 = {x : 200,
y : 0}; var p4 = {x : 300, y : 100};
var punto = {x : nulo, y : nulo};

// para punto beziers


cúbico = getPointOnCurve(0.5, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, point); // o No es necesario
establecer el punto ya que es una referencia y se establecerá getPointOnCurve(0.5, p1.x, p1.y, p2.x, p2.y,
p3.x, p3.y, p4.x, p4 .y, punto); // o para crear un nuevo punto var point1 = getPointOnCurve(0.5, p1.x, p1.y,
p2.x, p2.y, p3.x, p3.y, p4.x, p4.y);

// para punto beziers


cuadrático = getPointOnCurve(0.5, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, null, null, point); // o No es necesario
establecer el punto ya que es una referencia y se establecerá getPointOnCurve(0.5, p1.x, p1.y, p2.x, p2.y,
p3.x, p3.y, null, null, point ); // o para crear un nuevo punto var point1 = getPointOnCurve(0.5, p1.x, p1.y,
p2.x, p2.y, p3.x, p3.y);

La función

getPointOnCurve = función (posición, x1, y1, x2, y2, x3, y3, [x4, y4], [vec])

Nota: Los argumentos dentro de [x4, y4] son opcionales.

Nota: x4,y4 si es nulo o indefinido significa que la curva es un Bézier cuadrático. vec es opcional y mantendrá el
punto devuelto si se proporciona. Si no, se creará.

var getPointOnCurve = function(posición, x1, y1, x2, y2, x3, y3, x4, y4, vec){
var vec,
cuádruple;
cuádruple = falso; if(vec
=== indefinido){ vec = {};
}

if(x4 === indefinido || x4 === nulo){ quad =


verdadero; x4 = x3;

GoalKicker.com – Notas HTML5 Canvas para profesionales 80


Machine Translated by Google

y4 = y3;
}

if(posición <= 0){ vec.x =


x1; vec.y = y1;
volver vec;

} if(posición >= 1){ vec.x =


x4; vec.y = y4;
volver vec;

} c = posición;
si(cuádruple){ x1
+= (x2 - x1) * c; y1 += (y2 -
y1) * c; x2 += (x3 - x2) * c; y2
+= (y3 - y2) * c; vec.x = x1 +
(x2 - x1) * c; vec.y = y1 + (y2
- y1) * c; volver vec;

} x1 += (x2 - x1) * c; y1 += (y2


- y1) * c; x2 += (x3 - x2) * c;
y2 += (y3 - y2) * c; x3 += (x4
- x3) * c; y3 += (y4 - y3) * c;
x1 += (x2 - x1) * c; y1 += (y2
- y1) * c; x2 += (x3 - x2) * c;
y2 += (y3 - y2) * c; vec.x = x1
+ (x2 - x1) * c; vec.y = y1 +
(y2 - y1) * c; volver vec;

Sección 7.2: Encontrar la extensión de la curva cuadrática


Cuando necesite encontrar el rectángulo delimitador de una curva Bézier cuadrática, puede usar el siguiente método de
rendimiento.

// Este método fue descubierto por Blindman67 y lo resuelve normalizando primero el punto de control, reduciendo así la complejidad del algoritmo //
x1,y1, x2,y2, x3,y3 Coords de inicio, control y fin de bezier // [extensión] es opcional y si se proporciona, se agregará la extensión que le permitirá
usar la función

para obtener la medida de muchos beziers.


// // devuelve el objeto de extensión (si no se proporciona, se crea una nueva extensión)
// Propiedades del objeto de extensión //
arriba, izquierda, derecha, abajo, ancho, alto función
getQuadraticCurevExtent(x1, y1, x2, y2, x3, y3, extension) {
var brx, bx, x, bry, by, y, px, py;

// resuelve los límites cuadráticos mediante la ecuación de normalización BM67 brx =


x3 - x1; // obtener x rango bx = x2 - x1; // obtener el desplazamiento del punto de
control x x = bx / brx; // normaliza el punto de control que se usa para verificar si el
máximo está dentro del rango

// hacer lo mismo para los puntos y

GoalKicker.com – Notas HTML5 Canvas para profesionales 81


Machine Translated by Google

bry = y3 - y1; por


= y2 - y1; y = por /
bry;

px = x1; // establece los valores predeterminados en caso de que los máximos estén fuera del
rango py = y1;

// encuentra arriba/ izquierda, arriba/ derecha, abajo/ izquierda o abajo/


derecha si (x < 0 || x > 1) { // verifica si x maxima está en la curva
px = bx * bx / (2 * bx - brx) + x1; // obtener los x máximos

} si (y < 0 || y > 1) { // igual que x


py = por * por / (2 * por - bry) + y1;
}

// crea un objeto de extensión y agrega una


extensión if (extensión === indefinida)
{ extensión = {}; extensión.izquierda =
Math.min (x1, x3, px); extensión.top =
Math.min (y1, y3, py); extensión.derecha =
Math.max (x1, x3, px); extensión.inferior =
Math.max (y1, y3, py); } else { // usa la extensión
dividida y extiéndela para que se ajuste a esta curva extension.left = Math.min
(x1, x3, px, extension.left); extensión.superior = Math.min (y1, y3, py,
extensión.superior); extensión.derecha = Math.max (x1, x3, px,
extensión.derecha); extensión.inferior = Math.max (y1, y3, py,
extensión.inferior);
}

extensión.ancho = extensión.derecha - extensión.izquierda;


extensión.altura = extensión.abajo - extensión.superior;
extensión de retorno ;
}

Para obtener una visión más detallada de cómo resolver la extensión, consulte la respuesta Para obtener la extensión de un bezier cuadrático que incluye
demostraciones ejecutables.

Sección 7.3: Encontrar puntos a lo largo de una curva Bezier cúbica


Este ejemplo encuentra una matriz de puntos espaciados aproximadamente uniformemente a lo largo de una curva Bézier cúbica.

Descompone los segmentos de ruta creados con context.bezierCurveTo en puntos a lo largo de esa curva.

// Retorno: una matriz de puntos espaciados aproximadamente uniformemente a lo largo de una curva Bézier
cúbica // // Reconocimiento: @Blindman67 de Stackoverflow // Cita: http:// stackoverflow.com/ questions/
36637211/ drawing-a-curved-line-in -css-or-canvas-and-moving-circle along-it/ 36827074#36827074 //
Modificado de la cita anterior

// // ptCount: muestra esta cantidad de puntos a intervalos a lo largo de la curva //


pxTolerance: espacio aproximado permitido entre puntos
// Ax,Ay,Bx,By,Cx,Cy,Dx,Dy: puntos de control que definen la curva // función
plotCBez(ptCount,pxTolerance,Ax,Ay,Bx,By,Cx,Cy,Dx,Dy){

var deltaBAx=Bx-Ax; var


deltaCBx=Cx-Bx; var
deltaDCx=Dx-Cx; var
deltaBAy=Por-Ay;

GoalKicker.com – Notas HTML5 Canvas para profesionales 82


Machine Translated by Google

var deltaCBy=Cy-Por; var


deltaDCy=Dy-Cy; var
ax,ay,bx,por; var
últimoX=-10000; var
ultimoY=-10000; var
pts=[{x:Ax,y:Ay}]; for(var
i=1;i<contador de puntos;i++){ var t=i/
contador de puntos;
ax=Ax+deltaBAx*t;
bx=Bx+deltaCBx*t;
cx=Cx+deltaDCx*t; ax+=(bx-ax)*t;
bx+=(cx-bx)*t; // ay=Ay+deltaBAy*t;
por=Por+deltaCBy*t;
cy=Cy+deltaDCy*t; ay+=(por-ay)*t;
por+=(cy-por)*t; var x=ax+(bx-
ax)*t; var y=ay+(by-ay)*t; var dx=x-
últimoX; var dy=y-lastY;
if(dx*dx+dy*dy>pxTolerancia)
{ pts.push({x:x,y:y}); últimoX=x;
ultimoY=y;

} puntos.push({x:Dx,y:Dy});
retorno(puntos);
}

Sección 7.4: Encontrar puntos a lo largo de una curva cuadrática


Este ejemplo encuentra una matriz de puntos aproximadamente uniformemente espaciados a lo largo de una curva cuadrática.

Descompone los segmentos de Path creados con context.quadraticCurveTo en puntos a lo largo de esa curva.

// Retorno: una matriz de puntos espaciados aproximadamente uniformemente a lo largo de una curva cuadrática // //
Atribución: @Blindman67 de Stackoverflow // Cita: http:// stackoverflow.com/ questions/ 36637211/ drawing-a-curved-
line-in- css-or-canvas-and-moving-circle along-it/ 36827074#36827074 // Modificado de la cita anterior // // ptCount:
muestree este número de puntos a intervalos a lo largo de la curva // pxTolerance: espacio aproximado permitido entre
puntos // Ax,Ay,Bx,By,Cx,Cy: puntos de control que definen la curva // function plotQBez(ptCount,pxTolerance,Ax,Ay,Bx,By,Cx,Cy){ var deltaBAx=Bx-
Ax; var deltaCBx=Cx-Bx; var deltaBAy=Por-Ay; var deltaCBy=Cy-Por;

var hacha, ay;


var últimoX=-10000; var
ultimoY=-10000; var
pts=[{x:Ax,y:Ay}]; for(var
i=1;i<cuentaCuenta;i++){

GoalKicker.com – Notas HTML5 Canvas para profesionales 83


Machine Translated by Google

var t=i/cuentaCuenta;
ax=Ax+deltaBAx*t;
ay=Ay+deltaBAy*t; var
x=ax+((Bx+deltaCBx*t)-ax)*t; var y=ay+
((By+deltaCBy*t)-ay)*t; var dx=x-últimoX; var
dy=y-lastY; if(dx*dx+dy*dy>pxTolerancia)
{ pts.push({x:x,y:y}); últimoX=x; ultimoY=y;

} puntos.push({x:Cx,y:Cy});
retorno(puntos);
}

Sección 7.5: Encontrar puntos a lo largo de una línea


Este ejemplo encuentra una matriz de puntos espaciados aproximadamente uniformemente a lo largo de una línea.

Descompone los segmentos de Path creados con context.lineTo en puntos a lo largo de esa línea.

// Devuelve: una matriz de puntos espaciados de manera aproximadamente uniforme a lo largo de una
línea // // pxTolerance: espacio aproximado permitido entre puntos // Ax,Ay,Bx,By: puntos finales que
definen la línea // function plotLine(pxTolerance,Ax,Ay ,Bx,Por){ var dx=Bx-Ax; var dy=Por-Ay; var
ptCount=parseInt(Math.sqrt(dx*dx+dy*dy))*3; var últimoX=-10000; var ultimoY=-10000; var
pts=[{x:Ax,y:Ay}]; for(var i=1;i<=cuentaCuenta;i++){

var t=i/cuentaCuenta;
varx =Ax+dx*t; var
y=Ay+dy*t; var dx1=x-
últimoX; var dy1=y-
últimoY;
if(dx1*dx1+dy1*dy1>pxTolerancia)
{ pts.push({x:x,y:y}); últimoX=x; ultimoY=y;

} puntos.push({x:Bx,y:Por});
retorno(puntos);
}

Sección 7.6: Encontrar puntos a lo largo de un Camino completo que


contiene curvas y líneas

Este ejemplo encuentra una matriz de puntos espaciados de manera aproximadamente uniforme a lo largo de una ruta completa.

Descompone todos los segmentos de ruta creados con context.lineTo, context.quadraticCurveTo y/o context.bezierCurveTo en puntos a lo largo

de esa ruta.

GoalKicker.com – Notas HTML5 Canvas para profesionales 84


Machine Translated by Google

Uso

// Variables relacionadas con


la ruta var A={x:50,y:100};
varB ={x:125,y:25}; var
BB={x:150,y:15}; var
BB2={x:150,y:185}; varC
={x:175,y:200}; var
D={x:300,y:150}; variable
n=1000; tolerancia var =1,5;
var ptos;

// variables relacionadas con el


lienzo var canvas=document.createElement("canvas");
var ctx=canvas.getContext("2d");
documento.cuerpo.appendChild(lienzo); lienzo.ancho=378;
lienzo.altura=256;

// Dígale al Contexto que trace el punto de ruta además de //


dibujar la ruta plotPathCommands(ctx,n,tolerance);

// Comandos de dibujo de
rutas ctx.beginPath();
ctx.moverA(Ax,Ay);
ctx.bezierCurveTo(Bx,Por,Cx,Cy,Dx,Dy);
ctx.quadraticCurveTo(BB.x,BB.y,Ax,Ay);
ctx.lineTo(Dx,Dy); ctx.strokeStyle='gris'; ctx.stroke();

// Dígale al Contexto que deje de trazar waypoints


ctx.stopPlottingPathCommands();

// Demostración: dibuja la ruta de forma incremental usando los puntos


trazados ptsToRects(ctx.getPathPoints()); function ptsToRects(pts)
{ ctx.fillStyle='red'; var i=0; requestAnimationFrame(animar); función animar()
{ ctx.fillRect(pts[i].x-0.50,pts[i].y-0.50,tolerancia,tolerancia); yo++;
if(i<pts.length){ requestAnimationFrame(animar); }

}
}

Un complemento que calcula automáticamente los puntos a lo largo de la ruta

Este código modifica los comandos de dibujo de Canvas Context para que los comandos no solo dibujen la línea o la curva, sino que
también creen una matriz de puntos a lo largo de todo el camino:

ruta de
inicio, mover a,
línea a,
quadraticCurveTo,
bezierCurveTo.

GoalKicker.com – Notas HTML5 Canvas para profesionales 85


Machine Translated by Google

¡Nota IMPORTANTE!

Este código modifica las funciones de dibujo reales del contexto, por lo que cuando haya terminado de trazar puntos a lo largo de la ruta, debe llamar a
los comandos stopPlottingPathCommands proporcionados para devolver las funciones de dibujo del contexto a su estado sin modificar.

El propósito de este Contexto modificado es permitirle "conectar" el cálculo de matriz de puntos en su código existente sin tener que modificar sus comandos de
dibujo de ruta existentes. Pero no necesita usar este Contexto modificado; puede llamar por separado a las funciones individuales que descomponen una línea,
una curva cuadrática y una curva Bézier cúbica y luego concatenar manualmente esas matrices de puntos individuales en una matriz de puntos única para todo
el camino.

Obtiene una copia de la matriz de puntos resultante utilizando la función getPathPoints proporcionada .

Si dibuja varias rutas con el contexto modificado, la matriz de puntos contendrá un único conjunto de puntos concatenados para todas las múltiples rutas
dibujadas.

Si, en cambio, desea obtener matrices de puntos separadas, puede obtener la matriz actual con getPathPoints y luego borrar esos puntos de la matriz con
la función clearPathPoints suministrada .

// Modifique el contexto del lienzo para calcular un conjunto de puntos de ruta


espaciados aproximadamente
function // uniformemente a medida que dibuja la(s) ruta(s).
plotPathCommands(ctx,sampleCount,pointSpacing){

ctx.mySampleCount=muestraCount;
ctx.myPointSpacing=pointSpacing;
ctx.myTolerance=pointSpacing*pointSpacing;
ctx.myBeginPath=ctx.beginPath; ctx.miMoverA=ctx.moverA;
ctx.myLineTo=ctx.lineTo;
ctx.myQuadraticCurveTo=ctx.quadraticCurveTo;
ctx.myBezierCurveTo=ctx.bezierCurveTo; // no use
myPathPoints[] directamente -- use "ctx.getPathPoints"
ctx.myPathPoints=[]; ctx.beginPath=función(){ this.myLastX=0; this.myLastY=0;
this.myBeginPath();

} ctx.moveTo=function(x,y)
{ this.myLastX=x;
this.myLastY=y;
this.myMoveTo(x,y);

} ctx.lineTo=función(x,y){
var pts=plotLine(this.myTolerance,this.myLastX,this.myLastY,x,y);
Array.prototype.push.apply(this.myPathPoints,pts); this.myLastX=x; this.myLastY=y;
this.myLineTo(x,y);

} ctx.quadraticCurveTo=función(x0,y0,x1,y1){
variable

pts=plotQBez(this.mySampleCount,this.myTolerance,this.myLastX,this.myLastY,x0,y0,x1,y1);
Array.prototype.push.apply(this.myPathPoints,pts); this.myLastX=x1;
this.myLastY=y1; this.myQuadraticCurveTo(x0,y0,x1,y1);

} ctx.bezierCurveTo=función(x0,y0,x1,y1,x2,y2){
variable

GoalKicker.com – Notas HTML5 Canvas para profesionales 86


Machine Translated by Google

pts=plotCBez(this.mySampleCount,this.myTolerance,this.myLastX,this.myLastY,x0,y0,x1,y1,x2,y2);
Array.prototype.push.apply(this.myPathPoints,pts); this.myLastX=x2;
this.myLastY=y2; this.myBezierCurveTo(x0,y0,x1,y1,x2,y2);

} ctx.getPathPoints=function()
{ return(this.myPathPoints.slice());

} ctx.clearPathPoints=función()
{ this.myPathPoints.length=0;

} ctx.stopPlottingPathCommands=función(){ if(!
this.myBeginPath){return;}
this.beginPath=this.myBeginPath;
this.moveTo=this.myMoveTo;
this.lineTo=this.myLineTo;
this.quadraticCurveto=this.myQuadraticCurveTo;
this.bezierCurveTo=this.myBezierCurveTo;
this.myBeginPath=indefinido;
}
}

Una demostración completa:

// Variables relacionadas con la ruta


var A={x:50,y:100}; varB
={x:125,y:25}; var BB={x:150,y:15};
var BB2={x:150,y:185}; varC
={x:175,y:200}; var D={x:300,y:150};
variable n=1000; tolerancia var
=1,5; var ptos;

// variables relacionadas con el lienzo


var canvas=document.createElement("canvas"); var
ctx=canvas.getContext("2d"); documento.cuerpo.appendChild(lienzo);
lienzo.ancho=378; lienzo.altura=256;

// Dígale al Contexto que trace el punto de ruta además de // dibujar la ruta


plotPathCommands(ctx,n,tolerance);

// Comandos de dibujo de rutas


ctx.beginPath(); ctx.moverA(Ax,Ay);
ctx.bezierCurveTo(Bx,Por,Cx,Cy,Dx,Dy);
ctx.quadraticCurveTo(BB.x,BB.y,Ax,Ay); ctx.lineTo(Dx,Dy);
ctx.strokeStyle='gris'; ctx.stroke();

// Dígale al Contexto que deje de trazar waypoints


ctx.stopPlottingPathCommands();

// Dibujar incrementalmente la ruta usando los puntos trazados


ptsToRects(ctx.getPathPoints());

GoalKicker.com – Notas HTML5 Canvas para profesionales 87


Machine Translated by Google

function ptsToRects(pts)
{ ctx.fillStyle='red'; var i=0;
requestAnimationFrame(animar);
función animar()

{ ctx.fillRect(pts[i].x-0.50,pts[i].y-0.50,tolerancia,tolerancia); yo++; if(i<pts.length)


{ requestAnimationFrame(animar); }

}
}

/////////////////////////////////////////
// Un
complemento /////////////////////////////////////////

// Modifique el contexto del lienzo para calcular un conjunto de puntos de ruta espaciados
funcion plotPathCommands(ctx,sampleCount,pointSpacing){
de manera aproximadamente uniforme a medida que dibuja la(s) ruta(s). //

ctx.mySampleCount=muestraCount;
ctx.myPointSpacing=pointSpacing;
ctx.myTolerance=pointSpacing*pointSpacing;
ctx.myBeginPath=ctx.beginPath; ctx.miMoverA=ctx.moverA;
ctx.myLineTo=ctx.lineTo;
ctx.myQuadraticCurveTo=ctx.quadraticCurveTo;
ctx.myBezierCurveTo=ctx.bezierCurveTo; // no use myPathPoints[]
directamente -- use "ctx.getPathPoints" ctx.myPathPoints=[];
ctx.beginPath=función(){ this.myLastX=0; this.myLastY=0; this.myBeginPath();

} ctx.moveTo=function(x,y)
{ this.myLastX=x;
this.myLastY=y;
this.myMoveTo(x,y);

} ctx.lineTo=función(x,y){
var pts=plotLine(this.myTolerance,this.myLastX,this.myLastY,x,y);
Array.prototype.push.apply(this.myPathPoints,pts); this.myLastX=x; this.myLastY=y;
this.myLineTo(x,y);

} ctx.quadraticCurveTo=función(x0,y0,x1,y1){
variable

pts=plotQBez(this.mySampleCount,this.myTolerance,this.myLastX,this.myLastY,x0,y0,x1,y1);
Array.prototype.push.apply(this.myPathPoints,pts); this.myLastX=x1;
this.myLastY=y1; this.myQuadraticCurveTo(x0,y0,x1,y1);

} ctx.bezierCurveTo=función(x0,y0,x1,y1,x2,y2){
variable

pts=plotCBez(this.mySampleCount,this.myTolerance,this.myLastX,this.myLastY,x0,y0,x1,y1,x2,y2);
Array.prototype.push.apply(this.myPathPoints,pts); this.myLastX=x2;
this.myLastY=y2; this.myBezierCurveTo(x0,y0,x1,y1,x2,y2);

GoalKicker.com – Notas HTML5 Canvas para profesionales 88


Machine Translated by Google

ctx.getPathPoints=function()
{ return(this.myPathPoints.slice());

} ctx.clearPathPoints=función()
{ this.myPathPoints.length=0;

} ctx.stopPlottingPathCommands=función(){ if(!
this.myBeginPath){return;}
this.beginPath=this.myBeginPath;
this.moveTo=this.myMoveTo;
this.lineTo=this.myLineTo;
this.quadraticCurveto=this.myQuadraticCurveTo;
this.bezierCurveTo=this.myBezierCurveTo;
this.myBeginPath=indefinido;
}
}

/////////////////////////////////
// Funciones
auxiliares ////////////////////////////////

// Retorno: un conjunto de puntos aproximadamente uniformemente espaciados a lo largo de una curva Bézier
cúbica // // Atribución: @Blindman67 de Stackoverflow // Cita: http:// stackoverflow.com/ questions/ 36637211/ drawing-
a-curved-line-in -css-or-canvas-and-moving-circle along-it/ 36827074#36827074 // Modificado de la cita anterior // //
ptCount: muestree este número de puntos a intervalos a lo largo de la curva // pxTolerance: espacio aproximado
permitido entre puntos // Ax,Ay,Bx,By,Cx,Cy,Dx,Dy: puntos de control que definen la curva // función
plotCBez(ptCount,pxTolerance,Ax,Ay,Bx,By,Cx,Cy,Dx,Dy){

var deltaBAx=Bx-Ax; var


deltaCBx=Cx-Bx; var
deltaDCx=Dx-Cx; var
deltaBAy=Por-Ay; var
deltaCBy=Cy-Por; var
deltaDCy=Dy-Cy; var
ax,ay,bx,por; var
últimoX=-10000; var
ultimoY=-10000; var
pts=[{x:Ax,y:Ay}]; for(var
i=1;i<contador de puntos;i++){ var t=i/
contador de puntos;
ax=Ax+deltaBAx*t;
bx=Bx+deltaCBx*t;
cx=Cx+deltaDCx*t; ax+=(bx-ax)*t;
bx+=(cx-bx)*t; // ay=Ay+deltaBAy*t;
por=Por+deltaCBy*t;
cy=Cy+deltaDCy*t; ay+=(por-ay)*t;
por+=(cy-por)*t; var x=ax+(bx-
ax)*t; var y=ay+(by-ay)*t; var dx=x-
últimoX; var dy=y-lastY;

GoalKicker.com – Notas HTML5 Canvas para profesionales 89


Machine Translated by Google

if(dx*dx+dy*dy>pxTolerancia)
{ pts.push({x:x,y:y}); últimoX=x;
ultimoY=y;

} puntos.push({x:Dx,y:Dy});
retorno(puntos);
}

// Retorno: una matriz de puntos espaciados aproximadamente uniformemente a lo largo de una curva cuadrática // //
Atribución: @Blindman67 de Stackoverflow // Cita: http:// stackoverflow.com/ questions/ 36637211/ drawing-a-curved-
line-in- css-or-canvas-and-moving-circle along-it/ 36827074#36827074 // Modificado de la cita anterior // // ptCount:
muestree este número de puntos a intervalos a lo largo de la curva // pxTolerance: espacio aproximado permitido entre
puntos // Ax,Ay,Bx,By,Cx,Cy: puntos de control que definen la curva // function plotQBez(ptCount,pxTolerance,Ax,Ay,Bx,By,Cx,Cy){ var deltaBAx=Bx-
Ax; var deltaCBx=Cx-Bx; var deltaBAy=Por-Ay; var deltaCBy=Cy-Por;

var hacha, ay;


var últimoX=-10000; var
ultimoY=-10000; var
pts=[{x:Ax,y:Ay}]; for(var
i=1;i<contador de puntos;i++){ var t=i/
contador de puntos;
ax=Ax+deltaBAx*t;
ay=Ay+deltaBAy*t; var x=ax+
((Bx+deltaCBx*t)-ax)*t; var y=ay+((By+deltaCBy*t)-
ay)*t; var dx=x-últimoX; var dy=y-lastY;
if(dx*dx+dy*dy>pxTolerancia){ pts.push({x:x,y:y});
últimoX=x; ultimoY=y;

} puntos.push({x:Cx,y:Cy});
retorno(puntos);
}

// Devuelve: una matriz de puntos espaciados de manera aproximadamente uniforme a lo largo de una
línea // // pxTolerance: espacio aproximado permitido entre puntos // Ax,Ay,Bx,By: puntos finales que
definen la línea // function plotLine(pxTolerance,Ax,Ay ,Bx,Por){ var dx=Bx-Ax; var dy=Por-Ay; var
ptCount=parseInt(Math.sqrt(dx*dx+dy*dy))*3; var últimoX=-10000; var ultimoY=-10000; var
pts=[{x:Ax,y:Ay}]; for(var i=1;i<=cuentaCuenta;i++){

GoalKicker.com – Notas HTML5 Canvas para profesionales 90


Machine Translated by Google

var t=i/cuentaCuenta;
varx =Ax+dx*t; var
y=Ay+dy*t; var dx1=x-
últimoX; var dy1=y-
últimoY;
if(dx1*dx1+dy1*dy1>pxTolerancia)
{ pts.push({x:x,y:y}); últimoX=x; ultimoY=y;

} puntos.push({x:Bx,y:Por});
retorno(puntos);
}

Sección 7.7: Dividir curvas Bézier en la posición


Este ejemplo divide las curvas cúbica y bezier en dos.

La función splitCurveAt divide la curva en la posición donde 0.0 = inicio, 0.5 = medio y 1 = final. Puede dividir curvas cuadráticas
y cúbicas. El tipo de curva está determinado por el último argumento x x4. Si no está indefinido o es nulo , se supone que la curva
es cúbica; de lo contrario, la curva es cuadrática.

Ejemplo de uso

Dividir la curva de Bézier cuadrática en dos

var p1 = {x : 10 , y : 100}; var p2 = {x :


100, y : 200}; var p3 = {x : 200, y : 0}; var
nuevasCurvas = splitCurveAt(0.5, p1.x,
p1.y, p2.x, p2.y, p3.x, p3.y)

var i = 0; var
p = newCurves // Dibujar
las 2 nuevas curvas // Supone que
ctx es un contexto de lienzo 2d ctx.lineWidth = 1;
ctx.strokeStyle = "negro"; ctx.beginPath();
ctx.moveTo(p[i++],p[i++]); ctx.quadraticCurveTo(p[i+
+], p[i++], p[i++], p[i++]); ctx.quadraticCurveTo(p[i+
+], p[i++], p[i++], p[i++]); ctx.stroke();

Dividir la curva bezier cúbica en dos

var p1 = {x : 10 , y : 100}; var p2 = {x :


100, y : 200}; var p3 = {x : 200, y : 0}; var
p4 = {x : 300, y : 100}; var nuevasCurvas
= splitCurveAt(0.5, p1.x, p1.y, p2.x, p2.y,
p3.x, p3.y, p4.x, p4.y)

var i = 0; var
p = newCurves // Dibujar
las 2 nuevas curvas // Supone que
ctx es un contexto de lienzo 2d ctx.lineWidth = 1;
ctx.strokeStyle = "negro"; ctx.beginPath();

GoalKicker.com – Notas HTML5 Canvas para profesionales 91


Machine Translated by Google

ctx.moveTo(p[i++],p[i++]);
ctx.bezierCurveTo(p[i++], p[i++], p[i++], p[i++], p[i++], p[i++]); ctx.bezierCurveTo(p[i++], p[i++], p[i+
+], p[i++], p[i++], p[i++]); ctx.stroke();

La función de división

splitCurveAt = función (posición, x1, y1, x2, y2, x3, y3, [x4, y4])

Nota: Los argumentos dentro de [x4, y4] son opcionales.

Nota: La función tiene algún código opcional comentado /* */ que se ocupa de los casos extremos en los que las curvas
resultantes pueden tener una longitud cero o quedar fuera del inicio o el final de la curva original. Al igual que intentar dividir una
curva fuera del rango válido para la posición >= 0 o la posición >= 1 arrojará un error de rango. Esto se puede eliminar y funcionará bien,
aunque es posible que tenga curvas resultantes que tengan una longitud cero.

// Con throw RangeError si no es 0 < posición < 1 // x1, y1, x2, y2, x3,
y3 para curvas cuadráticas // x1, y1, x2, y2, x3, y3, x4, y4 para curvas
cúbicas // Devuelve una matriz de puntos que representan 2 curvas. Las
curvas son del mismo tipo que la curva dividida var splitCurveAt = function(position, x1, y1, x2, y2, x3, y3, x4, y4){ var v1, v2, v3, v4, quad, retPoints,
i, c ;

// ================================================ ============================================= // puede eliminar


esto ya que la función seguirá funcionando y las curvas resultantes seguirán renderizándose // pero es posible que a otras funciones de curva
no les gusten las curvas con longitud 0 // ====================== ==================================================
===================== si(posición <= 0 || posición >= 1){

throw RangeError("spliteCurveAt requiere posición > 0 && posición < 1");


}

// ================================================ =============================================
// Si elimina el error de rango anterior, puede usar una o ambas de las siguientes secciones comentadas

// Dividir la posición de las curvas < 0 o la posición > 1 aún creará curvas válidas pero se extenderán // más allá de los puntos finales

// ================================================ ============================================ // Bloquear la


posición para dividir en la curva. / * posición A opcional = posición < 0 ? 0 : posición > 1 ? 1 final opcional */

: posición;

// ================================================ ============================================ // el siguiente


comentado la sección devolverá la curva original si la división da como resultado una longitud de 0
curva
// Es posible que desee descomentar esto si desea dicha funcionalidad / * opcional B si (posición
<= 0 || posición >= 1){

if(x4 === indefinido || x4 === nulo){ return [x1, y1, x2,


y2, x3, y3]; }else{ devuelve [x1, y1, x2, y2, x3,
y3, x4, y4];

}
}

GoalKicker.com – Notas HTML5 Canvas para profesionales 92


Machine Translated by Google

extremo B opcional */

retPuntos = []; // matriz de coordenadas i = 0; cuádruple


= falso; // suponer cubic bezier v1 = {}; v2 = {}; v4 = {};
v1.x = x1; v1.y = y1; v2.x = x2; v2.y = y2; if(x4 ===
indefinido || x4 === nulo){ quad = verdadero; // este es
un bezier cuadrático v4.x = x3; v4.y = y3; }más{ v3 = {};
v3.x = x3; v3.y = y3; v4.x = x4; v4.y = y4;

} c = posición;
retPuntos[i++] = v1.x; // punto de inicio retPoints[i++] =
v1.y;

if(quad){ // divide cuadráticos bezier retPoints[i++] =


(v1.x += (v2.x - v1.x) * c); // nuevo punto de control para la primera curva retPoints[i++] = (v1.y += (v2.y - v1.y) * c); v2.x +=
(v4.x - v2.x) * c; v2.y += (v4.y - v2.y) * c; retPuntos[i++] = v1.x + (v2.x - v1.x) * c; // nuevo final y comienzo de la primera y
segunda curva retPoints[i++] = v1.y + (v2.y - v1.y) * c; retPuntos[i++] = v2.x; // nuevo punto de control para la segunda curva
retPoints[i++] = v2.y; retPuntos[i++] = v4.x; // nuevo punto final de la segunda curva retPoints[i++] = v4.y; //
================================================ ======= // devuelve una matriz con 2 curvas devuelve retPoints;

} retPoints[i++] = (v1.x += (v2.x - v1.x) * c); // primera curva primer punto de control retPoints[i++] = (v1.y += (v2.y - v1.y)
* c); v2.x += (v3.x - v2.x) * c; v2.y += (v3.y - v2.y) * c; v3.x += (v4.x - v3.x) * c; v3.y += (v4.y - v3.y) * c; retPuntos[i++] =
(v1.x += (v2.x - v1.x) * c); // primera curva segundo punto de control retPoints[i++] = (v1.y += (v2.y - v1.y) * c); v2.x +=
(v3.x - v2.x) * c; v2.y += (v3.y - v2.y) * c; retPuntos[i++] = v1.x + (v2.x - v1.x) * c; // punto final e inicial de la primera
segunda curva retPoints[i++] = v1.y + (v2.y - v1.y) * c; retPuntos[i++] = v2.x; // segunda curva primer punto de control
retPoints[i++] = v2.y; retPuntos[i++] = v3.x; // segunda curva segundo punto de control retPoints[i++] = v3.y; retPuntos[i+
+] = v4.x; // punto final de la segunda curva retPoints[i++] = v4.y; //================================================
=======

GoalKicker.com – Notas HTML5 Canvas para profesionales 93


Machine Translated by Google

// devuelve una matriz con 2 curvas


devuelve retPoints;
}

Sección 7.8: Recortar la curva de Bézier


Este ejemplo le muestra cómo recortar un bezier.

La función trimBezier recorta los extremos de la curva devolviendo la curva fromPos a toPos. fromPos y toPos están en el rango
de 0 a 1 inclusive, puede recortar curvas cuadráticas y cúbicas. El tipo de curva está determinado por el último argumento x x4. Si
no está indefinido o es nulo , se supone que la curva es cúbica; de lo contrario, la curva es cuadrática.

La curva recortada se devuelve como una matriz de puntos. 6 puntos para curvas cuadráticas y 8 para curvas cúbicas.

Ejemplo de uso

Recortar una curva cuadrática.

var p1 = {x : 10 , y : 100}; var p2 = {x :


100, y : 200}; var p3 = {x : 200, y : 0}; var
nuevaCurva = splitCurveAt(0.25, 0.75,
p1.x, p1.y, p2.x, p2.y, p3.x, p3.y)

var i = 0; var
p = newCurve // Dibujar
la curva recortada // Supone que ctx
es un contexto de lienzo 2d ctx.lineWidth = 1;
ctx.strokeStyle = "negro"; ctx.beginPath();
ctx.moveTo(p[i++],p[i++]); ctx.quadraticCurveTo(p[i+
+], p[i++], p[i++], p[i++]); ctx.stroke();

Recortar una curva cúbica.

var p1 = {x : 10 , y : 100}; var p2 = {x :


100, y : 200}; var p3 = {x : 200, y : 0}; var
p4 = {x : 300, y : 100}; var nuevaCurva =
splitCurveAt(0.25, 0.75, p1.x, p1.y, p2.x,
p2.y, p3.x, p3.y, p4.x, p4.y)

var i = 0; var
p = newCurve // Dibujar
la curva recortada // Supone que ctx
es un contexto de lienzo 2d ctx.lineWidth = 1;
ctx.strokeStyle = "negro"; ctx.beginPath();
ctx.moveTo(p[i++],p[i++]); ctx.bezierCurveTo(p[i++],
p[i++], p[i++], p[i++], p[i++], p[i++]); ctx.stroke();

Función de ejemplo

trimBezier = función (dePos, aPos, x1, y1, x2, y2, x3, y3, [x4, y4])

GoalKicker.com – Notas HTML5 Canvas para profesionales 94


Machine Translated by Google

Nota: Los argumentos dentro de [x4, y4] son opcionales.

Nota: Esta función requiere la función del ejemplo Dividir curvas Bezier en en esta sección

var trimBezier = function(fromPos, toPos, x1, y1, x2, y2, x3, y3, x4, y4){
var quad, i, s, retBez;
cuádruple = falso; if(x4 ===
indefinido || x4 === nulo){ quad =
verdadero; // este es un bezier cuadrático

} if(fromPos > toPos){ // el intercambio es de es después de i


= fromPos; fromPos = toPos toPos = i;

} // sujetar a la curva toPos =


toPos <= 0 ? 0 : toPos >= 1 ? 1 : toPos; fromPos = fromPos
<= 0 ? 0 : desdePos >= 1 ? 1 if(toPos === fromPos){ : dePos;

s = splitBezierAt(toPos, x1, y1, x2, y2, x3, y3, x4, y4); yo = cuádruple ? 4 :
6; retBez = [s[i], s[i+1], s[i], s[i+1], s[i], s[i+1]]; if(!quad){ retBez.push(s[i],
s[i+1]);

} volver retBez;

} if(toPos === 1 && fromPos === 0){ // no se requiere recortar


retBez = [x1, y1, x2, y2, x3, y3]; // devuelve el bezier original if(!quad)
{ retBez.push(x4, y4);

} volver retBez;

} if(fromPos === 0)
{ if(toPos < 1){ s
= splitBezierAt(toPos, x1, y1, x2, y2, x3, y3, x4, y4); yo = 0; retBez = [s[i+
+], s[i++], s[i++], s[i++], s[i++], s[i++]]; if(!quad){ retBez.push(s[i++], s[i++]);

} volver retBez;

} if(toPos === 1)
{ if(fromPos < 1){ s
= splitBezierAt(toPos, x1, y1, x2, y2, x3, y3, x4, y4); yo = cuádruple ? 4 :
6; retBez = [s[i++], s[i++], s[i++], s[i++], s[i++], s[i++]]; if(!quad)
{ retBez.push(s[i++], s[i++]);

} volver retBez;

} s = splitBezierAt(fromPos, x1, y1, x2, y2, x3, y3, x4, y4); si(cuádruple){ i = 4;

GoalKicker.com – Notas HTML5 Canvas para profesionales 95


Machine Translated by Google

toPos = (toPos - fromPos) / (1 - fromPos); s = splitBezierAt(toPos,


s[i++], s[i++], s[i++], s[i++], s[i++], s[i++]); yo = 0; retBez = [s[i++], s[i++], s[i++], s[i++], s[i++], s[i++]]; volver
retBez;

} yo = 6;
toPos = (toPos - fromPos) / (1 - fromPos); s = splitBezierAt(toPos,
s[i++], s[i++], s[i++], s[i++], s[i++], s[i++], s[i++], s[i++]); yo = 0; retBez = [s[i++], s[i++], s[i++], s[i++], s[i++], s[i++], s[i++], s[i++]]; volver
retBez;

Sección 7.9: Longitud de una Curva Bézier Cúbica (una


aproximación cercana)
Dados los 4 puntos de una curva Bézier cúbica, la siguiente función devuelve su longitud.

Método: La longitud de una curva Bezier cúbica no tiene un cálculo matemático directo. Este método de "fuerza bruta" encuentra una
muestra de puntos a lo largo de la curva y calcula la distancia total abarcada por esos puntos.

Precisión: la longitud aproximada es más del 99 % precisa utilizando el tamaño de muestra predeterminado de 40.

// Retorno: Aproximación cercana a la longitud de una curva Cubic Bezier //

// Ax,Ay,Bx,By,Cx,Cy,Dx,Dy: los 4 puntos de control de la curva // sampleCount [opcional,


predeterminado=40]: cuántos intervalos calcular
// Requiere: cubicQxy (incluido a continuación) //
función
cubicBezierLength(Ax,Ay,Bx,By,Cx,Cy,Dx,Dy,sampleCount){ var ptCount=sampleCount||40; var
totDist=0; var ultimoX=Ax; var ultimoY=Ay; var dx,dy; for(var i=1;i<cuentaCuenta;i++){

var pt=cubicQxy(i/ptCount,Ax,Ay,Bx,By,Cx,Cy,Dx,Dy); dx=pt.x-últimoX;


dy=pt.y-lastY; totDist+=Math.sqrt(dx*dx+dy*dy); ultimoX=pt.x; lastY=pt.y;

} dx=Dx-últimoX;
dy=Dy-últimoY;
totDist+=Math.sqrt(dx*dx+dy*dy);
return(parseInt(totDist));
}

// Devuelve: un punto [x,y] a lo largo de una curva Bézier cúbica en el intervalo T // //


Reconocimiento: @Blindman67 de Stackoverflow // Cita: http:// stackoverflow.com/ questions/
36637211/ drawing-a-curved- línea-en-css-o-lienzo-y-movimiento-círculo a lo largo de él/
36827074#36827074

GoalKicker.com – Notas HTML5 Canvas para profesionales 96


Machine Translated by Google
// Modificado de la cita anterior // // t: un intervalo a lo
largo de la curva (0<=t<=1) // ax,ay,bx,by,cx,cy,dx,dy:
puntos de control que definen el curva // función
cubicQxy(t,ax,ay,bx,by,cx,cy,dx,dy) { ax += (bx - ax) * t; bx += (cx - bx) * t; cx += (dx - cx)
* t; hacha += (bx - hacha) * t; bx += (cx - bx) * t; ay += (por - ay) * t; por += (cy - por) * t; cy
+= (dy - cy) * t; ay += (por - ay) * t; por += (cy - por) * t; return({ x:ax +(bx - ax) * t, y:ay +
(by - ay) * t

});
}

Sección 7.10: Longitud de una Curva Cuadrática

Dados los 3 puntos de una curva cuadrática, la siguiente función devuelve la longitud.

función quadraticBezierLength(x1,y1,x2,y2,x3,y3)
var a, e, c, d, u, a1, e1, c1, d1, u1, v1x, v1y;

v1x = x2 * 2; v1y =
y2 * 2; d = x1 - v1x
+ x3; d1 = y1 - v1y + y3; e
= v1x - 2 * x1; e1 = v1y - 2
* y1; c1 = (a = 4 * (d * d +
d1 * d1)); c1 += (b = 4 * (d *
e + d1 * e1)); c1 += (c = e * e + e1 * e1); c1 = 2 *
Matemáticas.sqrt(c1); a1 = 2 * a * (u = Math.sqrt(a));
u1 = b / u; a = 4 * c * a - b * b; c = 2 *
Matemáticas.sqrt(c); return (a1 * c1 + u * b * (c1 -
c) + a * Math.log((2 * u + u1 + c1) / (u1 + c))) / (4 *
a1);

* (1 - t)2 + 2 * b * (1 - t) * t + c * t2
Derivado de la función cuadrática de Bézier F(t) = a

GoalKicker.com – Notas HTML5 Canvas para profesionales 97


Machine Translated by Google

Capítulo 8: Arrastrar formas de ruta y


Imágenes en lienzo
Sección 8.1: Cómo las formas e imágenes REALMENTE (!) se "mueven"
en el lienzo

Un problema: Canvas solo recuerda píxeles, no formas o imágenes

Esta es una imagen de una pelota de playa circular y, por supuesto, no puedes arrastrar la pelota alrededor de la imagen.

Puede que le sorprenda que, al igual que una imagen, si dibuja un círculo en un lienzo, no puede arrastrar ese círculo por el lienzo. Eso es
porque el lienzo no recordará dónde dibujó el círculo.

// ¡¡este arco (==círculo) no se puede arrastrar!!


contexto.beginPath(); contexto.arco(20, 30, 15,
0, Math.PI*2); context.fillStyle='azul';
contexto.rellenar();

Lo que el Canvas NO sabe...

...donde dibujaste el círculo (no sabe x,y =[20,30]). ...el tamaño del círculo
(no sabe radio=15). ...el color del círculo. (no sabe que el círculo es azul).

Lo que el lienzo SÍ sabe...

Canvas conoce el color de cada píxel en su superficie de dibujo.

El lienzo puede decirle que en x,y==[20,30] hay un píxel azul, pero no sabe si este píxel azul es parte de un círculo.

Lo que esto significa...

Esto significa que todo lo dibujado en el Lienzo es permanente: inamovible e inmutable.

Canvas no puede mover el círculo ni cambiar su tamaño.


Canvas no puede volver a colorear el círculo ni borrarlo.

Canvas no puede decir si el mouse está sobre el círculo.


Canvas no puede decir si el círculo está chocando con otro círculo.
Canvas no puede permitir que un usuario arrastre el círculo alrededor del lienzo.

GoalKicker.com – Notas HTML5 Canvas para profesionales 98


Machine Translated by Google

Pero Canvas puede dar la ILUSIÓN de movimiento

El lienzo puede dar la ilusión de movimiento al borrar continuamente el círculo y volver a dibujarlo en una posición diferente. Al
volver a dibujar el lienzo muchas veces por segundo, el ojo se engaña y ve que el círculo se mueve por el lienzo.

Borrar el lienzo

Actualizar la posición del círculo

Redibujar el círculo en su nueva posición.

Repetir, repetir, repetir...

Este código da la ilusión de movimiento al volver a dibujar continuamente un círculo en nuevas posiciones.

// crea un lienzo var


canvas=document.createElement("canvas"); var
ctx=canvas.getContext("2d"); ctx.fillStyle='rojo';
documento.cuerpo.appendChild(lienzo);

// una variable que indica la posición X de un círculo var


circleX=20;

// comienza a animar el círculo en el lienzo // borrando y


redibujando continuamente el círculo // en nuevas posiciones
requestAnimationFrame(animate);

function animate(){ //
actualiza la posición X del círculo circleX++; //
redibujar el círculo en su nueva posición
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.beginPath(); ctx.arc( círculoX, 30,15,0,Math.PI*2 );
ctx.llenar(); // solicitar otro bucle animate()
requestAnimationFrame(animate);

Sección 8.2: Arrastrar círculos y rectángulos alrededor del lienzo


¿Qué es una "Forma"?

Por lo general, guarda sus formas creando un objeto de "forma" de JavaScript que representa cada forma.

var miCirculo = { x:30, y:20, radio:15 };

Por supuesto, en realidad no estás guardando formas. En cambio, está guardando la definición de cómo dibujar las formas.

Luego coloque cada objeto de forma en una matriz para una fácil referencia.

// guarda información relevante sobre las formas dibujadas en el lienzo var


formas=[];

// definir un círculo y guardarlo en la matriz de formas []

GoalKicker.com – Notas HTML5 Canvas para profesionales 99


Machine Translated by Google

formas.push( {x:10, y:20, radio:15, color de relleno:'azul'} );

// definir un rectángulo y guardarlo en la matriz de formas [] formas.push ( {x: 10, y:


100, ancho: 50, altura: 35, color de relleno: 'rojo'} );

Usar eventos del mouse para arrastrar

Arrastrar una forma o imagen requiere responder a estos eventos del mouse:

Al bajar el mouse:

Prueba si hay alguna forma debajo del ratón. Si una forma está debajo del mouse, el usuario tiene la intención de arrastrar esa forma. Por lo tanto,
mantenga una referencia a esa forma y establezca un indicador isDragging verdadero/falso que indique que se está llevando a cabo un arrastre.

Al mover el mouse:

Calcule la distancia que se ha arrastrado el mouse desde el último evento de movimiento del mouse y cambie la posición de la forma arrastrada por esa
distancia. Para cambiar la posición de la forma, cambie las propiedades de posición x,y en el objeto de esa forma.

En mouseup o mouseout:

El usuario tiene la intención de detener la operación de arrastre, así que borre el indicador "isDragging". Se completó el arrastre.

Demostración: arrastrar círculos y rectángulos en el lienzo

Esta demostración arrastra círculos y rectángulos en el lienzo respondiendo a los eventos del mouse y dando la ilusión de movimiento al borrar y
volver a dibujar.

// variables relacionadas con el


lienzo var canvas=document.createElement("canvas"); var
ctx=canvas.getContext("2d"); var cw=lienzo.ancho; var
ch=lienzo.altura; documento.cuerpo.appendChild(lienzo);
canvas.style.border='1px rojo sólido';

// utilizado para calcular la posición del lienzo en relación con la función


de ventana reOffset(){ var BB=canvas.getBoundingClientRect();
desplazamientoX=BB.izquierda; desplazamientoY=BB.superior;

} var
desplazamientoX,desplazamientoY;
reOffset(); ventana.onscroll=función(e){ reOffset(); }
ventana.onresize=function(e){ reOffset(); }
canvas.onresize=function(e){ reOffset(); }

// guarda información relevante sobre las formas dibujadas en el lienzo var formas=[]; //
definir un círculo y guardarlo en la matriz de formas [] formas.push ( {x: 30, y: 30, radio:
15, color: 'azul'} ); // definir un rectángulo y guardarlo en la matriz de formas [] formas.push
( {x: 100, y: -1, ancho: 75, altura: 35, color: 'rojo'} );

// variables relacionadas con


el arrastre var isDragging=false;
var inicioX,inicioY;

GoalKicker.com – Notas HTML5 Canvas para profesionales 100


Machine Translated by Google

// mantener el índice de la forma que se está arrastrando (si corresponde) var


selectedShapeIndex ;

// dibujar las formas en el lienzo drawAll ();

// escucha los eventos del ratón


canvas.onmousedown =handleMouseDown ;
canvas.onmousemove =handleMouseMove ;
canvas.onmouseup =manejarMouseUp ;
canvas.onmouseout =handleMouseOut ;

// dado el mouse X & Y (mx & my) y el objeto de forma // devuelve


verdadero/ falso si el mouse está dentro de la función de forma isMouseInShape
(mx , my , shape ) { if (shape.radius ) { // esto es un círculo var dx =mx -forma.
x ; var dy = mi -forma. y ; // prueba matemática para ver si el mouse está
dentro del círculo if (dx *dx +dy *dy <shape.radius *shape.radius )
{ // sí, el mouse está dentro de este círculo return (true );

} }else if (forma.ancho ) {
// esto es un rectángulo var rLeft
=forma. x ; var rRight = forma.
x +forma.ancho ; var rTop =forma. y ; var
rBott =forma. y +forma.altura ; // prueba
matemática para ver si el mouse está dentro
del rectángulo if ( mx >rLeft && mx <rRight && my >rTop && my
<rBott ) { return (true ) ;

} // el ratón no está en ninguna de las formas return


(false );
}

función manejarMouseDown ( e ) {
// decirle al navegador que estamos manejando este evento
e.preventDefault ( ) ; e.detener la propagación (); // calcular la
posición actual del ratón startX =parseInt (e.clientX -offsetX ) ;
startY =parseInt (e.clientY -offsetY ) ; // prueba la posición del
mouse contra todas las formas // publica el resultado si el mouse
tiene una forma para (var i = 0 ; i <shapes.length ; i++) {

if (isMouseInShape (startX ,startY ,shapes [ i ] ) ) { // el mouse


está dentro de esta forma // selecciona esta forma
selectedShapeIndex = i ; // establecer el indicador isDragging
isDragging =true ; // y regresa (== deja de buscar //

otras formas bajo el ratón) return ;

}
}
}

GoalKicker.com – Notas HTML5 Canvas para profesionales 101


Machine Translated by Google

function handleMouseUp(e){ // regresa


si no estamos arrastrando if(!isDragging)
{return;} // le dice al navegador que estamos
manejando este evento e.preventDefault(); e.detener la
propagación(); // el arrastre ha terminado -- borre el indicador
isDragging isDragging=false;

function handleMouseOut(e){ // regresa


si no estamos arrastrando if(!isDragging)
{return;} // le dice al navegador que estamos
manejando este evento e.preventDefault(); e.detener la
propagación(); // el arrastre ha terminado -- borre el indicador
isDragging isDragging=false;

function handleMouseMove(e){ // regresa


si no estamos arrastrando if(!isDragging)
{return;} // le dice al navegador que estamos
manejando este evento e.preventDefault(); e.detener la
propagación(); // calcular la posición actual del ratón
mouseX=parseInt(e.clientX-offsetX); mouseY=parseInt(e.clientY-
offsetY); // ¿Qué tan lejos se ha arrastrado el mouse desde su
posición anterior de movimiento del mouse? var dx=mouseX-
startX; var dy=mouseY-startY; // mueve la forma seleccionada la
distancia de arrastre var selectedShape=shapes[selectedShapeIndex]; FormaSeleccionada.x+=dx;
FormaSeleccionada.y+=dy; // limpia el lienzo y vuelve a dibujar todas las formas drawAll(); // actualiza
la posición inicial de arrastre (== la posición actual del mouse) startX=mouseX; inicioY=mouseY;

// limpia el lienzo y // vuelve a


dibujar todas las formas en sus posiciones actuales función
dibujarTodo(){ ctx.clearRect(0,0,cw,ch); for(var i=0;i<formas.longitud;i+
+){ var forma=formas[i]; if(shape.radius){ // es un círculo
ctx.beginPath(); ctx.arc(forma.x,forma.y,forma.radio,0,Math.PI*2);
ctx.fillStyle=forma.color; ctx.llenar(); }else if(shape.width)
{ // es un rectángulo ctx.fillStyle=shape.color;
ctx.fillRect(forma.x,forma.y,forma.ancho,forma.altura);

}
}
}

GoalKicker.com – Notas HTML5 Canvas para profesionales 102


Machine Translated by Google

Sección 8.3: Arrastrar formas irregulares alrededor del lienzo


La mayoría de los dibujos de Canvas son rectangulares (rectángulos, imágenes, bloques de texto) o circulares (círculos).

Los círculos y rectángulos tienen pruebas matemáticas para verificar si el mouse está dentro de ellos. Esto hace que probar círculos y rectángulos
sea fácil, rápido y eficiente. Puede "probar" cientos de círculos o rectángulos en una fracción de segundo.

También puede arrastrar formas irregulares. Pero las formas irregulares no tienen una prueba matemática rápida. Afortunadamente, las formas
irregulares tienen una prueba de impacto incorporada para determinar si un punto (mouse) está dentro de la forma: context.isPointInPath. Si bien
isPointInPath funciona bien, no es tan eficiente como las pruebas de acierto puramente matemáticas; a menudo es hasta 10 veces más lenta que las
pruebas de acierto puramente matemáticas.

Un requisito al usar isPointInPath es que debe "redefinir" la ruta que se está probando inmediatamente antes de llamar a isPointInPath. "Redefinir"
significa que debe ejecutar los comandos de dibujo de la ruta (como se indicó anteriormente), pero no necesita trazar () o rellenar () la ruta antes de
probarla con isPointInPath. De esta manera, puede probar las rutas dibujadas previamente sin tener que sobrescribir (trazar/rellenar) esas rutas
anteriores en el lienzo.

La forma irregular no necesita ser tan común como el triángulo cotidiano. También puede hacer una prueba de acierto de cualquier ruta
extremadamente irregular.

Este ejemplo anotado muestra cómo arrastrar formas de Trazado irregulares, así como círculos y rectángulos:

// variables relacionadas con el


lienzo var canvas=document.createElement("canvas"); var
ctx=canvas.getContext("2d"); var cw=lienzo.ancho; var
ch=lienzo.altura; documento.cuerpo.appendChild(lienzo);
canvas.style.border='1px rojo sólido';

// utilizado para calcular la posición del lienzo en relación con la función


de ventana reOffset(){ var BB=canvas.getBoundingClientRect();
desplazamientoX=BB.izquierda; desplazamientoY=BB.superior;

} var

desplazamientoX,desplazamientoY; reOffset();
ventana.onscroll=función(e){ reOffset(); }
ventana.onresize=function(e){ reOffset(); } canvas.onresize=function(e){ reOffset(); }

// guarda información relevante sobre las formas dibujadas en el lienzo var formas=[]; //
definir un círculo y guardarlo en la matriz de formas [] formas.push ( {x: 20, y: 20, radio:
15, color: 'azul'} ); // definir un rectángulo y guardarlo en la matriz de formas []
formas.push ( {x: 100, y: -1, ancho: 75, altura: 35, color: 'rojo'} ); // definir una ruta de
triángulo y guardarla en la matriz de formas [] formas.push ( {x: 0, y: 0, puntos: [{x: 50,
y: 30}, {x: 75, y: 60} ,{x:25,y:60}],color:'verde'} );

// variables relacionadas con


el arrastre var isDragging=false;
var inicioX,inicioY;

// mantener el índice de la forma que se está arrastrando (si corresponde)


var selectedShapeIndex;

// dibujar las formas en el lienzo

GoalKicker.com – Notas HTML5 Canvas para profesionales 103


Machine Translated by Google

dibujarTodo();

// escucha los eventos del mouse


canvas.onmousedown=handleMouseDown;
canvas.onmousemove=handleMouseMove;
canvas.onmouseup=manejarMouseUp;
canvas.onmouseout=manejarRatónFuera;

// dado el mouse X & Y (mx & my) y el objeto de forma // devuelve


verdadero/ falso si el mouse está dentro de la función de forma
isMouseInShape(mx,my,shape){ if(shape.radius){ // esto es un círculo var
dx=mx-forma.x; var dy=mi-forma.y; // prueba matemática para ver si el
mouse está dentro del círculo if(dx*dx+dy*dy<shape.radius*shape.radius)
{ // sí, el mouse está dentro de este círculo return(true);

} }else if(forma.ancho){
// esto es un rectángulo var
rLeft=shape.x; var
rRight=forma.x+forma.ancho; var rTop=forma.y;
var rBott=forma.y+forma.altura; // prueba
matemática para ver si el mouse está dentro
del rectángulo if( mx>rLeft && mx<rRight && my>rTop && my<rBott)
{ return(true);

} }else if(shape.points){ // esta es


una ruta de polilínea // Primero
redefine la ruta nuevamente (¡no es necesario trazar/ rellenar!)
defineIrregularPath(forma); // Luego haga una prueba de acierto con isPointInPath
if(ctx.isPointInPath(mx,my)){ return(true);

} // el ratón no está en ninguna de las formas return(false);

function handleMouseDown(e){ // decirle


al navegador que estamos manejando este evento
e.preventDefault(); e.detener la propagación(); // calcular la
posición actual del ratón startX=parseInt(e.clientX-offsetX);
startY=parseInt(e.clientY-offsetY); // prueba la posición del mouse
contra todas las formas // publica el resultado si el mouse tiene
una forma for(var i=0;i<shapes.length;i++)
{ if(isMouseInShape(startX,startY,shapes[i])){ / / el ratón está
dentro de esta forma // selecciona esta forma selectedShapeIndex=i; //
establecer el indicador isDragging isDragging=true; // y return
(==dejar de buscar // más formas bajo el ratón) return;

GoalKicker.com – Notas HTML5 Canvas para profesionales 104


Machine Translated by Google

}
}
}

function handleMouseUp(e){ // regresa


si no estamos arrastrando if(!isDragging)
{return;} // le dice al navegador que estamos
manejando este evento e.preventDefault(); e.detener la
propagación(); // el arrastre ha terminado -- borre el indicador
isDragging isDragging=false;

function handleMouseOut(e){ // regresa


si no estamos arrastrando if(!isDragging)
{return;} // le dice al navegador que estamos
manejando este evento e.preventDefault(); e.detener la
propagación(); // el arrastre ha terminado -- borre el indicador
isDragging isDragging=false;

function handleMouseMove(e){ // regresa


si no estamos arrastrando if(!isDragging)
{return;} // le dice al navegador que estamos
manejando este evento e.preventDefault(); e.detener la
propagación(); // calcular la posición actual del ratón
mouseX=parseInt(e.clientX-offsetX); mouseY=parseInt(e.clientY-
offsetY); // ¿Qué tan lejos se ha arrastrado el mouse desde su
posición anterior de movimiento del mouse? var dx=mouseX-
startX; var dy=mouseY-startY; // mueve la forma seleccionada la
distancia de arrastre var selectedShape=shapes[selectedShapeIndex]; FormaSeleccionada.x+=dx;
FormaSeleccionada.y+=dy; // limpia el lienzo y vuelve a dibujar todas las formas drawAll(); // actualiza
la posición inicial de arrastre (== la posición actual del mouse) startX=mouseX; inicioY=mouseY;

// limpia el lienzo y // vuelve a


dibujar todas las formas en sus posiciones actuales función
dibujarTodo(){ ctx.clearRect(0,0,cw,ch); for(var i=0;i<formas.longitud;i+
+){ var forma=formas[i]; if(shape.radius){ // es un círculo
ctx.beginPath(); ctx.arc(forma.x,forma.y,forma.radio,0,Math.PI*2);
ctx.fillStyle=forma.color; ctx.llenar(); }else if(shape.width)
{ // es un rectángulo ctx.fillStyle=shape.color;

GoalKicker.com – Notas HTML5 Canvas para profesionales 105


Machine Translated by Google

ctx.fillRect(forma.x,forma.y,forma.ancho,forma.altura); }else if(forma.puntos){

// es una ruta de polilínea


defineRutaIrregular(forma);
ctx.fillStyle=forma.color; ctx.llenar();

}
}
}

function definerrutairregular(forma){ var


puntos=forma.puntos; ctx.beginPath();
ctx.moveTo(forma.x+puntos[0].x,forma.y+puntos[0].y);
for(var i=1;i<puntos.longitud;i++){ ctx.lineTo(forma.x+puntos[i].x,forma.y+puntos[i].y);

} ctx.closePath();
}

Sección 8.4: Arrastrar imágenes por el lienzo


Consulte este ejemplo para obtener una explicación general sobre cómo arrastrar formas alrededor del lienzo.

Este ejemplo anotado muestra cómo arrastrar imágenes alrededor del lienzo.

// variables relacionadas con el


lienzo var canvas=document.createElement("canvas"); var
ctx=canvas.getContext("2d"); lienzo.ancho=378; lienzo.altura=378;
var cw=lienzo.ancho; var ch=lienzo.altura;
documento.cuerpo.appendChild(lienzo); canvas.style.border='1px
rojo sólido';

// utilizado para calcular la posición del lienzo en relación con la función


de ventana reOffset(){ var BB=canvas.getBoundingClientRect();
desplazamientoX=BB.izquierda; desplazamientoY=BB.superior;

} var
desplazamientoX,desplazamientoY;
reOffset(); ventana.onscroll=función(e){ reOffset(); }
ventana.onresize=function(e){ reOffset(); }
canvas.onresize=function(e){ reOffset(); }

// guarda información relevante sobre las formas dibujadas en el lienzo var formas=[];

// variables relacionadas con


el arrastre var isDragging=false;
var inicioX,inicioY;

// mantener el índice de la forma que se está arrastrando (si corresponde) var


selectedShapeIndex;

// carga la imagen var


card=new Image();
card.onload=función(){

GoalKicker.com – Notas HTML5 Canvas para profesionales 106


Machine Translated by Google

// define una imagen y la guarda en la matriz de formas [] formas.push ( {x: 30,


y: 10, ancho: 127, altura: 150, imagen: tarjeta} ); // dibuja las formas en el lienzo drawAll(); //
escucha los eventos del mouse canvas.onmousedown=handleMouseDown;
canvas.onmousemove=handleMouseMove; canvas.onmouseup=manejarMouseUp;
canvas.onmouseout=manejarRatónFuera;

}; // ¡Pon tu imagen src aquí!


card.src='https://dl.dropboxusercontent.com/u/139992952/stackoverflow/card.png';

// dado el mouse X & Y (mx & my) y el objeto de forma // devuelve


verdadero/ falso si el mouse está dentro de la función de forma
isMouseInShape(mx,my,shape){
// ¿Esta forma es una imagen?
if(shape.image){ // este es un
rectángulo var rLeft=shape.x;
var rRight=forma.x+forma.ancho;
var rTop=forma.y; var
rBott=forma.y+forma.altura; // prueba
matemática para ver si el mouse está dentro
de la imagen if( mx>rLeft && mx<rRight && my>rTop &&
my<rBott){ return(true);

} // el ratón no está en ninguna de estas formas return(false);

function handleMouseDown(e){ // decirle


al navegador que estamos manejando este evento
e.preventDefault(); e.detener la propagación(); // calcular la
posición actual del ratón startX=parseInt(e.clientX-offsetX);
startY=parseInt(e.clientY-offsetY); // prueba la posición del mouse
contra todas las formas // publica el resultado si el mouse tiene
una forma for(var i=0;i<shapes.length;i++){

if(isMouseInShape(startX,startY,shapes[i])){ // el mouse está


dentro de esta forma // selecciona esta forma
selectedShapeIndex=i; // establecer el indicador isDragging
isDragging=true; // y regresa (== deja de buscar //

otras formas bajo el ratón) volver;

}
}
}

function handleMouseUp(e){ // regresa


si no estamos arrastrando if(!isDragging)
{return;} // le dice al navegador que estamos
manejando este evento e.preventDefault(); e.detener la
propagación();

GoalKicker.com – Notas HTML5 Canvas para profesionales 107


Machine Translated by Google

// el arrastre ha terminado -- borre el indicador isDragging


isDragging=false;
}

function handleMouseOut(e){ // regresa


si no estamos arrastrando if(!isDragging)
{return;} // le dice al navegador que estamos
manejando este evento e.preventDefault(); e.detener la
propagación(); // el arrastre ha terminado -- borre el indicador
isDragging isDragging=false;

function handleMouseMove(e){ // regresa


si no estamos arrastrando if(!isDragging)
{return;} // le dice al navegador que estamos
manejando este evento e.preventDefault(); e.detener la
propagación(); // calcular la posición actual del ratón
mouseX=parseInt(e.clientX-offsetX); mouseY=parseInt(e.clientY-
offsetY); // ¿Qué tan lejos se ha arrastrado el mouse desde su
posición anterior de movimiento del mouse? var dx=mouseX-
startX; var dy=mouseY-startY; // mueve la forma seleccionada la
distancia de arrastre var selectedShape=shapes[selectedShapeIndex]; FormaSeleccionada.x+=dx;
FormaSeleccionada.y+=dy; // limpia el lienzo y vuelve a dibujar todas las formas drawAll(); // actualiza
la posición inicial de arrastre (== la posición actual del mouse) startX=mouseX; inicioY=mouseY;

// limpia el lienzo y // vuelve a


dibujar todas las formas en sus posiciones actuales función
dibujarTodo(){ ctx.clearRect(0,0,cw,ch); for(var i=0;i<formas.longitud;i+
+){ var forma=formas[i]; if(shape.image){ // es una imagen
ctx.drawImage(shape.image,shape.x,shape.y);

}
}
}

GoalKicker.com – Notas HTML5 Canvas para profesionales 108


Machine Translated by Google

Capítulo 9: Tipos de medios y el lienzo


Sección 9.1: Carga y reproducción básica de un video en el lienzo
El lienzo se puede usar para mostrar videos de una variedad de fuentes. Este ejemplo muestra cómo cargar un video como un recurso de archivo, mostrarlo y

agregar un simple clic en la pantalla para alternar reproducción/pausa.

Esta pregunta auto respondida de stackoverflow ¿Cómo muestro un video usando la etiqueta de lienzo HTML5? muestra el siguiente código de ejemplo en

acción.

solo una imagen

Un video es solo una imagen en lo que respecta al lienzo. Puedes dibujarlo como cualquier imagen. La diferencia es que el video puede reproducirse y tiene

sonido.

Obtener lienzo y configuración básica

// Se supone que sabe cómo agregar un lienzo y dimensionarlo correctamente. var


canvas = document.getElementById("myCanvas"); // obtener el lienzo de la página var ctx =
canvas.getContext("2d"); var videoContainer; // objeto para contener video e información asociada

Creando y cargando el video

var video = documento.createElement("video"); // crea un elemento de video video.src =


"urlOffVideo.webm"; // el video ahora comenzará a cargarse.

// Como se necesita información adicional, colocaremos el video en un // objeto


contenedor por conveniencia video.autoPlay = false; // asegúrese de que el video
no se reproduzca automáticamente video.loop = true; // establece el video en bucle.
videoContainer = { // agregaremos propiedades según sea necesario

video : video,
listo : falso,
};

A diferencia de los elementos de imágenes, los videos no tienen que estar completamente cargados para mostrarse en el lienzo. Los videos también brindan
una gran cantidad de eventos adicionales que se pueden usar para monitorear el estado del video.

En este caso, deseamos saber cuándo el video está listo para reproducirse. oncanplay significa que se ha cargado suficiente video para reproducirlo, pero es

posible que no haya suficiente para reproducirlo hasta el final.

video.oncanplay = readyToPlayVideo; // establece el evento en la función de reproducción que


// se puede encontrar a continuación

Alternativamente, puede usar oncanplaythrough , que se activará cuando se haya cargado suficiente video para que pueda reproducirse hasta el final.

video.oncanplaythrough = readyToPlayVideo; // establece el evento en la función de reproducción que // se


puede encontrar a continuación

Solo use uno de los eventos canPlay, no ambos.

El evento can play (equivalente a la carga de imágenes)

función readyToPlayVideo(evento){ // esta es una referencia al video // es posible que el


video no coincida con el tamaño del lienzo, así que busque una escala que se ajuste
a videoContainer.scale = Math.min(
lienzo.ancho / este.videoAncho,

GoalKicker.com – Notas HTML5 Canvas para profesionales 109


Machine Translated by Google

canvas.height / this.videoHeight); videoContainer.listo


= verdadero; // el video se puede reproducir, así que transfiéralo a la función de
visualización requestAnimationFrame(undateCanvas);

Mostrando

El video no se reproducirá solo en el lienzo. Necesitas dibujarlo para cada nuevo cuadro. Como es difícil saber la velocidad de fotogramas exacta y
cuándo ocurren, el mejor enfoque es mostrar el video como si se estuviera ejecutando a 60 fps. Si la velocidad de fotogramas es más baja, simplemente
renderice el mismo fotograma dos veces. Si la velocidad de fotogramas es más alta, no se puede hacer nada para ver los fotogramas adicionales, así
que simplemente los ignoramos.

El elemento de video es solo un elemento de imagen y se puede dibujar como cualquier imagen, puede escalar, rotar, desplazar el video, reflejarlo,
desvanecerlo, recortarlo y mostrar solo partes, dibujarlo dos veces la segunda vez con un modo compuesto global para agregar FX como aligerar, pantalla,
etc.

función actualizarCanvas(){
ctx.clearRect(0,0,lienzo.ancho,lienzo.alto); // Aunque no siempre es necesario // puede obtener píxeles
defectuosos de // videos anteriores, así que
claro para estar // seguro

// dibujar solo si está cargado y listo


if(videoContainer !== undefined && videoContainer.ready){ // encuentra la parte
superior izquierda del video en el lienzo var scale = videoContainer.scale; var
vidH = videoContainer.video.videoHeight; var vidW =
videoContainer.video.videoWidth; var top = canvas.height / 2 - (vidH /2 ) *
escala; var izquierda = canvas.width / 2 - (vidW /2 ) * escala; // ahora
simplemente dibuje el video del tamaño correcto ctx.drawImage(videoContainer.video,
left, top, vidW * scale, vidH * scale); if(videoContainer.video.paused){ // si no se
reproduce muestra la pantalla en pausa drawPayIcon();

} // todo hecho para mostrar //


solicita el siguiente cuadro en 1/60 de segundo
requestAnimationFrame(updateCanvas);
}

Control de pausa de reproducción básico

Ahora que tenemos el video cargado y mostrado, todo lo que necesitamos es el control de reproducción. Lo haremos como un clic para alternar reproducir
en la pantalla. Cuando el video se está reproduciendo y el usuario hace clic, el video se detiene. Cuando está en pausa, el clic reanuda la reproducción.
Añadiremos una función para oscurecer el vídeo y dibujaremos un icono de reproducción (triángulo)

función dibujarPayIcon()
{ ctx.fillStyle = "negro"; // oscurecer la pantalla ctx.globalAlpha =
0.5; ctx.fillRect(0,0,lienzo.ancho,lienzo.alto); ctx.fillStyle =
"#DDD"; // color del icono de reproducción ctx.globalAlpha = 0.75; //
parcialmente transparente ctx.beginPath(); // crea la ruta para el
ícono var size = (canvas.height / 2) * 0.5; // el tamaño del icono
ctx.moveTo(canvas.width/2 + size/2, canvas.height / 2); // comienza en
el extremo puntiagudo ctx.lineTo(canvas.width/2 - size/2, canvas.height / 2 + size);
ctx.lineTo(canvas.width/2 - size/2, canvas.height / 2 - size);

GoalKicker.com – Notas HTML5 Canvas para profesionales 110


Machine Translated by Google

ctx.closePath();
ctx.llenar();
ctx.globalAlpha = 1; // restaurar alfa
}

Ahora la función de evento de

pausa de reproducción playPauseClick()


{ if(videoContainer !== undefined && videoContainer.ready){ if(videoContainer.video.paused)
{
videoContainer.video.play(); }
else{ videoContainer.video.pause();

}
}

} // registrar el evento
canvas.addEventListener("click",playPauseClick);

Resumen

Reproducir un video es muy fácil usando el lienzo, agregar efectos en tiempo real también es fácil. Sin embargo, existen algunas
limitaciones en los formatos, cómo puede jugar y buscar. MDN HTMLMediaElement es el lugar para obtener la referencia completa al objeto de
video.

Una vez que la imagen se ha dibujado en el lienzo, puede usar ctx.getImageData para acceder a los píxeles que contiene. O puede usar
canvas.toDataURL para capturar una imagen fija y descargarla. (Solo si el video es de una fuente confiable y no contamina el lienzo).

Tenga en cuenta que si el video tiene sonido, al reproducirlo también se reproducirá el sonido.

Feliz video.

Sección 9.2: Capturar lienzo y guardar como video webM


Creación de un video WebM a partir de marcos de lienzo y reproducción en lienzo, carga o descarga.

Ejemplo de lienzo de captura y reproducción

nombre = "Captura de lienzo"; // Colocado en los campos Mux y Write Application Name de la calidad del encabezado WebM = 0.7; // buena calidad 1
Óptima < 0.7 aceptable a pobre fps = 30; // He probado todo tipo de frecuencias de cuadro y todas parecen funcionar

// Haga alguna prueba para averiguar lo que su máquina puede manejar, ya que // hay mucha
variación entre las máquinas.
var video = nueva función Groover.Video(fps,calidad,nombre) capture()
{ if(video.timecode < 5000){ // 5 segundos

setTimeout(captura,video.frameDelay); }else{ var


videoElement = document.createElement("video");
videoElement.src = URL.createObjectURL(video.toBlob());
documento.cuerpo.appendChild(videoElement); vídeo = indefinido; // Dereference
ya que tiene hambre de memoria. regreso;

} // el primer cuadro establece el tamaño del video


video.addFrame(canvas); // Agregar marco de lienzo actual

} capturar(); // iniciar la captura

GoalKicker.com – Notas HTML5 Canvas para profesionales 111


Machine Translated by Google

En lugar de hacer un gran esfuerzo solo para ser rechazado, esta es una inserción rápida para ver si es aceptable. Dará todos los detalles si es aceptado.
También incluya opciones de captura adicionales para mejores tasas de captura HD (eliminado de esta versión, puede capturar HD 1080 a 50 fps en
buenas máquinas).

Esto fue inspirado por Wammy pero es una reescritura completa con la metodología de codificación sobre la marcha, lo que reduce en gran medida la
memoria requerida durante la captura. Puede capturar más de 30 segundos mejores datos, manejando algoritmos.

Los marcos de notas se codifican en imágenes webP. Solo Chrome admite la codificación de lienzo webP. Para otros navegadores
(Firefox y Edge), deberá usar un codificador webP de terceros, como Libwebp Javascript. La codificación de imágenes WebP a través
de Javascript es lenta. (incluirá la adición de soporte de imágenes webp sin procesar si se acepta).

El codificador webM inspirado en Whammy: A Real Time Javascript WebM

var Groover = (función(){


// asegurar que webp sea
compatible function canEncode()
{ var canvas = document.createElement("canvas");
lienzo.ancho = 8; lienzo.altura = 8; return
canvas.toDataURL("imagen/webp",0.1).indexOf("imagen/
webp") > -1;

} if(!canEncode())
{ devuelve indefinido;

} var webmData = nulo;


var clusterTimecode = 0; var
contadorContador = 0; var
CLUSTER_MAX_DURATION = 30000;
var número de cuadro = 0; ancho variable ;
altura variable ; var frameDelay; calidad
var ; nombre de la variable ; const
videoMimeType = "video/webm"; // el
único. const frameMimeType = 'imagen/
webp'; // no puede ser otro const S =
String.fromCharCode; const tipos de datos = { objeto :
función(datos){ volver aBlob (datos);}, número : función(datos)
{ volver corriente.num(datos);}, cadena : función(datos){ volver
corriente.str(datos) ;}, matriz : función(datos){ devolver datos;},
double2Str : función(num){

var c = new Uint8Array((new Float64Array([num])).buffer); devuelve


S(c[7]) + S(c[6]) + S(c[5]) + S(c[4]) + S(c[3]) + S(c[2]) + S (c[1]) + S(c[0]);
}
};

const stream =
{ num : function(num){ // escribe int var
partes = []; while(num > 0)
{ partes.push(num & 0xff); número = número >> 8; } devuelve nuevo
Uint8Array(parts.reverse());
},
str : function(str){ // escribe una cadena
var i, len, arr; len =
str.longitud;

GoalKicker.com – Notas HTML5 Canvas para profesionales 112


Machine Translated by Google

arr = new Uint8Array(largo);


for(i = 0; i < len; i++){arr[i] = str.charCodeAt(i);}
retorno arr;
},
compInt : function(num){ // no se pudieron encontrar los detalles completos, así que es una suposición
if(num < 128){ // el número tiene como prefijo un bit (1000 está en el byte 0100 dos, 0010
tres y así sucesivamente)
número += 0x80;
devolver nuevo Uint8Array([num]);
}demás
si (num < 0x4000){
número += 0x4000 ;
devolver nuevo Uint8Array([núm>>8, número])
}demás
si (num < 0x200000){
número += 0x200000 ;
devolver nuevo Uint8Array([num>>16, numero>>8, numero])
}demás
si (num < 0x10000000){
número += 0x10000000;
devolver nuevo Uint8Array([num>>24, numero>>16, numero>>8, numero])
}
}
}
const ids = { // nombres y valores de encabezado
videoData : 0x1a45dfa3,
Versión : 0x4286,
Versión de lectura : 0x42f7 ,
Longitud máxima de ID : 0x42f2 ,
MaxSizeLength : 0x42f3,
Tipo de documento : 0x4282 ,

Versión de tipo de documento : 0x4287 ,


Versión de lectura de tipo de documento : 0x4285 ,
Segmento : 0x18538067,
Información : 0x1549a966,
TimecodeScale : 0x2ad7b1,
muxingapp : 0x4d80,
aplicación de escritura : 0x5741,
Duración : 0x4489,
Pistas : 0x1654ae6b,
Entrada de pista : 0xae,
Número de pista : 0xd7,
TrackUID : 0x63c5,
BanderaCordón : 0x9c,
Idioma : 0x22b59c,
ID de códec : 0x86,
Nombre del códec : 0x258688,
Tipo de pista : 0x83,
Video : 0xe0,
ancho de píxel : 0xb0,
Altura de píxel : 0xba,
Grupo : 0x1f43b675,
código de tiempo : 0xe7,
Marco : 0xa3,
Fotograma clave : 0x9d012a,
Bloque de marco : 0x81,
};
const keyframeD64Header = '\x9d\x01\x2a'; // Encabezado de fotograma clave VP8 0x9d012a
const videoDataPos = 1; // pos de datos del encabezado de datos del marco
const defaultDelay = dataTypes.double2Str(1000/25);
const header = [ // estructura del encabezado/ fragmentos de webM como se llamen.
ids.videoData,[

GoalKicker.com – Notas HTML5 Canvas para profesionales 113


Machine Translated by Google

ids.Version, 1,
ids.ReadVersion, 1,
ids.MaxIDLength, 4,
ids.MaxSizeLength, 8,
ids.DocType, 'webm',
ids.DocTypeVersion, 2,
ids.DocTypeReadVersion, 2
],
ids.Segment,
[ ids.Info,
[ ids.TimecodeScale, 1000000,
ids.MuxingApp, 'Groover', ids.WritingApp,
'Groover', ids.Duration, 0

],
ids.Tracks,
[ ids.TrackEntry,
[ ids.TrackNumber, 1,
ids.TrackUID, 1,
ids.FlagLacing, 0, // siempre o ids.Language,
'und', // indefinido Creo que esto significa ids .CodecID, 'V_VP8', // Creo que no
deben cambiar ids.CodecName, 'VP8', // Creo que no deben cambiar
ids.TrackType, 1, ids.Video, [ ids.PixelWidth, 0, ids. Altura de píxel, 0

]
]
]
]
];
función getHeader()
{ encabezado[3][2][3] = nombre;
encabezado[3][2][5] = nombre;
encabezado[3][2][7] = dataTypes.double2Str(frameDelay); encabezado[3][3][1]
[15][1] = ancho; encabezado[3][3][1][15][3] = altura; función crear(dat){ var
i,kv,datos; datos = []; for(i = 0; i < dat.longitud; i += 2){ kv = {i : dat[i]};
if(Array.isArray(dat[i + 1])){ kv.d = create(dat[i + 1]); }else{ kv.d = dat[i + 1];

} datos.push(kv);

} devolver datos;

} volver crear (encabezado);

} función addCluster()
{ webmData[videoDataPos].d.push({ i: ids.Cluster,d: [{ i: ids.Timecode, d:
Math.round(clusterTimecode)}]}); // Corregido error con Round clusterCounter = 0;

} function addFrame(marco){ var


VP8, kfS,riff; riff =
getWebPChunks(atob(frame.toDataURL(frameMimeType, quality).slice(23)));

GoalKicker.com – Notas HTML5 Canvas para profesionales 114


Machine Translated by Google

VP8 = riff.RIFF[0].WEBP[0]; kfS =


VP8.indexOf(keyframeD64Header) + 3; marco = { ancho:
((VP8.charCodeAt(kfS + 1) << 8) | VP8.charCodeAt(kfS)) &
0x3FFF, altura: ((VP8.charCodeAt(kfS + 3) << 8) | VP8.charCodeAt (kfS + 2)) & 0x3FFF, datos: VP8, riff:
riff

};
if(clusterCounter > CLUSTER_MAX_DURATION){ addCluster();

} webmData[videoDataPos].d[webmData[videoDataPos].d.length-1].d.push({ i: ids.Frame, d:
S(ids.FrameBlock) + S( Math.round(clusterCounter) >> 8) + S(

Math.round(clusterCounter) & 0xff) + S(128) + frame.data.slice(4),


});
clusterCounter += frameDelay;
clusterTimecode += frameDelay;
webmData[videoDataPos].d[0].d[3].d = dataTypes.double2Str(clusterTimecode);

} función iniciarCodificación(){
frameNumber = clusterCounter = clusterTimecode = 0; webmData =
getHeader(); agregarCluster();

} función toBlob(vidData){
var datos,i,vData, len; vDatos =
[]; for(i = 0; i < vidData.length; i+
+){ data = dataTypes[typeof vidData[i].d](vidData[i].d);
len = datos.tamaño || datos.byteLength || longitud de datos;
vData.push(stream.num(vidData[i].i)); vData.push(stream.compInt(largo));
vData.push(datos)

} devuelve un nuevo blob (vData, {tipo: videoMimeType});

} function getWebPChunks(str){ var


offset, chunks, id, len, data; compensación = 0;
trozos = {}; while (desplazamiento < str.longitud) {

id = str.substr(desplazamiento, 4); // el
valor tendrá el bit superior activado (bit 32), por lo que no es simplemente una operación bit a bit //
Advertencia little endian (no funcionará en sistemas big endian) len = new Uint32Array( new
Uint8Array([ str.charCodeAt(offset + 7), str.charCodeAt(desplazamiento + 6), str.charCodeAt(desplazamiento
+ 5), str.charCodeAt(desplazamiento + 4) ]).buffer)[0]; id = str.substr(desplazamiento, 4);
fragmentos[id] = fragmentos[id] === indefinido ? [] : fragmentos[id]; if (id === 'RIFF' || id ===
'LISTA') { trozos[id].push(getWebPChunks(str.substr(offset + 8, len))); desplazamiento += 8 +
largo; } else if (id === 'WEBP') { trozos[id].push(str.substr(offset + 8)); descanso; } else
{ fragmentos[id].push(str.substr(offset + 4));

GoalKicker.com – Notas HTML5 Canvas para profesionales 115


Machine Translated by Google

descanso;
}

} devuelve fragmentos;

} Codificador de función (fps, _calidad = 0.8, _nombre = "Groover"){ this.fps = fps;


this.calidad = calidad = _calidad; this.frameDelay = frameDelay = 1000 / fps;
este.marco = 0; este.ancho = ancho = nulo; este.código de tiempo = 0;
este.nombre = nombre = _nombre;

}
Encoder.prototype =
{ addFrame : function(frame){ if('canvas'
in frame){
marco = marco.lienzo;

} if(ancho === nulo)


{ este.ancho = ancho = marco.ancho, este.alto
= alto = marco.alto startEncoding(); }else if(ancho !
== marco.ancho || alto !== marco.alto){

throw RangeError(" Error de tamaño de fotograma. Los fotogramas deben tener el mismo tamaño.");

} añadirMarco(marco);
este.fotograma += 1;
this.timecode = clusterTimecode;
},
toBlob : function(){ return
toBlob(webmData);
}

} volver {
Vídeo: Codificador,

} })()

Sección 9.3: Dibujar una imagen svg


Para dibujar una imagen SVG vectorial, la operación no es diferente de una imagen rasterizada: primero
debe cargar su imagen SVG en un elemento HTMLImage y luego usar el método drawImage() .

var imagen = nueva imagen();


imagen.cargar = función(){
ctx.drawImage(esto, 0,0);

} imagen.src = "algunArchivo.SVG";

Las imágenes SVG tienen algunas ventajas sobre las rasterizadas, ya que no perderás calidad, sea cual sea la escala en la que lo dibujes en tu lienzo.
Pero cuidado, también puede ser un poco más lento que dibujar una imagen rasterizada.

Sin embargo, las imágenes SVG vienen con más restricciones que las imágenes rasterizadas.

Por motivos de seguridad, no se puede cargar ningún contenido externo desde una imagen SVG a la que se hace referencia en un
HTMLImageElement(<img>)
Sin hoja de estilo externa, sin imagen externa referenciada en elementos SVGImage (<imagen/>) , sin filtro externo o

GoalKicker.com – Notas HTML5 Canvas para profesionales 116


Machine Translated by Google

elemento vinculado por el atributo xlink:href (<use xlink:href="anImage.SVG#anElement"/>) o el método de atributo funcIRI (url()) , etc.

Además, las hojas de estilo adjuntas en el documento principal no tendrán ningún efecto en el documento SVG una vez que
se haga referencia en un elemento HTMLImage.
Finalmente, no se ejecutará ningún script dentro de la imagen SVG.
Solución alternativa: deberá agregar todos los elementos externos dentro del mismo SVG antes de hacer referencia al elemento
HTMLImage. (para imágenes o fuentes, debe agregar una versión dataURI de sus recursos externos).

El elemento raíz (<svg>) debe tener sus atributos de ancho y alto establecidos en un valor absoluto.
Si tuviera que usar la longitud relativa (por ejemplo , %), entonces el navegador no podrá saber a qué es relativo. Algunos navegadores
(Blink) intentarán adivinar, pero la mayoría simplemente ignorará su imagen y no dibujará nada, sin una advertencia.

Algunos navegadores mancharán el lienzo cuando se dibuje una imagen SVG.


Específicamente, Internet-Explorer <Edge en cualquier caso, y Safari 9 cuando un <foreignObject> está presente en la imagen SVG.

Sección 9.4: Cargar y mostrar una imagen


Para cargar una imagen y colocarla en el lienzo

var imagen = nueva imagen(); // ver nota sobre la creación de una imagen image.src
= "imageURL"; imagen.cargar = función(){

ctx.drawImage(esto,0,0);
}

Creando una imagen

Hay varias formas de crear una imagen.

new Image()
document.createElement("img") <img src
= 'imageUrl' id='myImage'> Como parte del cuerpo HTML y recuperado con document.getElementById('myImage')

La imagen es un HTMLImageElement

Propiedad de imagen.src

El origen de la imagen puede ser cualquier URL de imagen válida o URL de datos codificados. Consulte las Observaciones de este tema para obtener más información sobre

los formatos de imagen y la compatibilidad.

image.src = "http://my.domain.com/images/myImage.jpg" image.src =


*
"data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="

*La dataURL es una imagen gif de 1 x 1 píxel que contiene

Comentarios sobre la carga y los errores

La imagen comenzará a cargarse cuando se establezca su propiedad src. La carga es sincrónica, pero el evento de carga no se llamará hasta
que la función o el código haya salido/regresado.

Si obtiene una imagen de la página (por ejemplo , document.getElementById ("myImage")) y su src está configurado, es posible que se haya
cargado o no. Puede verificar el estado de la imagen con HTMLImageElement.complete que será

GoalKicker.com – Notas HTML5 Canvas para profesionales 117


Machine Translated by Google

verdadero si es completo. Esto no significa que la imagen se haya cargado, significa que tiene

cargado
Hubo un error

La propiedad src no se ha establecido y es igual a la cadena vacía ""

Si la imagen proviene de una fuente no confiable y es posible que no se pueda acceder a ella por una variedad de razones, generará un evento
de error. Cuando esto suceda, la imagen estará rota. Si luego intenta dibujarlo en el lienzo, arrojará el siguiente error

DOMException no detectada: no se pudo ejecutar 'drawImage' en 'CanvasRenderingContext2D': el HTMLImageElement proporcionado


está en estado 'roto' .

Al proporcionar el evento image.onerror = myImgErrorHandler , puede tomar las medidas adecuadas para evitar errores.

GoalKicker.com – Notas HTML5 Canvas para profesionales 118


Machine Translated by Google

Capítulo 10: Animación


Sección 10.1: Use requestAnimationFrame() NO setInterval()
para bucles de animación
requestAnimationFrame es similar a setInterval, pero tiene estas importantes mejoras:

El código de animación se sincroniza con las actualizaciones de la pantalla para mayor eficiencia. El código de borrado +
redibujado está programado, pero no se ejecuta de inmediato. El navegador ejecutará el código borrar + redibujar solo cuando la pantalla
esté lista para actualizarse. Esta sincronización con el ciclo de actualización aumenta el rendimiento de su animación al darle a su código el
tiempo más disponible para completarse.

Cada ciclo siempre se completa antes de que se permita que comience otro ciclo. Esto evita el "desgarro", donde el usuario ve una
versión incompleta del dibujo. El ojo nota particularmente el lagrimeo y se distrae cuando ocurre el lagrimeo. Por lo tanto, evitar el
desgarro hace que su animación parezca más suave y consistente.

La animación se detiene automáticamente cuando el usuario cambia a una pestaña diferente del navegador. Esto ahorra energía
en los dispositivos móviles porque el dispositivo no está desperdiciando energía al calcular una animación que el usuario no puede actualmente
ver.

Las pantallas de los dispositivos se actualizarán unas 60 veces por segundo, por lo que requestAnimationFrame puede volver a dibujarse
continuamente a unos 60 "fotogramas" por segundo. El ojo ve movimiento a 20-30 fotogramas por segundo, por lo que requestAnimationFrame
puede crear fácilmente la ilusión de movimiento.

Tenga en cuenta que requestAnimationFrame se recupera al final de cada animateCircle. Esto se debe a que cada
'requestAnimatonFrame solo solicita una sola ejecución de la función de animación.

Ejemplo: simple `requestAnimationFrame

<!doctype html>
<html> <head>
<style>
body{ background-
color:white; } #canvas{border:1px rojo
sólido; } </estilo> <guión>
ventana.onload=(función(){

// variables relacionadas con el


lienzo var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d"); var cw=lienzo.ancho;
var ch=lienzo.altura;

// inicia la animación
requestAnimationFrame(animate);

función animar (hora actual) {

// dibuja un círculo completo al azar


var x=Math.random()*canvas.width; var
y=Math.random()*canvas.height; var
radio=10+Math.random()*15;
ctx.beginPath(); ctx.arc(x,y,radio,0,Math.PI*2);

ctx.fillStyle='#'+Math.floor(Math.random()*16777215).toString(16);

GoalKicker.com – Notas HTML5 Canvas para profesionales 119


Machine Translated by Google

ctx.llenar();

// solicitar otro bucle de animación


requestAnimationFrame(animate);
}

}); // finaliza $(función(){}); </script>


</head> <cuerpo> <lienzo
id="lienzo" ancho=512 alto=512></
lienzo> </cuerpo> </html>

Para ilustrar las ventajas de requestAnimationFrame, esta pregunta de stackoverflow tiene una demostración en vivo

Sección 10.2: Animar una imagen en el lienzo


Este ejemplo carga y anima una imagen en el lienzo

¡Consejo importante! Asegúrese de darle tiempo a su imagen para que se cargue por completo usando image.onload.

Código anotado

<!doctype html>
<html>

<cabeza>
<estilo> cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </
estilo> <guión> ventana.onload=(función(){

// variables relacionadas con el


lienzo var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d"); var cw=lienzo.ancho; var
ch=lienzo.altura;

// variables relacionadas con la animación


var minX=20; // Mantener la imagen animada //
var maxX=250; entre minX y maxX
var x=minX; var // La coordenada X actual
velocidadX=1; // La imagen se moverá a 1px por ciclo
var dirección=1; var // La dirección de la imagen: 1==hacia la derecha, -1==hacia la izquierda
y=20; // La coordenada Y

// Cargar una nueva


imagen // ¡¡¡IMPORTANTE!!! ¡Debes darle tiempo a la imagen para que se cargue usando
img.onload! var img=nueva imagen(); img.onload=inicio; img.src="https://
dl.dropboxusercontent.com/u/139992952/stackoverflow/sun.png"; function start () { // la
imagen está completamente cargada, así que comience a animar requestAnimationFrame (animar);

función animar (tiempo) {

GoalKicker.com – Notas HTML5 Canvas para profesionales 120


Machine Translated by Google
// limpia el lienzo
ctx.clearRect(0,0,cw,ch);

// dibuja
ctx.drawImage(img,x,y);

// actualizar
x += velocidadX * dirección; //
mantener "x" dentro de min y max
if(x<minX){ x=minX; dirección*=-1; } if(x>maxX)
{ x=maxX; dirección*=-1; }

// solicitar otro bucle de animación


requestAnimationFrame(animate);
}

}); // finaliza $(función(){}); </script> </


head> <cuerpo> <lienzo id="lienzo"
ancho=512 alto=512></lienzo> </
cuerpo> </html>

Sección 10.3: Establecer la velocidad de fotogramas mediante requestAnimationFrame

El uso de requestAnimationFrame puede actualizarse en algunos sistemas a más fotogramas por segundo que los 60 fps. 60 fps es la tasa
predeterminada si el renderizado puede mantenerse. Algunos sistemas funcionarán a 120 fps, tal vez más.

Si usa el siguiente método, solo debe usar velocidades de fotogramas que sean divisiones enteras de 60, de modo que (60 /
FRAMES_PER_SECOND) % 1 === 0 sea verdadero o obtendrá velocidades de fotogramas inconsistentes.

constante FOTOS_POR_SEGUNDO = 30; // Los valores válidos son 60,30,20,15,10... // establece


el tiempo mínimo para representar el siguiente cuadro const FRAME_MIN_TIME = (1000/60) * (60 /
FRAMES_PER_SECOND) - (1000/60) * 0.5 ; var lastFrameTime = 0; // la actualización de la función de tiempo del
último cuadro (tiempo) {

if(time-lastFrameTime < FRAME_MIN_TIME){ //omita el marco si la llamada es demasiado temprana


requestAnimationFrame(actualizar);
regreso; // regresa ya que no hay nada que hacer

} lastFrameTime = tiempo; // recordar la hora del cuadro renderizado // renderizar el cuadro


requestAnimationFrame(update); // obtener la próxima granja

} requestAnimationFrame(actualizar); // iniciar animacion

Sección 10.4: Facilitación usando ecuaciones de Robert Penners

Una aceleración hace que alguna variable cambie de manera desigual durante una duración.

"variable" debe poder expresarse como un número y puede representar una notable variedad de cosas:

una coordenada X,

el ancho de un rectángulo,
un ángulo de rotación, el
componente rojo de un color R,G,B. cualquier
cosa que se pueda expresar como un número.

GoalKicker.com – Notas HTML5 Canvas para profesionales 121


Machine Translated by Google

"duración" debe poder expresarse como un número y también puede ser una variedad de cosas:

un período de
tiempo, una distancia a
recorrer, una cantidad de bucles de animación a
ejecutar, cualquier cosa que se pueda expresar como

"desigualmente" significa que la variable progresa de manera desigual desde el principio hasta el final:

más rápido al principio y más lento al final, o viceversa, sobrepasa el final


pero retrocede hasta el final cuando termina la duración, avanza/retrocede elásticamente
repetidamente durante la duración, "rebota" en el final mientras se detiene como termina
la duración.

Reconocimiento: Robert Penner ha creado el "estándar de oro" de las funciones de aceleración.

Citar: https://github.com/danro/jquery-easing/blob/master/jquery.easing.js

// t: tiempo transcurrido dentro de la duración (currentTime-startTime), //


b: valor inicial, // c: cambio total desde el valor inicial (endingValue-
startingValue), // d: duración total var Easings={ easyInQuad: función ( t, b, c, d)
{ devuelve c*(t/=d)*t + b; }, easyOutQuad: function (t, b, c, d) { return -c *(t/
=d)*(t-2) + b; }, easyInOutQuad: function (t, b, c, d) { if ((t/=d/2) < 1) return c/2*t*t
+ b; volver -c/2 * ((--t)*(t-2) - 1) + b; }, easyInCubic: function (t, b, c, d)
{ return c*(t/=d)*t*t + b; }, easyOutCubic: function (t, b, c, d) { return c*((t=t/
d-1)*t*t + 1) + b; }, easyInOutCubic: función (t, b, c, d) {

si ((t/=d/2) < 1) devuelve c/2*t*t*t + b; devuelve


c/2*((t-=2)*t*t + 2) + b; }, easyInQuart: function
(t, b, c, d) { return c*(t/=d)*t*t*t + b; }, salida fácil:
función (t, b, c, d) {

volver -c * ((t=t/d-1)*t*t*t - 1) + b; },
facilidadEnSalidaCuarto: función (t, b, c, d) {

si ((t/=d/2) < 1) devuelve c/2*t*t*t*t + b; volver -c/


2 * ((t-=2)*t*t*t - 2) + b; }, easyInQuint: function (t,
b, c, d) { return c*(t/=d)*t*t*t*t + b; }, easyOutQuint:
función (t, b, c, d) {

devuelve c*((t=t/d-1)*t*t*t*t + 1) + b; },
facilidadEnSalidaQuint: función (t, b, c, d) {

GoalKicker.com – Notas HTML5 Canvas para profesionales 122


Machine Translated by Google

si ((t/=d/2) < 1) devuelve c/2*t*t*t*t*t + b; devuelve c/2*((t-


=2)*t*t*t*t + 2) + b; }, easyInSine: function (t, b, c, d) { return -c
* Math.cos(t/d * (Math.PI/2)) + c + b; }, facilidadFueraSeno: función
(t, b, c, d) {

return c * Math.sin(t/d * (Math.PI/2)) + b; }, facilitarInOutSine:


función (t, b, c, d) {

return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b; }, facilidadEnExpo:


función (t, b, c, d) {

devolver (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b; }, easyOutExpo: function


(t, b, c, d) { return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; },
facilidadEntradaSalidaExpo: función (t, b, c, d) {

si (t==0) devuelve b; si
(t==d) devuelve b+c; si ((t/=d/
2) < 1) devuelve c/2 * Math.pow(2, 10 * (t - 1)) + b; return c/2 * (-Math.pow(2, -10 * --t) + 2)
+ b; }, easyInCirc: function (t, b, c, d) { return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b; },
easyOutCirc: función (t, b, c, d) {

return c * Math.sqrt(1 - (t=t/d-1)*t) + b; }, facilitarInOutCirc:


función (t, b, c, d) {

si ((t/=d/2) < 1) devuelve -c/2 * (Math.sqrt(1 - t*t) - 1) + b; return c/2 * (Math.sqrt(1 - (t-
=2)*t) + 1) + b; }, easyInElastic: function (t, b, c, d) { var s=1.70158;var p=0;var a=c; si
(t==0) devuelve b; si ((t/=d)==1) devuelve b+c; si (!p) p=d*.3; si (a < Math.abs(c)) { a=c; var
s=p/4; } else var s = p/(2*Math.PI) * Math.asin (c/a); return -(a*Math.pow(2,10*(t-=1)) *
Math.sin( (t*ds)*(2*Math.PI)/p )) + b; }, easyOutElastic: function (t, b, c, d) { var
s=1.70158;var p=0;var a=c; si (t==0) devuelve b; si ((t/=d)==1) devuelve b+c; si (!p) p=d*.3; si
(a < Math.abs(c)) { a=c; var s=p/4; } else var s = p/(2*Math.PI) * Math.asin (c/a); return
a*Math.pow(2,-10*t) * Math.sin( (t*ds)*(2*Math.PI)/p ) + c + b; }, facilidadEnSalidaElástica:
función (t, b, c, d) {

var s=1.70158;var p=0;var a=c; si (t==0)


devuelve b; si ((t/=d/2)==2) devuelve b+c; si (!p) p=d*(.3*1.5); si (a < Math.abs(c)) { a=c; var s=p/4; } else
var s = p/(2*Math.PI) * Math.asin (c/a); si (t < 1) devuelve -.5*(a*Math.pow(2,10*(t-=1)) *
Math.sin( (t*ds)*(2*Math.PI)/p ) ) + b; return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*ds)*(2*Math.PI)/p )*.5 +
c + b; }, easyInBack: function (t, b, c, d, s) { if (s == indefinido) s = 1.70158; devuelve c*(t/=d)*t*((s+1)*t - s) + b; }, easyOutBack:
function (t, b, c, d, s) { if (s == indefinido) s = 1.70158;

GoalKicker.com – Notas HTML5 Canvas para profesionales 123


Machine Translated by Google

devuelve c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; }, easyInOutBack:


function (t, b, c, d, s) { if (s == indefinido) s = 1.70158; si ((t/=d/2) <
1) devuelve c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b; return c/2*((t-
=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; }, EasingInBounce: function
(t, b, c, d) { return c - Easings.easeOutBounce (dt, 0, c, d) + b; }, easyOutBounce: function (t, b,
c, d) { if ((t/=d) < (1/2.75)) {

devuelve c*(7.5625*t*t) + b;
} else if (t < (2/2.75)) { return
c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; } else if (t < (2.5/2.75)) { return
c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; } else { return c*(7.5625*(t-
=(2.625/2.75))*t + .984375) + b;

} },
fácilInOutBounce: función (t, b, c, d) {
si (t < d/2) devuelve Easings.easeInBounce (t*2, 0, c, d) * .5 + b; return Easings.easeOutBounce
(t*2-d, 0, c, d) * .5 + c*.5 + b; },

};

Ejemplo de uso:

// incluir el objeto Easings de arriba var Easings =


...

//
Demostración var
startTime; var valorprincipio=50; // comienzo de la coordenada x var
endValue=450; // finaliza la coordenada x var totalChange=endingValue-
beginningValue; var
DuraciónTotal=3000; // Sra

var keys=Object.keys(Easings);
ctx.textBaseline='medio';
requestAnimationFrame(animar);

función animar(tiempo){ var


PI2=Math.PI*2; if(!startTime)
{startTime=time;} var elapsedTime=Math.min(time-
startTime,totalDuration); ctx.clearRect(0,0,cw,ch); ctx.beginPath(); for(var
y=0;y<keys.length;y++){ var key=keys[y]; var flexibilización=Facilitaciones[clave];
var aligeradoX=aceleración( tiempo transcurrido,valor inicial,cambiototal,duracióntotal);

if(easedX>endingValue){easedX=endingValue;}
ctx.moveTo(easedX,y*15); ctx.arc(aceleradoX,y*15+10,5,0,PI2);
ctx.fillText(clave,460,y*15+10-1);

} ctx.llenar();
if(hora<tiempoInicio+DuraciónTotal){

GoalKicker.com – Notas HTML5 Canvas para profesionales 124


Machine Translated by Google

requestAnimationFrame(animar);
}
}

Sección 10.5: Animar en un intervalo específico (añadir un


nuevo rectángulo cada 1 segundo)
Este ejemplo agrega un nuevo rectángulo al lienzo cada 1 segundo (== un intervalo de 1 segundo)

Código anotado:

<!doctype html> <html>

<cabeza>
<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){

// variables relacionadas con el lienzo var


canvas=document.getElementById("canvas"); var
ctx=canvas.getContext("2d"); var cw=lienzo.ancho; var ch=lienzo.altura;

// variables de intervalo de animación var


nextTime=0; var duración=1000; ////ejecutar
la próxima animación comienza en "nextTime"
animación cada 1000ms

varx =20; // la X donde se dibuja el siguiente rect

// inicia la animación
requestAnimationFrame(animate);

función animar (hora actual) {

// espera a que ocurra la siguiente hora


if(currentTime<nextTime){ // solicita otro
ciclo de animación requestAnimationFrame(animate); //
el tiempo no ha transcurrido, así que solo devuelve
return;

} // establece nextTime
nextTime=currentTime+duration;

// agregar otro rectángulo cada 1000ms


ctx.fillStyle='#'+Math.floor(Math.random()*16777215).toString(16); ctx.fillRect(x,30,30,30);

// actualiza la posición X para el siguiente rectángulo x+=30;

// solicitar otro bucle de animación


requestAnimationFrame(animate);
}

}); // finaliza $(función(){});

GoalKicker.com – Notas HTML5 Canvas para profesionales 125


Machine Translated by Google

</script> </
head>
<cuerpo>
<lienzo id="lienzo" ancho=512 alto=512></lienzo> </cuerpo> </html>

Sección 10.6: Animar a una hora específica (un reloj animado)


Este ejemplo anima un reloj que muestra los segundos como una cuña rellena.

Código anotado:

<!doctype html> <html>

<cabeza>
<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){

// variables relacionadas con el lienzo


var canvas=document.getElementById("canvas"); var
ctx=canvas.getContext("2d"); var cw=lienzo.ancho; var
ch=lienzo.altura; // estilo de lienzo para el reloj
ctx.strokeStyle='lightgray'; ctx.fillStyle='azul cielo'; ctx.lineWidth=5;

// almacena en caché los valores de


uso frecuente var PI=Math.PI; var
fullCircle=PI*2; varsa=-PI/2 ; // == el
ángulo de las 12 en punto en context.arc

// inicia la animación
requestAnimationFrame(animate);

función animar (hora actual) {

// obtener el valor de segundos actual del reloj del sistema var date=new Date();
var segundos=fecha.getSeconds();

// limpia el lienzo
ctx.clearRect(0,0,cw,ch);

// dibuja un círculo completo (== la esfera del reloj);


ctx.beginPath(); ctx.moveTo(100,100);
ctx.arc(100,100,75,0,círculo completo); ctx.stroke(); // dibujar
una cuña que represente el valor actual de los segundos
ctx.beginPath(); ctx.moveTo(100,100);
ctx.arc(100,100,75,sa,sa+círculo completo*segundos/60); ctx.llenar();

GoalKicker.com – Notas HTML5 Canvas para profesionales 126


Machine Translated by Google

// solicitar otro bucle de animación


requestAnimationFrame(animate);
}

}); // finaliza $(función(){}); </script> </


head> <cuerpo> <lienzo id="lienzo"
ancho=512 alto=512></lienzo> </
cuerpo> </html>

Sección 10.7: No dibuje animaciones en sus controladores de eventos


(una aplicación de boceto simple)
Durante mousemove te inundan con 30 eventos de mouse por segundo. Es posible que no pueda volver a dibujar sus dibujos
30 veces por segundo. Incluso si puede, probablemente esté desperdiciando poder de cómputo dibujando cuando el navegador no
está listo para dibujar (desperdiciado == en ciclos de actualización de pantalla).

Por lo tanto, tiene sentido separar los eventos de entrada de sus usuarios (como el movimiento del mouse) del dibujo de su
animaciones

En los controladores de eventos, guarde todas las variables de eventos que controlan dónde se colocan los dibujos en el lienzo.
Pero en realidad no dibujes nada.

En un bucle requestAnimationFrame , renderice todos los dibujos en el lienzo utilizando la información guardada.

Al no dibujar en los controladores de eventos, no obliga a Canvas a intentar actualizar dibujos complejos a la velocidad de los eventos
del mouse.

Al hacer todo el dibujo en requestAnimationFrame , obtiene todos los beneficios descritos aquí Use
'requestanimationFrame' no 'setInterval' para los bucles de animación.

Código anotado:

<!doctype html> <html>

<cabeza>
<estilo>
cuerpo{ color de fondo: marfil; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){

registro de función () {console.log.apply (consola, argumentos);}

// variables del lienzo var


canvas=document.getElementById("canvas"); var
ctx=canvas.getContext("2d"); var cw=lienzo.ancho; var
ch=lienzo.altura; // establece el estilo del lienzo
ctx.strokeStyle='skyblue'; ctx.lineJoint='redondo'; ctx.lineCap='redondo';
ctx.lineWidth=6;

GoalKicker.com – Notas HTML5 Canvas para profesionales 127


Machine Translated by Google

// manejar la función de desplazamiento y cambio de


tamaño de ventanas reOffset (){ var
BB=canvas.getBoundingClientRect();
desplazamientoX=BB.izquierda; desplazamientoY=BB.superior;

} var
desplazamientoX,desplazamientoY;
reOffset(); ventana.onscroll=función(e){ reOffset(); }
ventana.onresize=function(e){ reOffset(); }

// vars para guardar puntos creados durante el manejo de mousemove var


points=[]; var últimaLongitud=0;

// inicia el bucle de animación


requestAnimationFrame(dibujar);

canvas.onmousemove=function(e){handleMouseMove(e);}

function handleMouseMove(e){ // decirle


al navegador que estamos manejando este evento
e.preventDefault(); e.detener la propagación();

// obtener la posición del ratón


mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);

// guarda la posición del ratón en la matriz de puntos[] // pero no dibuja


nada puntos.push({x:mouseX,y:mouseY});

function dibujar(){ //
¿No hay puntos adicionales? Solicite otro marco y devuelva var length=points.length;
if(longitud==últimaLongitud){requestAnimationFrame(dibujar);return;}

// dibujar los puntos adicionales var


point=points[lastLength]; ctx.beginPath();
ctx.moveTo(point.x,point.y) for(var
i=lastLength;i<longitud;i++){ point=points[i];
ctx.lineTo(punto.x,punto.y);

} ctx.stroke();

// solicitar otro bucle de animación


requestAnimationFrame(dibujar);
}

}); // fin de ventana.onload </script>


</head> <body> <h4>Mueva el
mouse sobre Canvas para dibujar</
h4> <canvas id="canvas" width=512
height=512></canvas> </ cuerpo> </html>

GoalKicker.com – Notas HTML5 Canvas para profesionales 128


Machine Translated by Google

Sección 10.8: Animación simple con contexto 2D y


requestAnimationFrame
Este ejemplo le mostrará cómo crear una animación simple utilizando el lienzo y el contexto 2D. Es asumido
sabes cómo crear y agregar un lienzo al DOM y obtener el contexto

// este ejemplo asume que se han creado ctx y canvas


const textToDisplay = "Este es un ejemplo que usa el lienzo para animar texto.";
const textStyle = "blanco";
const BGStyle = "negro"; // estilo de fondo
const textoVelocidad = 0.2; const // en píxeles por milisegundo
textHorMargin = 8; // tener el texto un poco fuera del lienzo

ctx.font = Math.floor(canvas.height * 0.8) + "px arial"; // tamaño de la fuente al 80% de la altura del lienzo
var textWidth = ctx.measureText(textToDisplay).width; // obtener el ancho del texto
var totalTextSize = (canvas.width + textHorMargin * 2 + textWidth);
ctx.textBaseline = "medio"; // no poner el texto en el centro vertical
= "izquierda";
ctx.textAlign // alinear a la izquierda
var textoX = lienzo.ancho + 8; // comienza con el texto fuera de la pantalla a la derecha
var textOffset = 0; // cuánto se ha movido el texto

var horainicio;
// esta función se llama una vez por fotograma, que dura aproximadamente 16,66 ms (60 fps)
actualización de la función // el tiempo pasa por requestAnimationFrame
(hora) { if ( hora de inicio === // obtener una referencia para la hora de inicio si este es el primer cuadro
indefinido) { hora de inicio = hora;
}
ctx.fillStyle = BGStyle;
ctx.fillRect(0, 0, lienzo.ancho, lienzo.alto); dibujando sobre eso // limpiar el lienzo por

desplazamiento = ((tiempo - tiempo de inicio ) * velocidad de texto) % (tamaño de texto total); // mueve el texto a la izquierda
de texto ctx.fillStyle = estilo de texto; // establecer el estilo de texto
ctx.fillText(textToDisplay, textX - textOffset, canvas.height / 2); // renderiza el texto

requestAnimationFrame(update);// todo hecho solicita el siguiente cuadro


}
requestAnimationFrame(update);// para empezar a solicitar el primer fotograma

Una demostración de este ejemplo en jsfiddle

Sección 10.9: Animar de [x0,y0] a [x1,y1]


Use vectores para calcular [x,y] incremental desde [startX,startY] hasta [endX,endY]

// dx es la distancia total a recorrer en la dirección X


var dx = finalX - inicioX;

// dy es la distancia total para moverse en la dirección Y


var dy = finalY - inicioY;

// usa un pct (porcentaje) para recorrer las distancias totales


// comienza en 0% que == el punto de partida
// termina en 100% que == luego punto final
var pct=0;

// usa dx & dy para calcular dónde está el [x,y] actual en un porcentaje dado
var x = startX + dx * pct/100;
var y = startY + dx * pct/100;

GoalKicker.com – Notas HTML5 Canvas para profesionales 129


Machine Translated by Google

Código de ejemplo:

// canvas vars var


canvas=document.createElement("canvas");
documento.cuerpo.appendChild(lienzo);
canvas.style.border='1px rojo sólido'; var
ctx=canvas.getContext("2d"); var cw=lienzo.ancho; var
ch=lienzo.altura; // estilos de lienzo ctx.strokeStyle='skyblue';
ctx.fillStyle='azul';

// animando vars var


pct=101; var
inicioX=20; var
inicioY=50; var
endX=225; var
endY=100; var
dx=finX-inicioX; var
dy=finY-inicioY;

// iniciar el ciclo de animación ejecutando


requestAnimationFrame(animate);

// escucha los eventos del ratón


window.onmousedown=(function(e){handleMouseDown(e);});
ventana.onmouseup=(función(e){handleMouseUp(e);});

// bucle en ejecución constante //


animará el punto desde startX,startY hasta endX,endY function
animate(time){
// demostración: volver a
ejecutar la animación if(+
+pct>100){pct=0;} // actualizar
x=startX+dx*pct/100;
y=inicioY+dy*pct/100; // dibujar
ctx.clearRect(0,0,cw,ch);
ctx.beginPath();
ctx.moveTo(inicioX,inicioY);
ctx.lineTo(finX,finY); ctx.stroke();
ctx.beginPath();
ctx.arc(x,y,5,0,Math.PI*2);
ctx.fill() // solicita otro bucle de
animación
requestAnimationFrame(animate);

GoalKicker.com – Notas HTML5 Canvas para profesionales 130


Machine Translated by Google

Capítulo 11: Colisiones e intersecciones


Sección 11.1: ¿Están colisionando 2 círculos?

// objetos circulares: { x:, y:, radio: } // devuelve


verdadero si los 2 círculos chocan // c1 y c2 son círculos
como se define arriba

function Colisión de Círculos(c1,c2){ var


dx=c2.x-c1.x; var dy=c2.y-c1.y; var
rSum=c1.radio+c2.radio;
return(dx*dx+dy*dy<=rSum*rSum);

Sección 11.2: ¿Están colisionando 2 rectángulos?

// objetos de rectángulo { x:, y:, ancho:, altura: } // devuelve


verdadero si los 2 rectángulos chocan // r1 y r2 son
rectángulos como se definió anteriormente

function RectsColliding(r1,r2){ return !


( r1.x>r2.x+r2.width ||
r1.x+r1.width<r2.x ||
r1.y>r2.y+r2.height ||
r1.y+r1.altura<r2.y

);
}

Sección 11.3: ¿Están chocando un círculo y un rectángulo?

// objeto rectángulo: { x:, y:, ancho:, altura: } // objeto círculo:


{ x:, y:, radio: } // devuelve verdadero si el rectángulo y el
círculo chocan

function RectCircleColliding(rect,circle){
var dx=Math.abs(circle.x-(rect.x+rect.width/2)); var
dy=Math.abs(circle.y-(rect.y+rect.height/2));

if( dx > circle.radius+rect.width/2 ){ return(false); } if( dy >


circle.radius+rect.height/2 ){ return(false); }

if( dx <= rect.width ){ return(true); } if( dy <=


rect.height ){ return(true); }

var dx=dx-rect.ancho; var


dy=dy-rect.height
return(dx*dx+dy*dy<=circle.radius*circle.radius);
}

Sección 11.4: ¿Se interceptan 2 segmentos de línea?


La función de este ejemplo devuelve verdadero si dos segmentos de línea se intersecan y falso en caso contrario.

El ejemplo está diseñado para el rendimiento y utiliza el cierre para contener variables de trabajo.

GoalKicker.com – Notas HTML5 Canvas para profesionales 131


Machine Translated by Google

// objeto de punto: {x:, y:} // p0 y p1


forman un segmento, p2 y p3 forman el segundo segmento
// Devuelve verdadero si los segmentos de líneas están interceptando var
lineSegmentsIntercept = (function(){ // funciona como singleton para que se pueda usar el cierre

var v1, v2, v3, cruz, u1, u2; // las variables de trabajo están cerradas por lo que no necesitan creación

// cada vez que se llama a la función. Esto da un importante


aumento de rendimiento.
v1 = {x : nulo, y : nulo}; // línea p0, p1 como vector v2 = {x : nulo, y :
nulo}; // línea p2, p3 como vector v3 = {x : nulo, y : nulo}; // la línea de p0
a p2 como vector

función líneaSegmentosIntersección (p0, p1, p2, p3) { v1.x = p1.x -


p0.x; // línea p0, p1 como vector v1.y = p1.y - p0.y; v2.x = p3.x -
p2.x; // línea p2, p3 como vector v2.y = p3.y - p2.y; if((cross = v1.x
* v2.y - v1.y * v2.x) === 0){ // cross prod 0 si las lineas son paralelas

devolver falso; // sin intercepción

} v3 = {x : p0.x - p2.x, y : p0.y - p2.y}; // la recta de p0 a p2 como vector u2 = (v1.x * v3.y - v1.y * v3.x) / cross; //
obtiene la unidad de distancia a lo largo de la línea p2 p3 // codifica el punto B si (u2 >= 0 && u2 <= 1){ // es la
intersección en la línea p2, p3 u1 = (v2.x * v3.y - v2.y * v3.x) / cruz; // obtener la unidad de distancia en la línea p0,
p1; // retorno del punto de código A (u1 >= 0 && u1 <= 1); // punto de código A final

// devuelve verdadero si está en línea, de lo contrario, es falso.

} devuelve falso; // sin intercepción; // final


del punto B del código

} return intercepción de segmentos de línea; // función de retorno con cierre para optimización. })();

ejemplo de uso

var p1 = {x: 100, y: 0}; // línea 1 var p2 = {x: 120, y:


200}; var p3 = {x: 0, y: 100}; // línea 2 var p4 = {x:
100, y: 120}; var areIntersepting = lineSegmentsIntercept
(p1, p2, p3, p4); // cierto

El ejemplo se modifica fácilmente para devolver el punto de intersección. Reemplace el código entre el punto de código A y el extremo
A con

si(u1 >= 0 && u1 <= 1){ devuelve


{ x : p0.x + v1.x * u1, y : p0.y
+ v1.y * u1,

};
}

O si desea obtener el punto de intercepción en las líneas, ignorando el inicio y el final de los segmentos de línea, reemplace el
código entre el punto de código B y el final B con

volver { x :
p2.x + v2.x * u2,

GoalKicker.com – Notas HTML5 Canvas para profesionales 132


Machine Translated by Google

y : p2.y + v2.y * u2 ,
};

Ambas modificaciones devolverán falso si no hay intersección o devolverán el punto de intersección como {x : xCoord, y : yCoord}

Sección 11.5: ¿Están chocando un segmento de línea y un círculo?


// [x0,y0] a [x1,y1] define un segmento de línea // [cx,cy] es el
centro del círculo, cr es el radio del círculo función
isCircleSegmentColliding(x0,y0,x1,y1,cx,cy,cr){

// calcular la distancia delta: punto de origen a inicio de línea var dx=cx-


x0; var dy=cy-y0;

// calcular la distancia delta: línea de principio a fin var


dxx=x1-x0; var dyy=y1-y0;

// Calcular la posición en la línea normalizada entre 0,00 y 1,00 // == producto


escalar dividido por las distancias de la línea delta al cuadrado var t=(dx*dxx+dy*dyy)/
(dxx*dxx+dyy*dyy);

// calcular el punto más cercano en la


línea var x=x0+dxx*t; var y=y0+dyy*t;

// sujetar los resultados a estar en el segmento if(t<0)


{x=x0;y=y0;} if(t>1){x=x1;y=y1;}

return( (cx-x)*(cx-x)+(cy-y)*(cy-y) < cr*cr );


}

Sección 11.6: ¿Están chocando el segmento de línea y el rectángulo?


// var rect={x:,y:,ancho:,alto:}; // línea
var={x1:,y1:,x2:,y2:};
// Obtener el punto de intersección del segmento de línea y el rectángulo (si lo hay)
función lineRectCollide(line,rect){

// p=punto inicial de línea, p2=punto final de línea var


p={x:línea.x1,y:línea.y1}; var p2={x:línea.x2,y:línea.y2};

// línea recta superior


var q={x:rect.x,y:rect.y}; var
q2={x:rect.x+rect.width,y:rect.y};
if(lineSegmentsCollide(p,p2,q,q2)){ devuelve verdadero; } // línea recta
derecha var q=q2; var q2={x:rect.x+rect.width,y:rect.y+rect.height};
if(lineSegmentsCollide(p,p2,q,q2)){ devuelve verdadero; } // línea recta
inferior var q=q2; var q2={x:rect.x,y:rect.y+rect.height};
if(lineSegmentsCollide(p,p2,q,q2)){ devuelve verdadero; } // línea recta
izquierda var q=q2;

GoalKicker.com – Notas HTML5 Canvas para profesionales 133


Machine Translated by Google

var q2={x:rect.x,y:rect.y};
if(lineSegmentsCollide(p,p2,q,q2)){ devuelve verdadero; }

// sin intersección con ninguno de los 4 lados rectos return(false);

// objeto de punto: {x:, y:} // p0 y p1


forman un segmento, p2 y p3 forman el segundo segmento // Obtiene el punto de
intersección de 2 segmentos de línea (si los hay)
// Atribución: http:// paulbourke.net/ geometry/ pointlineplane/ function
lineSegmentsCollide(p0,p1,p2,p3) {

var unknownA = (p3.x-p2.x) * (p0.y-p2.y) - (p3.y-p2.y) * (p0.x-p2.x); var unknownB = (p1.x-p0.x) * (p0.y-
p2.y) - (p1.y-p0.y) * (p0.x-p2.x); var denominador = (p3.y-p2.y) * (p1.x-p0.x) - (p3.x-p2.x) * (p1.y-p0.y);

// Prueba si coinciden // Si el
denominador y el numerador de ua y ub son 0, entonces las dos líneas son coincidentes.
//
if(unknownA==0 && unknownB==0 && denominador==0){return(null);}

// Prueba si son paralelas //


Si el denominador de las ecuaciones para ua y ub es 0 // entonces las dos rectas son
paralelas. if (denominador == 0) devuelve nulo;

// prueba si los segmentos de línea están colisionando


unknownA /= denominador; desconocidoB /=
denominador; var isIntersecting=(unknownA>=0 &&
unknownA<=1 && unknownB>=0 && unknownB<=1)

return(isIntersecting);
}

Sección 11.7: ¿Están colisionando 2 polígonos convexos?


Usa el teorema del eje de separación para determinar si 2 polígonos convexos se intersecan

LOS POLÍGONOS DEBEN SER CONVEXOS

Atribución: Markus Jarderot @ ¿Cómo verificar la intersección entre 2 rectángulos girados?

// los objetos poligonales son una matriz de vértices que forman el polígono var
SER CONVEXOS
polígono1=[{x:100,y:100},{x:150,y:150},{x:50,y:150},...]
// devuelve verdadero si los 2 polígonos chocan; // // LOS POLÍGONOS DEBEN

función convexPolygonsCollide(a, b){ var polígonos =


[a, b]; var minA, maxA, proyectada, i, i1, j, minB,
maxB;

for (i = 0; i < polígonos.longitud; i++) {

// para cada polígono, observe cada borde del polígono y determine si separa // las dos formas var polígono = polígonos[i];
for (i1 = 0; i1 < polígono.longitud; i1++) {

// toma 2 vértices para crear un borde var i2 = (i1 +


1) % polígono.longitud;

GoalKicker.com – Notas HTML5 Canvas para profesionales 134


Machine Translated by Google

var p1 = polígono[i1]; var p2 =


polígono[i2];

// encuentra la línea perpendicular a este borde var normal =


{ x: p2.y - p1.y, y: p1.x - p2.x };

minA = maxA = indefinido; // para


cada vértice de la primera forma, proyéctelo sobre la línea perpendicular a la
borde
// y realizar un seguimiento del mínimo y máximo de estos valores para (j =
0; j < a.length; j++) { proyectado = normal.x * a[j].x + normal.y * a[j]. y; if
(minA==indefinido || proyectado < minA) { minA = proyectado;

} if (maxA==indefinido || proyectado > maxA) { maxA =


proyectado;
}
}

// para cada vértice en la segunda forma, proyéctelo sobre la línea perpendicular a la


borde
// y realizar un seguimiento del mínimo y máximo de estos valores minB =
maxB = indefinido; for (j = 0; j < b.longitud; j++) { proyectada = normal.x *
b[j].x + normal.y * b[j].y; if (minB==indefinido || proyectado < minB) { minB
= proyectado;

} if (maxB==indefinido || proyectado > maxB) { maxB =


proyectado;
}
}

// si no hay superposición entre los proyectos, el borde que estamos viendo separa el
dos
// polígonos, y sabemos que no hay superposición if (maxA <
minB || maxB < minA) { return false;

}
}

} devuelve verdadero;
};

Sección 11.8: ¿Están colisionando 2 polígonos? (se


permiten polis cóncavos y convexos)
Prueba todos los lados del polígono en busca de intersecciones para determinar si 2 polígonos están colisionando.

// los objetos poligonales son una matriz de vértices que forman el polígono //
var polígono1=[{x:100,y:100},{x:150,y:150},{x:50,y:150},...];
// Los polígonos pueden ser tanto cóncavos como convexos //
devuelve verdadero si los 2 polígonos chocan

función polígonosCollide(p1,p2){
// convierte los vértices en puntos de línea var
lines1=verticesToLinePoints(p1); var
lineas2=verticesToLinePoints(p2); // prueba cada lado
poli1 frente a cada lado poli2 en busca de intersecciones para(i=0; i<lines1.length; i++){

GoalKicker.com – Notas HTML5 Canvas para profesionales 135


Machine Translated by Google

for(j=0; j<lines2.length; j++){ // prueba si los


lados se cruzan var p0=lines1[i][0]; var
p1=líneas1[i][1]; var p2=líneas2[j][0]; var
p3=líneas2[j][1]; // encontró una
intersección -- los polis colisionan
if(lineSegmentsCollide(p0,p1,p2,p3))
{return(true);}

}} // ninguno de los lados interseca


return(false);

} // ayudante: convierte los vértices en puntos de línea


function verticesToLinePoints(p){
// asegúrese de que los polis se cierren
automáticamente if(!(p[0].x==p[p.length-1].x && p[0].y==p[p.length-1].y )){
p.push({x:p[0].x,y:p[0].y});

} var lineas=[];
for(var i=1;i<p.longitud;i++){ var p1=p[i-1];
var p2=p[i]; lineas.push([ {x:p1.x,
y:p1.y}, {x:p2.x, y:p2.y}

]);

} retorno(líneas);

} // ayudante: probar intersecciones de línea //


objeto de punto: {x:, y:} // p0 y p1 forman un
segmento, p2 y p3 forman el segundo segmento // Obtener el punto de intersección de 2
segmentos de línea (si corresponde)
// Atribución: http:// paulbourke.net/ geometry/ pointlineplane/ function
lineSegmentsCollide(p0,p1,p2,p3) { var unknownA = (p3.x-p2.x) * (p0.y-p2.y) - (p3.y-p2.y) *
(p0.x-p2.x); var unknownB = (p1.x-p0.x) * (p0.y-p2.y) - (p1.y-p0.y) * (p0.x-p2.x); var denominador = (p3.y-
p2.y) * (p1.x-p0.x) - (p3.x-p2.x) * (p1.y-p0.y);

// Prueba si coinciden // Si el
denominador y el numerador de ua y ub son 0, entonces las dos líneas son coincidentes.
//
if(unknownA==0 && unknownB==0 && denominador==0){return(null);}

// Prueba si son paralelas //


Si el denominador de las ecuaciones para ua y ub es 0 // entonces las dos rectas son
paralelas. if (denominador == 0) devuelve nulo;

// prueba si los segmentos de línea están colisionando


unknownA /= denominador; desconocidoB /=
denominador; var isIntersecting=(unknownA>=0 &&
unknownA<=1 && unknownB>=0 && unknownB<=1)

return(isIntersecting);
}

Sección 11.9: ¿Está un punto X,Y dentro de un arco?


Comprueba si el punto [x,y] está dentro de un arco cerrado.

GoalKicker.com – Notas HTML5 Canvas para profesionales 136


Machine Translated by Google

var
arc={ cx:150, cy:150,
radio interno: 75, radio externo: 100, ángulo
inicial: 0, ángulo final: matemáticas.PI
}

function isPointInArc(x,y,arc){ var dx=x-arc.cx;


var dy=y-arco.cy; var dxy=dx*dx+dy*dy;
var rrOuter=arc.outerRadius*arc.outerRadius;
var rrInner=arc.innerRadius*arc.innerRadius;
if(dxy<rrInner || dxy>rrOuter){return(false);} var
angle=(Math.atan2(dy,dx)+PI2)%PI2; return(ángulo>=arc.startAngle
&& angle<=arc.endAngle);

Sección 11.10: ¿Está un punto X,Y dentro de una cuña?


Comprueba si el punto [x,y] está dentro de una cuña.

// objetos de cuña: {cx:,cy:,radius:,startAngle:,endAngle:} // var wedge={ // cx:150,


cy:150, // radio del punto central:100, // // startAngle: 0, ángulo final: matemáticas.PI // }

// Devuelve verdadero si el punto x,y está dentro de la cuña cerrada

función isPointInWedge(x,y,wedge){ var


PI2=Math.PI*2; var dx=x-cuña.cx; var dy=cuña-
y.cy; var rr=cuña.radio*cuña.radio;
if(dx*dx+dy*dy>rr){return(false);} var
angle=(Math.atan2(dy,dx)+PI2)%PI2;
return(ángulo>=cuña.ánguloInicio &&
ángulo<=cuña.ÁnguloFinal);

GoalKicker.com – Notas HTML5 Canvas para profesionales 137


Machine Translated by Google

Sección 11.11: ¿Está un punto X,Y dentro de un círculo?


Comprueba si un punto [x,y] está dentro de un círculo.

// objetos circulares: {cx:,cy:,radius:,startAngle:,endAngle:} // var circle={ //


cx:150, cy:150, // punto central // radio:100, // }

// Devuelve verdadero si el punto x,y está dentro del círculo

función isPointInCircle(x,y,circle){ var dx=x-


circle.cx; var dy=y-circle.cy;
return(dx*dx+dy*dy<circle.radius*circle.radius);

Sección 11.12: ¿Está un punto X,Y dentro de un rectángulo?


Comprueba si un punto [x,y] está dentro de un rectángulo.

// objetos rectangulares: {x:, y:, ancho:, alto: } // var rect={x:10,


y:15, ancho:25, alto:20}
// Devuelve verdadero si el punto x,y está dentro del rectángulo

función esPuntoEnRectángulo(x,y,rect){
return(x>rect.x && x<rect.x+rect.width && y>rect.y && y<rect.y+rect.height);
}

GoalKicker.com – Notas HTML5 Canvas para profesionales 138


Machine Translated by Google

Capítulo 12: Borrar la pantalla


Sección 12.1: Rectángulos
Puede usar el método clearRect para borrar cualquier sección rectangular del lienzo.

// Limpia todo el lienzo


ctx.clearRect(0, 0, canvas.width, canvas.height);

Nota: clearRect depende de la matriz de transformación.

Para lidiar con esto, es posible restablecer la matriz de transformación antes de borrar el lienzo.

ctx.guardar(); // Guardar el estado del contexto actual


ctx.setTransform(1, 0, 0, 1, 0, 0); // Restablecer la matriz de transformación
ctx.clearRect(0, 0, lienzo.ancho, lienzo.alto); // Limpiar el lienzo ctx.restore();
// Revertir el estado del contexto, incluida //
la matriz de transformación

Nota: ctx.save y ctx.restore solo son necesarios si desea mantener el estado de contexto 2D del lienzo. En algunas situaciones, guardar y
restaurar puede ser lento y, en general, debe evitarse si no es necesario.

Sección 12.2: Lienzo transparente con degradado


En lugar de usar clearRect, que hace que todos los píxeles sean transparentes, es posible que desee un fondo.

Para aclarar con un degradado

// crea el gradiente de fondo una vez var bgGrad


= ctx.createLinearGradient(0,0,0,canvas.height); bgGrad.addColorStop(0,"#0FF");
bgGrad.addColorStop(1,"#08F");

// Cada vez que necesite borrar el lienzo ctx.fillStyle =


bgGrad; ctx.fillRect(0,0,lienzo.ancho,lienzo.altura);

Esto es aproximadamente la mitad de rápido 0,008 ms que clearRect 0,004 ms, pero los 4 millones de segundos no deberían afectar negativamente a
ninguna animación en tiempo real. (Los tiempos variarán considerablemente según el dispositivo, la resolución, el navegador y la configuración del
navegador. Los tiempos son solo para comparación)

Sección 12.3: Limpiar el lienzo usando la operación compuesta


Limpia el lienzo usando la operación de composición. Esto borrará el lienzo independientemente de las transformaciones, pero no es tan rápido como
clearRect().

ctx.globalCompositeOperation = 'copiar';

todo lo que se dibuje a continuación borrará el contenido anterior.

GoalKicker.com – Notas HTML5 Canvas para profesionales 139


Machine Translated by Google

Sección 12.4: Datos de imagen sin procesar


Es posible escribir directamente en los datos de la imagen renderizada usando putImageData. Al crear nuevos datos de imagen y
luego asignarlos al lienzo, borrará toda la pantalla.

var imageData = ctx.createImageData(canvas.width, canvas.height);


ctx.putImageData(imageData, 0, 0);

Nota: putImageData no se ve afectado por ninguna transformación aplicada al contexto. Escribirá datos directamente en la región
de píxeles renderizada.

Sección 12.5: Formas complejas


Es posible borrar regiones con formas complejas cambiando la propiedad globalCompositeOperation .

// Todos los píxeles que se dibujen serán transparentes


ctx.globalCompositeOperation = 'destination-out';

// Limpiar una sección triangular


ctx.globalAlpha = 1; // asegurar que
el alfa sea 1 ctx.fillStyle =
'#000'; // asegúrese de que el estilo de relleno actual no tenga ninguna transparencia ctx.beginPath();
ctx.moveTo(10, 0); ctx.lineTo(0, 10); ctx.lineTo(20, 10); ctx.llenar();

// Empezar a dibujar normalmente de


nuevo ctx.globalCompositeOperation = 'source-over';

GoalKicker.com – Notas HTML5 Canvas para profesionales 140


Machine Translated by Google

Capítulo 13: Diseño receptivo


Sección 13.1: Creación de un lienzo de página completa con capacidad de respuesta

Código de inicio para crear y eliminar un lienzo de página completa que responde a eventos de cambio de tamaño a través de JavaScript.

lona var ; // Referencia de lienzo global


varctx ; // Referencia de contexto 2D global
// Crea un lienzo
función crearCanvas () {
const lienzo = documento.createElement("lienzo");
canvas.style.position = "absoluto"; // Establecer el estilo
lienzo.estilo.izquierda = "0px"; lienzo.estilo.top // Posición en la parte superior izquierda
= "0px";
lienzo.estilo.zIndex = 1;
documento.cuerpo.appendChild(lienzo); // Agregar al documento
lienzo de retorno ;
}
// Cambia el tamaño del lienzo. Creará un lienzo si no existe.
function sizeCanvas () { if (canvas
=== indefinido) { // Comprobar la referencia global del lienzo
lienzo = crearLienzo(); // Crea un nuevo elemento de lienzo
ctx = lienzo.getContext("2d"); // Obtener el contexto 2D
}
lienzo.ancho = ancho interior; lienzo.altura // Establecer la resolución del lienzo para llenar la página
= alturainterna;
}
// Elimina el lienzo
función removeCanvas () {
if (canvas !== indefinido) // Asegúrate de que haya algo que eliminar
{ removeEventListener("resize", sizeCanvas); // Eliminar evento de cambio de tamaño
documento.cuerpo.removeChild(lienzo); // Elimina el lienzo del DOM
ctx = indefinido; lienzo // Desreferenciar el contexto
= indefinido; // Desreferenciar el lienzo
}
}

// Agregar el oyente de cambio de tamaño


addEventListener("redimensionar", sizeCanvas);
// Llamar a sizeCanvas para crear y establecer la resolución del lienzo
tamañoCanvas();
// ctx y canvas ahora están disponibles para su uso.

Si ya no necesita el lienzo, puede eliminarlo llamando a removeCanvas()

Una demostración de este ejemplo en jsfiddle

Sección 13.2: Coordenadas del mouse después de cambiar el tamaño (o desplazarse)

Las aplicaciones de lienzo a menudo dependen en gran medida de la interacción del usuario con el mouse, pero cuando se cambia el tamaño de la ventana, el mouse

Es probable que las coordenadas de eventos en las que se basa el lienzo cambien porque el cambio de tamaño hace que el lienzo se desplace en un

posición diferente con respecto a la ventana. Por lo tanto, el diseño receptivo requiere que la posición de compensación del lienzo sea
se vuelve a calcular cuando se cambia el tamaño de la ventana, y también se vuelve a calcular cuando se desplaza la ventana.

Este código escucha eventos de cambio de tamaño de ventana y vuelve a calcular las compensaciones utilizadas en los controladores de eventos del mouse:

// variables que mantienen la posición actual de desplazamiento del lienzo


// relativo a la ventana

GoalKicker.com – Notas HTML5 Canvas para profesionales 141


Machine Translated by Google

var desplazamientoX,desplazamientoY;

// una función para recalcular las compensaciones del lienzo function


reOffset(){ var BB=canvas.getBoundingClientRect();
desplazamientoX=BB.izquierda; desplazamientoY=BB.superior;

// escucha los eventos de cambio de tamaño (y desplazamiento) de la


ventana y luego vuelve
window.onscroll=function(e){ a calcular
reOffset(); los desplazamientos del lienzo //
} ventana.onresize=function(e)
{ reOffset(); }

// ejemplo de uso de las compensaciones en una función de controlador


de mouse handleMouseUp(e){ // use offsetX & offsetY para obtener la
posición correcta del mouse mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY); // ...

Sección 13.3: Animaciones de lienzo sensibles sin eventos de


cambio de tamaño
Los eventos de cambio de tamaño de ventana pueden activarse en respuesta al movimiento del dispositivo de entrada del usuario. Cuando cambia el tamaño de un

lienzo, se borra automáticamente y se ve obligado a volver a representar el contenido. Para las animaciones, haces esto en cada cuadro a través de la función de bucle

principal llamada requestAnimationFrame , que hace todo lo posible para mantener la representación sincronizada con el hardware de la pantalla.

El problema con el evento de cambio de tamaño es que cuando se usa el mouse para cambiar el tamaño de la ventana, los eventos pueden activarse muchas veces más

rápido que la velocidad estándar de 60 fps del navegador. Cuando el evento de cambio de tamaño finaliza, el búfer de respaldo del lienzo se presenta al DOM sin estar

sincronizado con el dispositivo de visualización, lo que puede provocar cortes y otros efectos negativos. También hay una gran cantidad de asignación y liberación de memoria

innecesarias que pueden afectar aún más la animación cuando GC se limpia algún tiempo después.

Evento de cambio de tamaño eliminado

Una forma común de lidiar con las altas tasas de activación del evento de cambio de tamaño es eliminar el rebote del evento de cambio de tamaño.

// Asumir que el lienzo está dentro del


alcance addEventListener.("resize", debouncedResize );

// identificador de tiempo de espera


de rebote var debounceTimeoutHandle;

// El tiempo de rebote en ms (1/1000 de segundo) const


DEBOUNCE_TIME = 100;

// Función de cambio de
tamaño function debouncedResize ()
{ clearTimeout(debounceTimeoutHandle); // Borra cualquier evento de rebote pendiente

// Programar un cambio de tamaño del


lienzo debounceTimeoutHandle = setTimeout(resizeCanvas, DEBOUNCE_TIME);
}

GoalKicker.com – Notas HTML5 Canvas para profesionales 142


Machine Translated by Google
// función de cambio de tamaño del
lienzo function resizeCanvas () { ... cambiar el tamaño y redibujar ... }

El ejemplo anterior retrasa el cambio de tamaño del lienzo hasta 100 ms después del evento de cambio de tamaño. Si en ese momento se activan más eventos de

cambio de tamaño, se cancela el tiempo de espera de cambio de tamaño existente y se programa uno nuevo. Esto consume efectivamente la mayoría de los
eventos de cambio de tamaño.

Todavía tiene algunos problemas, el más notable es la demora entre cambiar el tamaño y ver el lienzo redimensionado. Reducir el tiempo de rebote mejora esto,

pero el cambio de tamaño aún no está sincronizado con el dispositivo de visualización. También tiene el bucle principal de animación renderizado en un lienzo

que no se ajusta.

¡Más código puede reducir los problemas! Más código también crea sus propios problemas nuevos.

Simple y el mejor cambio de tamaño

Después de haber intentado muchas formas diferentes de suavizar el cambio de tamaño del lienzo, desde lo absurdamente complejo, hasta simplemente

ignorar el problema (¿a quién le importa de todos modos?), recurrí a un amigo de confianza.

KISS es algo que la mayoría de los programadores deberían conocer ((Keep It Simple Stupid) El estúpido se refiere a mí por no haberlo pensado hace años. ) y

resulta que la mejor solución es la más simple de todas.

Simplemente cambie el tamaño del lienzo desde el bucle de animación principal. Se mantiene sincronizado con el dispositivo de visualización, no hay

representación innecesaria y la gestión de recursos es la mínima posible mientras se mantiene la velocidad de fotogramas completa.

Tampoco necesita agregar un evento de cambio de tamaño a la ventana ni ninguna función de cambio de tamaño adicional.

Agrega el cambio de tamaño donde normalmente limpiaría el lienzo al verificar si el tamaño del lienzo coincide con el tamaño de la ventana. Si no cambia el
tamaño.

// Supone que el elemento canvas está dentro del alcance como canvas

// Devolución de llamada de la función de bucle principal estándar desde requestAnimationFrame


function mainLoop(time) {

// Comprobar si el tamaño del lienzo coincide con el tamaño de la ventana if


(canvas.width !== innerWidth || canvas.height !== innerHeight) { canvas.width = innerWidth; // cambiar el
tamaño del lienzo canvas.height = innerHeight; 0,
// también
canvas.width,
borra el
canvas.height);
lienzo } else {//ctx.clearRect(0,
borrar si no se
cambia el tamaño

// Código de animación normal.

requestAnimationFrame(mainLoop);
}

GoalKicker.com – Notas HTML5 Canvas para profesionales 143


Machine Translated by Google

Capítulo 14: Sombras


Sección 14.1: Efecto adhesivo usando sombras
Este código agrega sombras cada vez mayores a una imagen para crear una versión de "pegatina" de la imagen.

Notas:

Además de ser un ImageObject, el argumento "img" también puede ser un elemento Canvas. Esto le permite etiquetar sus propios
dibujos personalizados. Si dibuja texto en el argumento Canvas, también puede etiquetarlo
texto.

Las imágenes totalmente opacas no tendrán efecto adhesivo porque el efecto se dibuja alrededor de grupos de píxeles opacos que
están bordeados por píxeles transparentes.

var lienzo=documento.createElement("lienzo"); var


ctx=canvas.getContext("2d");
documento.cuerpo.appendChild(lienzo);
canvas.style.background='azul marino';
canvas.style.border='1px rojo sólido;';

// ¡Siempre (!) espere a que sus imágenes se carguen por completo antes de intentar dibujarlas!
var img=nueva imagen(); img.onload=inicio; // pon tu img.src aquí... img.src='http://i.stack.imgur.com/
bXaB6.png'; inicio de función (){ ctx.drawImage(img,20,20); var sticker=stickerEffect(img,5);
ctx.drawImage(pegatina, 150,20);

function stickerEffect(img,crecer){ var


canvas1=document.createElement("canvas"); var
ctx1=canvas1.getContext("2d"); var
canvas2=document.createElement("canvas"); var
ctx2=canvas2.getContext("2d");
canvas1.width=canvas2.width=img.width+crecer*2;
lienzo1.altura=lienzo2.altura=img.altura+crecer*2;
ctx1.drawImage(img,crecer,crecer); ctx2.shadowColor='blanco';
ctx2.shadowBlur=2; for(var i=0;i<crecer;i++)
{ ctx2.drawImage(canvas1,0,0); ctx1.drawImage(lienzo2,0,0);

} ctx2.shadowColor='rgba(0,0,0,0)';
ctx2.drawImage(img,crecer,crecer);
retorno (lienzo2);

GoalKicker.com – Notas HTML5 Canvas para profesionales 144


Machine Translated by Google

Sección 14.2: Cómo detener más sombreado


Una vez que se activa el sombreado, se sombreará cada nuevo dibujo en el lienzo.

Desactive el sombreado adicional configurando context.shadowColor en un color transparente.

// comienza a sombrear
context.shadowColor='black';

... renderizar algunos dibujos sombreados ...

// desactivar el sombreado.
contexto.shadowColor='rgba(0,0,0,0)';

Sección 14.3: La sombra es computacionalmente costosa --


¡Guarde esa sombra en caché!
¡Advertencia! ¡Aplica las sombras con moderación!

La aplicación de sombreado es costosa y multiplicativamente costosa si aplica sombreado dentro de un bucle de animación.

En su lugar, guarde en caché una versión sombreada de su imagen (u otro dibujo):

Al comienzo de su aplicación, cree una versión sombreada de su imagen en un segundo lienzo solo en memoria: var memoryCanvas =
document.createElement('canvas') ...

Siempre que necesite la versión sombreada, dibuje esa imagen sombreada previamente desde el lienzo en memoria al lienzo
visible: context.drawImage(memoryCanvas,x,y)

var lienzo=documento.createElement("lienzo"); var


ctx=canvas.getContext("2d"); var cw=lienzo.ancho; var
ch=lienzo.altura; canvas.style.border='1px rojo sólido;';
documento.cuerpo.appendChild(lienzo);

// ¡Siempre (!) Use "img.onload" para darle tiempo a su imagen para que se cargue
Imagen(); img.onload=inicio;
por completo antes//de
Ponintentar
tu propio
dibujarla
img.src
enaquí
el lienzo.
img.src="http://
// var img=nueva
i.stack.imgur.com/hYFNe.png"; inicio de función (){ ctx.drawImage(img,0,20); var
cached=cacheShadowedImage(img,'black',5,3,3); para(var i=0;i<5;i++){

GoalKicker.com – Notas HTML5 Canvas para profesionales 145


Machine Translated by Google

ctx.drawImage(en caché,i*(img.ancho+10),80);
}
}

function cacheShadowedImage(img,shadowcolor,blur){ var


c=document.createElement('canvas'); var cctx=c.getContext('2d');
c.ancho=img.ancho+desenfoque*2+2; c.altura=img.altura+desenfoque*2+2;
cctx.shadowColor=color de sombra; cctx.shadowBlur=desenfoque;
cctx.drawImage(img,desenfoque+1,desenfoque+1); retorno(c);

Sección 14.4: Agregar profundidad visual con sombras


El uso tradicional del sombreado es dar a los dibujos bidimensionales la ilusión de profundidad 3D.

Este ejemplo muestra el mismo "botón" con y sin sombreado

var lienzo=documento.createElement("lienzo"); var


ctx=canvas.getContext("2d"); documento.cuerpo.appendChild(lienzo);

ctx.fillStyle='azul cielo';
ctx.strokeStyle='gris claro'; ctx.lineWidth=5;

// sin sombra
ctx.beginPath();
ctx.arc(60,60,30,0,Math.PI*2); ctx.closePath();
ctx.llenar(); ctx.stroke();

// con sombra
ctx.shadowColor='black';
ctx.shadowBlur=4;
ctx.shadowOffsetY=3;
ctx.beginPath();
ctx.arco(175,60,30,0,Matemáticas.PI*2);
ctx.closePath(); ctx.llenar(); ctx.stroke(); //
detener el sombreado
ctx.shadowColor='rgba(0,0,0,0)';

Sección 14.5: Sombras interiores


Canvas no tiene la sombra interna de CSS.

GoalKicker.com – Notas HTML5 Canvas para profesionales 146


Machine Translated by Google

El lienzo sombreará el exterior de una forma rellena.


El lienzo sombreará tanto el interior como el exterior de una forma trazada.

Pero es fácil crear sombras internas usando composición.

Trazos con una sombra interna

Para crear trazos con una sombra interna, use la composición de destino en la que hace que el contenido existente permanezca solo donde
el contenido existente se superpone con el contenido nuevo. El contenido existente que no se superpone con contenido nuevo se borra.

1. Traza una forma con una sombra. La sombra se extenderá tanto hacia afuera como hacia adentro desde el trazo. Debemos
deshacerse de la sombra exterior, dejando solo la sombra interior deseada.
2. Establezca la composición en destino-en que mantiene la sombra trazada existente solo donde se superpone con
cualquier nuevo dibujo.
3. Rellena la forma. Esto hace que el trazo y la sombra interior permanezcan mientras se borra la sombra exterior. ¡Bueno no
exactamente! Dado que un trazo está mitad dentro y mitad fuera de la forma rellena, la mitad exterior del trazo también se borrará.
La solución es duplicar context.lineWidth para que la mitad del trazo de tamaño doble aún esté dentro de la forma rellena.

var lienzo=documento.createElement("lienzo"); var


ctx=canvas.getContext("2d");
documento.cuerpo.appendChild(lienzo);

// dibujar una forma opaca -- aquí usamos un rectángulo redondeado


defineRoundedRect(30,30,100,75,10);

// establece el
sombreado ctx.shadowColor='black';
ctx.shadowBlur=10;

// trazar el rectángulo redondeado sombreado


ctx.lineWidth=4; ctx.stroke();

// configura la composición para borrar todo lo que esté fuera del trazo
ctx.globalCompositeOperation='destination-in'; ctx.llenar();

// limpiar siempre -- establecer la composición de nuevo a su valor


predeterminado ctx.globalCompositeOperation='source-over';

function defineRoundedRect(x,y,ancho,alto,radio) {
ctx.beginPath();
ctx.moveTo(x + radio, y);
ctx.lineTo(x + ancho - radio, y);
ctx.quadraticCurveTo(x + ancho, y, x + ancho, y + radio); ctx.lineTo(x + ancho,
y + alto - radio); ctx.quadraticCurveTo(x + ancho, y + alto, x + ancho - radio, y
+ alto); ctx.lineTo(x + radio, y + altura); ctx.quadraticCurveTo(x, y + altura, x, y + altura - radio);

GoalKicker.com – Notas HTML5 Canvas para profesionales 147


Machine Translated by Google

ctx.lineTo(x, y + radio);
ctx.quadraticCurveTo(x, y, x + radio, y); ctx.closePath();

}
Rellenos acariciados con una sombra interna

Para crear rellenos con una sombra interior, siga los pasos del 1 al 3 anteriores, pero utilice además la composición de destino sobre la que
hace que el nuevo contenido se dibuje debajo del contenido existente.

4. Establezca la composición en destino sobre lo que hace que el relleno se dibuje debajo de la sombra interior existente.
5. Desactive el sombreado configurando context.shadowColor en un color transparente.
6. Rellene la forma con el color deseado. La forma se rellenará debajo de la sombra interior existente.

var lienzo=documento.createElement("lienzo"); var


ctx=canvas.getContext("2d"); documento.cuerpo.appendChild(lienzo);

// dibujar una forma opaca -- aquí usamos un rectángulo redondeado


defineRoundedRect(30,30,100,75,10);

// establece el
sombreado ctx.shadowColor='black';
ctx.shadowBlur=10;

// trazar el rectángulo redondeado sombreado


ctx.lineWidth=4; ctx.stroke();

// dejar de sombrear
ctx.shadowColor='rgba(0,0,0,0)';

// configura la composición para borrar todo lo que esté fuera del trazo
ctx.globalCompositeOperation='destination-in'; ctx.llenar();

// establece la composición para borrar todo lo que esté fuera del trazo
ctx.globalCompositeOperation='destination-over'; ctx.fillStyle='oro'; ctx.llenar();

// limpiar siempre -- establecer la composición de nuevo a su valor


predeterminado ctx.globalCompositeOperation='source-over';

function defineRoundedRect(x,y,ancho,alto,radio) {
ctx.beginPath();
ctx.moveTo(x + radio, y); ctx.lineTo(x
+ ancho - radio, y); ctx.quadraticCurveTo(x +
ancho, y, x + ancho, y + radio); ctx.lineTo(x + ancho, y + alto - radio);
ctx.quadraticCurveTo(x + ancho, y + alto, x + ancho - radio, y + alto); ctx.lineTo(x +
radio, y + altura); ctx.quadraticCurveTo(x, y + altura, x, y + altura - radio); ctx.lineTo(x, y + radio);
ctx.quadraticCurveTo(x, y, x + radio, y);

GoalKicker.com – Notas HTML5 Canvas para profesionales 148


Machine Translated by Google

ctx.closePath();
}

Rellenos sin trazo con una sombra interna

Para dibujar una forma rellena con una sombra interna, pero sin trazo, puede dibujar el trazo fuera del lienzo y usar
shadowOffsetX para empujar la sombra de regreso al lienzo.

var lienzo=documento.createElement("lienzo"); var


ctx=canvas.getContext("2d"); documento.cuerpo.appendChild(lienzo);

// define una forma opaca -- aquí usamos un rectángulo redondeado


defineRoundedRect(30-500,30,100,75,10);

// establece el
sombreado ctx.shadowColor='black';
ctx.shadowBlur=10;
ctx.shadowOffsetX=500;

// trazar el rectángulo redondeado sombreado


ctx.lineWidth=4; ctx.stroke();

// dejar de sombrear
ctx.shadowColor='rgba(0,0,0,0)';

// redefinir una forma opaca -- aquí usamos un rectángulo redondeado


defineRoundedRect(30,30,100,75,10);

// configura la composición para borrar todo lo que esté fuera del trazo
ctx.globalCompositeOperation='destination-in'; ctx.llenar();

// establece la composición para borrar todo lo que esté fuera del trazo
ctx.globalCompositeOperation='destination-over'; ctx.fillStyle='oro'; ctx.llenar();

// limpiar siempre -- establecer la composición de nuevo a su valor


predeterminado ctx.globalCompositeOperation='source-over';

function defineRoundedRect(x,y,ancho,alto,radio) {
ctx.beginPath();
ctx.moveTo(x + radio, y); ctx.lineTo(x
+ ancho - radio, y); ctx.quadraticCurveTo(x +
ancho, y, x + ancho, y + radio); ctx.lineTo(x + ancho, y + alto - radio);
ctx.quadraticCurveTo(x + ancho, y + alto, x + ancho - radio, y + alto); ctx.lineTo(x +
radio, y + altura); ctx.quadraticCurveTo(x, y + altura, x, y + altura - radio); ctx.lineTo(x, y + radio);
ctx.quadraticCurveTo(x, y, x + radio, y); ctx.closePath();

GoalKicker.com – Notas HTML5 Canvas para profesionales 149


Machine Translated by Google

GoalKicker.com – Notas HTML5 Canvas para profesionales 150


Machine Translated by Google

Capítulo 15: Cuadros y diagramas


Sección 15.1: Gráfico circular con demostración

<!doctype html>
<html>

<cabeza>
<estilo> cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </
estilo> <guión> ventana.onload=(función(){

var lienzo = documento.getElementById("lienzo"); var ctx =


lienzo.getContext("2d"); ctx.lineWidth = 2; ctx.font = '14px
verde';

var PI2 = Math.PI * 2; var


myColor = ["Verde", "Rojo", "Azul"]; var misDatos
= [30, 60, 10]; var cx = 150; varcy = 150 ; radio
var = 100;

pieChart(misDatos, miColor);

function gráfico circular (datos, colores)


{ var total = 0; for (var i = 0; i <
data.length; i++) { total += data[i];

var barridos = []
for (var i = 0; i < datos.longitud; i++)
{ barridos.push(datos[i] / total * PI2);
}

var accumAngle = 0;
for (var i = 0; i < barridos.longitud; i++) {
drawWedge(accumAngle, accumAngle + barridos[i], colores[i], datos[i]); accumAngle
+= barridos[i];
}

GoalKicker.com – Notas HTML5 Canvas para profesionales 151


Machine Translated by Google

function dibujarWedge(startAngle, endAngle, fill, label) { // dibujar la cuña


ctx.beginPath(); ctx.mover a (cx, cy); ctx.arc(cx, cy, radio, startAngle, endAngle,
false); ctx.closePath(); ctx.fillStyle = relleno; ctx.strokeStyle = 'negro'; ctx.llenar();
ctx.stroke();

// dibujar la etiqueta var


midAngle = startAngle + (endAngle - startAngle) / 2; var labelRadius = radio * .65;
var x = cx + (labelRadius) * Math.cos(midAngle); var y = cy + (labelRadius) *
Math.sin(midAngle); ctx.fillStyle = 'blanco'; ctx.fillText(etiqueta, x, y);

}); // finaliza $(función(){}); </script> </


head> <cuerpo> <lienzo id="lienzo"
ancho=512 alto=512></lienzo> </
cuerpo> </html>

Sección 15.2: Línea con puntas de flecha

// Uso:
dibujarLíneaConFlechas(50,50,150,50,5,8,verdadero,verdadero);

// x0,y0: el punto inicial de la línea // x1,y1: el punto


final de la línea // ancho: la distancia que la punta
de la flecha se extiende perpendicularmente alejándose de la línea // altura: la distancia que la punta de la flecha se
extiende hacia atrás desde el punto final // arrowStart : dirección verdadera/ falsa para dibujar una punta de flecha
en el punto inicial de la línea // arrowEnd: dirección verdadera/ falsa para dibujar la punta de flecha en el punto final de la
línea

función dibujarLíneaConFlechas(x0,y0,x1,y1,aAncho,aLongitud,inicioflecha,finflecha){
var dx=x1-x0; var
dy=y1-y0; ángulo
var =Math.atan2(dy,dx); var
length=Math.sqrt(dx*dx+dy*dy); // ctx.translate(x0,y0);
ctx.rotate(ángulo); ctx.beginPath(); ctx. mover a
(0,0); ctx.lineTo(longitud,0);

GoalKicker.com – Notas HTML5 Canvas para profesionales 152


Machine Translated by Google

if(arrowStart)
{ ctx.moveTo(aLength,-aWidth);
ctx.lineTo(0,0); ctx.lineTo(aLongitud,aAncho);

} if(arrowEnd)
{ ctx.moveTo(longitud-aLength,-aWidth);
ctx.lineTo(longitud,0); ctx.lineTo(longitud-
aLongitud,aAncho);

} //
ctx.stroke();
ctx.setTransform(1,0,0,1,0,0);
}

Sección 15.3: Curva Bezier cúbica y cuadrática con puntas de flecha

// Uso: var
p0={x:50,y:100}; var
p1={x:100,y:0}; var
p2={x:200,y:200}; var
p3={x:300,y:100};

cubicCurveArrowHeads(p0, p1, p2, p3, 15, verdadero, verdadero);

cabezas de flecha de curva cuadrática (p0, p1, p2, 15, verdadero, verdadero);

// o use valores predeterminados verdaderos para ambos extremos con puntas


de flecha cubicCurveArrowHeads(p0, p1, p2, p3, 15);

Cabezas de flecha de curva cuadrática (p0, p1, p2, 15);

// dibuja la función bezier tanto cúbica como cuadrática


bezWithArrowheads(p0, p1, p2, p3, arrowLength, hasStartArrow, hasEndArrow) {
var x, y, norma, ex, ey; puntos de
función a Vec normalizado (p, pp) { var len;

norma.y = pp.x - px; norma.x


= -(pp.y - py); len =
Math.sqrt(norma.x * norma.x + norma.y * norma.y); norma.x /= len; norma.y /
= len; norma de devolución ;

GoalKicker.com – Notas HTML5 Canvas para profesionales 153


Machine Translated by Google

var ancho de flecha = largo de flecha / 2; norma


= {}; // el valor predeterminado es verdadero para
ambas flechas si no se incluyen los argumentos hasStartArrow = hasStartArrow ===
indefinido || hasStartArrow === nulo ? verdadero : tiene flecha de inicio; hasEndArrow = hasEndArrow === indefinido || hasEndArrow
=== nulo ? verdadero : hasEndArrow; ctx.beginPath(); ctx.moveTo(p0.x, p0.y); si (p3 === indefinido) {

ctx.quadraticCurveTo(p1.x, p1.y, p2.x, p2.y); ex = p2.x; // obtener


el punto final ey = p2.y; norma = puntosAVecNormalizado(p1,p2); }
else { ctx.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y) ex =
p3.x; // obtener el punto final ey = p3.y; norma =
puntosAVecNormalizado(p2,p3);

} if (hasEndArrow) { x =
ancho de flecha * norma.x + longitud de flecha * -norma.y; y = ancho de
flecha * norma.y + longitud de flecha * norma.x; ctx.moveTo(ex + x, ey
+ y); ctx.lineTo(ex, ey); x = ancho de flecha * -norma.x + largo de flecha
* -norma.y; y = ancho de flecha * -norma.y + largo de flecha * norma.x;
ctx.lineTo(ex + x, ey + y);

} if (hasStartArrow) { norma =
puntosParaNormalizarVec(p0,p1); x = ancho de flecha
* norma.x - longitud de flecha * -norma.y; y = ancho de flecha * norma.y
- longitud de flecha * norma.x; ctx.moveTo(p0.x + x, p0.y + y);
ctx.lineTo(p0.x, p0.y); x = ancho de flecha * -norma.x - largo de flecha *
-norma.y; y = ancho de flecha * -norm.y - largo de flecha * norm.x;
ctx.lineTo(p0.x + x, p0.y + y);

ctx.stroke();
}

function cubicCurveArrowHeads(p0, p1, p2, p3, arrowLength, hasStartArrow, hasEndArrow) { bezWithArrowheads(p0, p1, p2, p3,
arrowLength, hasStartArrow, hasEndArrow);

} function quadraticCurveArrowHeads(p0, p1, p2, arrowLength, hasStartArrow, hasEndArrow) { bezWithArrowheads(p0, p1, p2,
undefined, arrowLength, hasStartArrow, hasEndArrow);
}

Sección 15.4: Cuña


El código dibuja solo la cuña... el círculo dibujado aquí solo como perspectiva.

GoalKicker.com – Notas HTML5 Canvas para profesionales 154


Machine Translated by Google

// Uso var
cuña={ cx:150,
cy:150, radio:100,
startAngle:0,
endAngle:Math.PI*.65

drawWedge(cuña, 'azul cielo', 'gris', 4);

function dibujarWedge(w,fill,stroke,strokewidth){ ctx.beginPath();


ctx.moveTo(w.cx, w.cy); ctx.arc(w.cx, w.cy, w.radius,
w.startAngle, w.endAngle); ctx.closePath(); ctx.fillStyle=llenar;
ctx.llenar(); ctx.strokeStyle=trazo; ctx.lineWidth=ancho de trazo; ctx.stroke();

Sección 15.5: Arco con relleno y trazo

// Uso: var
arc={ cx:150,
cy:150, radiointerno:75,
radioexterior:100, ánguloinicial:-Math.PI/4,
ángulofinal:Math.PI
}

GoalKicker.com – Notas HTML5 Canvas para profesionales 155


Machine Translated by Google

dibujarArc(arco,'azul cielo','gris',4);

function dibujarArc(a,relleno,trazo,ancho del trazo){ ctx.beginPath();


ctx.arc(a.cx,a.cy,a.innerRadius,a.startAngle,a.endAngle);
ctx.arc(a.cx,a.cy,a.outerRadius,a.endAngle,a.startAngle,true); ctx.closePath();
ctx.fillStyle=llenar; ctx.strokeStyle=trazo; ctx.lineWidth=ancho de trazo ctx.fill(); ctx.stroke();

GoalKicker.com – Notas HTML5 Canvas para profesionales 156


Machine Translated by Google

Capítulo 16: Transformaciones


Sección 16.1: Girar una imagen o ruta alrededor de su punto central

Los pasos del 1 al 5 a continuación permiten mover cualquier imagen o forma de ruta a cualquier lugar del lienzo y rotarla en cualquier ángulo sin
cambiar ninguna de las coordenadas de punto originales de la imagen/forma de ruta.

1. Mueva el origen del lienzo [0,0] al punto central de la forma

context.translate( formaCentroX, formaCentroY );

2. Gire el lienzo en el ángulo deseado (en radianes)

context.rotate( radianAngle );

3. Mueva el origen del lienzo de nuevo a la esquina superior izquierda

context.translate( -shapeCenterX, -shapeCenterY );

4. Dibuje la imagen o la forma de la ruta usando sus coordenadas originales.

context.fillRect( formaX, formaY, ancho de forma, altura de forma );

5. ¡Siempre limpia! Vuelva a establecer el estado de transformación donde estaba antes del n.º 1

Paso #5, Opción #1: Deshacer todas las transformaciones en el orden inverso

// deshacer
#3 context.translate( formaCentroX, formaCentroY ); //
deshacer #2 context.rotate( -radianAngle ); // deshace #1
context.translate( -shapeCenterX, shapeCenterY );

Paso n.° 5, opción n.° 2: si el lienzo estaba en un estado sin transformar (el valor predeterminado) antes de comenzar el paso n.° 1, puede
deshacer los efectos de los pasos n.° 1 a 3 restableciendo todas las transformaciones a su estado predeterminado.

GoalKicker.com – Notas HTML5 Canvas para profesionales 157


Machine Translated by Google

// establece la transformación en el estado predeterminado (==no se aplica transformación)


context.setTransform(1,0,0,1,0,0)

Ejemplo de demostración de código:

// referencias de lienzo y estilo de lienzo var


canvas=document.createElement("canvas"); canvas.style.border='1px
rojo sólido'; documento.cuerpo.appendChild(lienzo); lienzo.ancho=378;
lienzo.altura=256; var ctx=canvas.getContext("2d"); ctx.fillStyle='verde';
ctx.globalAlpha=0.35;

// definir un rectángulo para rotar var


rect={ x:100, y:100, ancho:175, alto:50 };

// dibujar el rectángulo sin rotar ctx.fillRect( rect.x,


rect.y, rect.width, rect.height );

// dibujar el rectángulo girado 45 grados (==PI/ 4 radianes) ctx.translate( rect.x+rect.width/2,


rect.y+rect.height/2 ); ctx.rotate( Matemáticas.PI/4 ); ctx.translate( -rect.x-rect.width/2, -rect.y-
rect.height/2 ); ctx.fillRect( rect.x, rect.y, rect.ancho, rect.alto );

Sección 16.2: Dibujar muchas imágenes traducidas, escaladas


y rotadas rápidamente
Hay muchas situaciones en las que desea dibujar una imagen rotada, escalada y traducida. La rotación debe ocurrir alrededor del centro
de la imagen. Esta es la forma más rápida de hacerlo en el lienzo 2D. Estas funciones se adaptan bien a los juegos 2D en los que la expectativa
es generar unos pocos cientos, incluso hasta más de 1000 imágenes cada 60 de segundo. (dependiendo del hardware)

// asume que el contexto del lienzo está en ctx y en la función de alcance


drawImageRST(imagen, x, y, escala, rotación){
ctx.setTransform(escala, 0, 0, escala, x, y); // establece la escala y la traducción ctx.rotate(rotation);
ctx.drawImage(imagen, -imagen.ancho / 2, -imagen.altura / 2); // dibujar // agrega la rotación
la imagen compensada por la mitad de su
ancho y alto }

Una variante también puede incluir el valor alfa, que es útil para los sistemas de partículas.

function dibujarImagenRST_Alpha(imagen, x, y, escala, rotación, alfa){


ctx.setTransform(escala, 0, 0, escala, x, y); // establece la escala y la traducción ctx.rotate(rotation); ctx.globalAlpha =
alfa; ctx.drawImage(imagen, -imagen.ancho / 2, -imagen.altura / 2); // //dibujar
agregalala rotación
imagen compensada por la mitad de
su ancho y alto }

Es importante tener en cuenta que ambas funciones dejan el contexto del lienzo en un estado aleatorio. Aunque las funciones no se verán
afectadas, otras funciones pueden serlo. Cuando haya terminado de renderizar imágenes, es posible que deba restaurar la transformación
predeterminada

GoalKicker.com – Notas HTML5 Canvas para profesionales 158


Machine Translated by Google

ctx.setTransform(1, 0, 0, 1, 0, 0); // establecer la transformación de contexto de nuevo al valor predeterminado

Si usa la versión alfa (segundo ejemplo) y luego la versión estándar, deberá asegurarse de que se restablezca el estado alfa global

ctx.globalAlpha = 1;

Un ejemplo del uso de las funciones anteriores para renderizar algunas partículas y algunas imágenes.

// asume que las partículas contienen una matriz de partículas para (var i =
0; i < partículas.longitud; i++){
var p = partículas[i];
drawImageRST_Alpha(p.image, px, py, p.scale, p.rot, p.alpha); // no es necesario dejar
descansar el alfa en el ciclo

} // necesita restablecer el alfa ya que puede ser cualquier valor ctx.globalAlpha


= 1;

dibujarImagenRST(miImagen, 100, 100, 1, 0.5); // dibuja una imagen a 100.100 // no es necesario


restablecer la transformación drawImageRST(myImage, 200, 200, 1, -0.5); // dibuja una imagen a
200,200 ctx.setTransform(1,0,0,1,0,0);
// restablecer la transformación

Sección 16.3: Introducción a las Transformaciones


Las transformaciones alteran la posición de inicio de un punto dado al mover, rotar y escalar ese punto.

Traducción: Mueve un punto una distanciaX y una distanciaY.


Rotación: Rota un punto en un ángulo en radianes alrededor de su punto de rotación. El punto de rotación predeterminado
en Html Canvas es el origen superior izquierdo [x=0,y=0] del Canvas. Pero puede reposicionar el punto de rotación usando
traslaciones.

Escalado: Escala la posición de un punto por un scalingFactorX y scalingFactorY desde su punto de escala. El punto de
escala predeterminado en Html Canvas es el origen superior izquierdo [x=0,y=0] del Canvas. Pero puede reposicionar el punto
de escala usando traslaciones.

También puede hacer transformaciones menos comunes, como cortar (sesgar), configurando directamente la matriz de transformación del
lienzo usando context.transform.

Traduce (==mover) un punto con context.translate(75,25)

Rotar un punto con context.rotate(Math.PI/8)

GoalKicker.com – Notas HTML5 Canvas para profesionales 159


Machine Translated by Google

Escala un punto con context.scale(2,2)

Canvas en realidad logra transformaciones alterando todo el sistema de coordenadas del lienzo.

context.translate moverá el origen del lienzo [0,0] desde la esquina superior izquierda a una nueva
ubicación. context.rotate rotará todo el sistema de coordenadas del lienzo alrededor del origen.
context.scale escalará todo el sistema de coordenadas del lienzo alrededor del origen. Piense en esto como si
aumentara el tamaño de cada x,y en el lienzo: cada x*=escalaX y cada y*=escalaY.

Las transformaciones de lienzo son persistentes. Todos los dibujos nuevos continuarán transformándose hasta que restablezca la transformación del lienzo
a su estado predeterminado (==totalmente sin transformar). Puede restablecer los valores predeterminados con:

// restablecer las transformaciones de contexto al estado predeterminado (sin


transformar) context.setTransform(1,0,0,1,0,0);

Sección 16.4: Una matriz de transformación para rastrear


formas traducidas, rotadas y escaladas
Canvas le permite context.translate, context.rotate y context.scale para dibujar su forma en la posición y el tamaño que necesita.

Canvas en sí mismo utiliza una matriz de transformación para realizar un seguimiento eficiente de las transformaciones.

Puede cambiar la matriz de Canvas con context.transform .


Puede cambiar la matriz de Canvas con comandos individuales de traducción, rotación y escala .
Puede sobrescribir completamente la matriz de Canvas con context.setTransform, pero no puede
leer la matriz de transformación interna de Canvas, es de solo escritura.

¿Por qué utilizar una matriz de transformación?

Una matriz de transformación le permite agregar muchas traslaciones, rotaciones y escalas individuales en una sola matriz que se puede volver a
aplicar fácilmente.

GoalKicker.com – Notas HTML5 Canvas para profesionales 160


Machine Translated by Google

Durante animaciones complejas, puede aplicar docenas (o cientos) de transformaciones a una forma. Al usar una matriz de transformación,
puede (casi) volver a aplicar instantáneamente esas docenas de transformaciones con una sola línea de código.

Algunos ejemplos de usos:

Pruebe si el mouse está dentro de una forma que ha traducido, rotado y escalado

Hay un context.isPointInPath incorporado que prueba si un punto (por ejemplo, el mouse) está dentro de una forma de ruta, pero esta prueba
incorporada es muy lenta en comparación con las pruebas con una matriz.

Probar de manera eficiente si el mouse está dentro de una forma implica tomar la posición del mouse informada por el navegador y
transformarla de la misma manera que se transformó la forma. Luego puede aplicar pruebas de impacto como si la forma no se hubiera
transformado.

Vuelva a dibujar una forma que se ha traducido, girado y escalado extensamente.

En lugar de volver a aplicar transformaciones individuales con múltiples .translate, .rotate, .scale , puede aplicar todas las
transformaciones agregadas en una sola línea de código.

Formas de prueba de colisión que se han trasladado, girado y escalado

Puede usar geometría y trigonometría para calcular los puntos que componen formas transformadas, pero es más rápido usar una
matriz de transformación para calcular esos puntos.

Una matriz de transformación "clase"

Este código refleja los comandos nativos de transformación context.translate , context.rotate, context.scale .
A diferencia de la matriz de lienzo nativa, esta matriz es legible y reutilizable.

Métodos:

traducir, rotar, escalar , reflejar los comandos de transformación de contexto y permitirle introducir transformaciones
en la matriz. La matriz contiene eficientemente las transformaciones agregadas.

setContextTransform toma un contexto y establece la matriz de ese contexto igual a esta matriz de transformación. Esto vuelve a
aplicar de manera eficiente todas las transformaciones almacenadas en esta matriz al contexto.

resetContextTransform restablece la transformación del contexto a su estado predeterminado (==sin transformar).

getTransformedPoint toma un punto de coordenadas sin transformar y lo convierte en un punto transformado.

getScreenPoint toma un punto de coordenadas transformado y lo convierte en un punto sin transformar.

getMatrix devuelve las transformaciones agregadas en forma de matriz.

Código:

var TransformationMatrix=( función(){ // privado var


self; var m=[1,0,0,1,0,0]; var reset=función()
{ var m=[1,0,0,1, 0,0]; } var
multiplicar=función(mat){ var
m0=m[0]*mat[0]+m[2]*mat[1];

GoalKicker.com – Notas HTML5 Canvas para profesionales 161


Machine Translated by Google

var m1=m[1]*mat[0]+m[3]*mat[1]; var


m2=m[0]*mat[2]+m[2]*mat[3]; var
m3=m[1]*mat[2]+m[3]*mat[3]; var
m4=m[0]*mat[4]+m[2]*mat[5]+m[4]; var
m5=m[1]*mat[4]+m[3]*mat[5]+m[5];
m=[m0,m1,m2,m3,m4,m5];

} var screenPoint=function(transformedX,transformedY){ // invertir var d =1/


(m[0]*m[3]-m[1]*m[2]); im=[ m[3]*d, -m[1]*d, -m[2]*d, m[0]*d, d*(m[2]*m[5]-
m[3] *m[4]), d*(m[1]*m[4]-m[0]*m[5]) ]; // retorno del
punto({ x:transformadoX*im[0]+transformadoY*im[2]+im[4], y:transformadoX*im[1]+transformadoY*im[3]+im[5]

});

} var puntoTransformado=función(pantallaX,pantallaY)
{ return({ x:pantallaX*m[0] + pantallaY*m[2] + m[4],
y:pantallaX*m[1] + pantallaY*m[3] + m[5]

});

} // función
pública TransformationMatrix(){ self=this;

} // métodos compartidos
TransformationMatrix.prototype.translate=function(x,y){
var mat=[ 1, 0, 0, 1, x, y ]; multiplicar(estera);

};
TransformationMatrix.prototype.rotate=function(rAngle){
var c = Math.cos(rAngle); var s =
Math.sin(rAngle); var mat=[ c, s, -s,
c, 0, 0 ]; multiplicar(estera);

};
TransformationMatrix.prototype.scale=function(x,y){ var mat=[ x, 0, 0, y,
0, 0 ]; multiplicar(estera);

};
TransformationMatrix.prototype.skew=función(radianX,radianY){
var mat=[ 1, Math.tan(radianY), Math.tan(radianX), 1, 0, 0 ]; multiplicar(estera);

};
TransformationMatrix.prototype.reset=function(){ reset();

}
TransformationMatrix.prototype.setContextTransform=function(ctx)
{ ctx.setTransform(m[0],m[1],m[2],m[3],m[4],m[5]);
}
TransformationMatrix.prototype.resetContextTransform=function(ctx){ ctx.setTransform(1,0,0,1,0,0);

}
TransformationMatrix.prototype.getTransformedPoint=función(pantallaX,pantallaY){
return(puntoTransformado(pantallaX,pantallaY));
}
TransformationMatrix.prototype.getScreenPoint=función(transformadoX,transformadoY){
return(screenPoint(transformadoX,transformadoY));
}
TransformationMatrix.prototype.getMatrix=función(){

GoalKicker.com – Notas HTML5 Canvas para profesionales 162


Machine Translated by Google

var clon=[m[0],m[1],m[2],m[3],m[4],m[5]]; volver (clonar);

} // devuelve el retorno
público (TransformationMatrix); })();

Manifestación:

Esta demostración utiliza la Matriz de Transformación "Clase" anterior para:

Rastree (== guarde) la matriz de transformación de un rectángulo.

Vuelva a dibujar el rectángulo transformado sin utilizar los comandos de transformación de contexto.

Pruebe si el mouse ha hecho clic dentro del rectángulo transformado.

Código:

<!doctype html> <html>

<cabeza>
<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){

var canvas=document.getElementById("canvas"); var


ctx=canvas.getContext("2d"); var cw=lienzo.ancho; var
ch=lienzo.altura; function reOffset(){ var
BB=canvas.getBoundingClientRect(); desplazamientoX=BB.izquierda;
desplazamientoY=BB.superior;

} var
desplazamientoX,desplazamientoY;
reOffset(); ventana.onscroll=función(e){ reOffset(); }
ventana.onresize=function(e){ reOffset(); }

// Matriz de transformación "Clase"

var TransformationMatrix=( función(){ // privado var self;


var m=[1,0,0,1,0,0]; var reset=función(){ var
m=[1,0,0,1, 0,0]; } var multiplicar=función(mat)
{ var m0=m[0]*mat[0]+m[2]*mat[1]; var
m1=m[1]*mat[0]+ m[3]*mat[1]; var m2=m[0]*mat[2]+m[2]*mat[3];
var m3=m[1]*mat[2]+m[3] *mat[3]; var
m4=m[0]*mat[4]+m[2]*mat[5]+m[4]; var
m5=m[1]*mat[4]+m[3 ]*mat[5]+m[5]; m=[m0,m1,m2,m3,m4,m5];

} var screenPoint=function(transformedX,transformedY){ // invertir

GoalKicker.com – Notas HTML5 Canvas para profesionales 163


Machine Translated by Google

var d =1/(m[0]*m[3]-m[1]*m[2]); im=[ m[3]*d,


-m[1]*d, -m[2]*d, m[0]*d, d*(m[2]*m[5]-m[3] *m[4]), d*(m[1]*m[4]-m[0]*m[5])
];
// retorno
del
punto({ x:transformadoX*im[0]+transformadoY*im[2]+im[4],
y:transformadoX*im[1]+transformadoY*im[3]+im[5]
});

} var puntoTransformado=función(pantallaX,pantallaY)
{ return({ x:pantallaX*m[0] + pantallaY*m[2] + m[4],
y:pantallaX*m[1] + pantallaY*m[3] + m[5]

});

} // Matriz de
transformación de función pública () {
yo=esto;

} // métodos compartidos
TransformationMatrix.prototype.translate=function(x,y){
var mat=[ 1, 0, 0, 1, x, y ]; multiplicar(estera);

};
TransformationMatrix.prototype.rotate=function(rAngle){
var c = Math.cos(rAngle); var s =
Math.sin(rAngle); var mat=[ c, s, -s,
c, 0, 0 ]; multiplicar(estera);

};
TransformationMatrix.prototype.scale=function(x,y){ var mat=[ x, 0, 0, y,
0, 0 ]; multiplicar(estera);

};
TransformationMatrix.prototype.skew=función(radianX,radianY){
var mat=[ 1, Math.tan(radianY), Math.tan(radianX), 1, 0, 0 ]; multiplicar(estera);

};
TransformationMatrix.prototype.reset=function(){ reset();

}
TransformationMatrix.prototype.setContextTransform=function(ctx)
{ ctx.setTransform(m[0],m[1],m[2],m[3],m[4],m[5]);
}
TransformationMatrix.prototype.resetContextTransform=function(ctx){ ctx.setTransform(1,0,0,1,0,0);

}
TransformationMatrix.prototype.getTransformedPoint=función(pantallaX,pantallaY){
return(puntoTransformado(pantallaX,pantallaY));
}
TransformationMatrix.prototype.getScreenPoint=función(transformadoX,transformadoY){
return(screenPoint(transformadoX,transformadoY));
}
TransformationMatrix.prototype.getMatrix=function(){ var
clone=[m[0],m[1],m[2],m[3],m[4],m[5]]; volver (clonar);

} // devuelve el retorno
público (TransformationMatrix); })();

// La DEMO comienza aquí

GoalKicker.com – Notas HTML5 Canvas para profesionales 164


Machine Translated by Google
// crea un rect y agrega una matriz de transformación // para rastrear
sus traslaciones, rotaciones y escalas var rect={x:30,y:30,w:50,h:35,matrix:new
TransformationMatrix()};

// dibujar el rect no transformado en negro


ctx.strokeRect(rect.x, rect.y, rect.w, rect.h); // Demostración: etiqueta
ctx.font='11px arial'; ctx.fillText('Rect sin transformar',rect.x,rect.y-10);

// transforma el lienzo y dibuja el recto transformado en rojo ctx.translate(100,0);


ctx.escala(2,2); ctx.rotate(Matemáticas.PI/12); // dibujar el rect transformado
ctx.strokeStyle='red'; ctx.strokeRect(rect.x, rect.y, rect.w, rect.h); ctx.font='6px arial'; //
Demostración: label ctx.fillText('Mismo Rect: Traducido, rotado y
escalado',rect.x,rect.y-6); // restablecer el contexto al estado no transformado
ctx.setTransform(1,0,0,1,0,0);

// registrar las transformaciones en la matriz var m=rect.matrix;


m.translate(100,0); escala m(2,2); m.rotar(Matemáticas.PI/12);

// usa la matriz de transformación guardada de rect para reposicionar, //


ctx.strokeStyle='blue'; dibujarTransformadoRect(rect);
cambiar el tamaño y redibujar el rect

// Demostración: instrucciones
ctx.font='14px arial';
ctx.fillText('Demostración: haga clic dentro del rectángulo azul ',30,200);

// redibujar un rect basado en su función de matriz de transformación guardada


drawTransformedRect(r){
// establece la matriz de transformación de contexto usando la matriz guardada de rect
m.setContextTransform(ctx); // dibujar el rect (¡no se necesitan cambios de posición o tamaño!)
ctx.strokeRect( rx, ry, rw, rh ); // restablece la transformación de contexto al valor predeterminado (==sin
transformar); m.resetContextTransform(ctx);

// ¿Está el punto en el rectángulo transformado? función


isPointInTransformedRect(r,transformedX,transformedY){ var
p=r.matrix.getScreenPoint(transformedX,transformedY); varx =px; var y=py; return(x>rx
&& x<r.x+rw && y>ry && y<r.y+rh);

// escucha los eventos de mousedown


canvas.onmousedown=handleMouseDown; función
manejarRatónAbajo(e){
// decirle al navegador que estamos manejando este evento
e.preventDefault(); e.detener la propagación();

GoalKicker.com – Notas HTML5 Canvas para profesionales 165


Machine Translated by Google

// obtener la posición del ratón


mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY); // ¿Está el ratón
dentro del rect transformado?
if(isPointInTransformedRect(rect,mouseX,mouseY)){ alert('Hiciste clic en
el Rect transformado');
}
}

// Demostración: redibujar rect transformado sin utilizar // function


comandos de transformación
drawTransformedRect(r,color){ de contexto
var m=r.matrix; var
tl=m.getTransformedPoint(rx,ry); var tr=m.getTransformedPoint(r.x+rw,ry);
var br=m.getTransformedPoint(r.x+rw,r.y+rh); var
bl=m.getTransformedPoint(rx,r.y+rh); ctx.beginPath();
ctx.moveTo(tl.x,tl.y); ctx.lineTo(tr.x,tr.y); ctx.lineTo(br.x,br.y);
ctx.lineTo(bl.x,bl.y); ctx.closePath(); ctx.strokeStyle=color; ctx.stroke();

}); // fin de ventana.onload </script>


</head> <body> <canvas
id="canvas" width=512 height=250></
canvas> </body> </html>

GoalKicker.com – Notas HTML5 Canvas para profesionales 166


Machine Translated by Google

Capítulo 17: Composición


Sección 17.1: Dibujar detrás de formas existentes con
"destino sobre"
context.globalCompositeOperation = "destino sobre"

La composición de "destino sobre" coloca un nuevo dibujo debajo de los dibujos existentes.

context.drawImage(lluvia,0,0);
context.globalCompositeOperation='destino-sobre'; // soleado BAJO contexto lluvioso.drawImage
(soleado,0,0);

Sección 17.2: Borrar formas existentes con "destination-out"


context.globalCompositeOperation = "destino de salida"

La composición de "destino fuera" utiliza nuevas formas para borrar los dibujos existentes.

La nueva forma en realidad no se dibuja, solo se usa como un "cortador de galletas" para borrar los píxeles existentes.

context.drawImage(manzana,0,0);
context.globalCompositeOperation = 'destino de salida'; // marca de mordida borra
context.drawImage(marca de mordida, 100,40);

GoalKicker.com – Notas HTML5 Canvas para profesionales 167


Machine Translated by Google

Sección 17.3: Composición predeterminada: las formas nuevas se dibujan


sobre las formas existentes
context.globalCompositeOperation = "fuente sobre"

La composición "fuente sobre" [predeterminada] coloca todos los dibujos nuevos sobre cualquier dibujo existente.

context.globalCompositeOperation='fuente-sobre'; // el contexto por


defecto.drawImage(background,0,0); context.drawImage(paracaidista,0,0);

Sección 17.4: Recorte imágenes dentro de formas con "destino-en"


context.globalCompositeOperation = "destino de entrada"

La composición de "destino de entrada" recorta los dibujos existentes dentro de una nueva forma.

Nota: Cualquier parte del dibujo existente que quede fuera del nuevo dibujo se borrará.

context.drawImage(imagen,0,0);
context.globalCompositeOperation='destino de entrada'; // imagen recortada dentro de oval
context.drawImage(oval,0,0);

Sección 17.5: Recorte imágenes dentro de formas con "fuente de entrada"


context.globalCompositeOperation = "fuente de entrada";

GoalKicker.com – Notas HTML5 Canvas para profesionales 168


Machine Translated by Google

la composición source-in recorta nuevos dibujos dentro de una forma existente.

Nota: Cualquier parte del nuevo dibujo que quede fuera del dibujo existente se borrará.

context.drawImage(oval,0,0);
context.globalCompositeOperation='fuente-en'; // imagen recortada dentro de oval context.drawImage(imagen,0,0);

Sección 17.6: Sombras internas con "source-atop"


context.globalCompositeOperation = 'fuente encima'

la composición source-atop recorta una nueva imagen dentro de una forma existente.

// Rect lleno de oro


ctx.fillStyle='oro';
ctx.fillRect(100,100,100,75); // sombra
ctx.shadowColor='negro';
ctx.shadowBlur=10; // restringe el nuevo
dibujo para cubrir los píxeles existentes
ctx.globalCompositeOperation='source-atop'; // trazo sombreado //
"source-atop" recorta la sombra exterior no deseada
ctx.strokeRect(100,100,100,75); ctx.strokeRect(100,100,100,75);

Sección 17.7: Cambiar la opacidad con "globalAlpha"


context.globalAlpha=0.50

GoalKicker.com – Notas HTML5 Canvas para profesionales 169


Machine Translated by Google

Puede cambiar la opacidad de los dibujos nuevos configurando globalAlpha en un valor entre 0,00 (totalmente transparente) y 1,00 (totalmente opaco).

El globalAlpha predeterminado es 1.00 (totalmente opaco).

Los dibujos existentes no se ven afectados por globalAlpha.

// dibujar un rectángulo opaco


context.fillRect(10,10,50,50);

// cambiar alfa a 50% -- todos los dibujos nuevos tendrán 50% de opacidad
context.globalAlpha=0.50;

// dibujar un rectángulo semitransparente


context.fillRect(100,10,50,50);

Sección 17.8: Invertir o Negar imagen con "diferencia"


Renderice un rectángulo blanco sobre una imagen con la operación compuesta

ctx.globalCompositeOperation = 'diferencia';

La cantidad del efecto se puede controlar con la configuración alfa

// Representar la imagen
ctx.globalCompositeOperation='source-atop';
ctx.drawImage(imagen, 0, 0);

// establece la operación compuesta


ctx.globalCompositeOperation='difference'; ctx.fillStyle
= "blanco"; ctx.globalAlpha = alfa; // alfa 0 = sin efecto
1 = efecto total ctx.fillRect(0, 0, image.width, image.height);

Sección 17.9: Blanco y negro con "color"

Eliminar el color de una imagen a través de

ctx.globalCompositeOperation = 'color';

La cantidad del efecto se puede controlar con la configuración alfa

// Representar la imagen
ctx.globalCompositeOperation='source-atop';

GoalKicker.com – Notas HTML5 Canvas para profesionales 170


Machine Translated by Google

ctx.drawImage(imagen, 0, 0);

// establecer la operación compuesta


ctx.globalCompositeOperation='color'; ctx.fillStyle =
"blanco"; ctx.globalAlpha = alfa; // alfa 0 = sin efecto 1
= efecto total ctx.fillRect(0, 0, image.width, image.height);

Sección 17.10: Aumentar el contraste de color con "saturación"


Aumente el nivel de saturación de una imagen con

ctx.globalCompositeOperation = 'saturación';

La cantidad del efecto se puede controlar con la configuración alfa o la cantidad de saturación en la superposición de relleno

// Representar la imagen
ctx.globalCompositeOperation='source-atop';
ctx.drawImage(imagen, 0, 0);

// establece la operación compuesta


ctx.globalCompositeOperation ='saturation'; ctx.fillStyle = "rojo";
ctx.globalAlpha = alfa; // alfa 0 = sin efecto 1 = efecto total
ctx.fillRect(0, 0, image.width, image.height);

Sección 17.11: Sepia FX con "luminosidad"


Cree un efecto sepia coloreado con

ctx.globalCompositeOperation = 'luminosidad';

En este caso, el color sepia se representa primero en la imagen.

GoalKicker.com – Notas HTML5 Canvas para profesionales 171


Machine Translated by Google

La cantidad del efecto se puede controlar con la configuración alfa o la cantidad de saturación en la superposición de relleno

// Representar la imagen
ctx.globalCompositeOperation='source-atop'; ctx.fillStyle
= "#F80"; // el color del sepia FX ctx.fillRect(0, 0, image.width,
image.height);

// establecer la operación compuesta


ctx.globalCompositeOperation ='luminosidad';

ctx.globalAlpha = alfa; // alfa 0 = sin efecto 1 = efecto completo ctx.drawImage(imagen,


0, 0);

GoalKicker.com – Notas HTML5 Canvas para profesionales 172


Machine Translated by Google

Capítulo 18: Manipulación de


píxeles con "getImageData" y "putImageData"
Sección 18.1: Introducción a "context.getImageData"
Html5 Canvas le brinda la capacidad de buscar y cambiar el color de cualquier píxel en el lienzo.

Puede usar la manipulación de píxeles de Canvas para:

Cree un selector de color para una imagen o seleccione un color en una rueda de colores.

Cree filtros de imagen complejos como desenfoque y detección de bordes.

Vuelva a colorear cualquier parte de una imagen a nivel de píxel (si usa HSL, incluso puede volver a colorear una imagen mientras conserva la iluminación

y la saturación importantes para que el resultado no se vea como si alguien hubiera puesto pintura en la imagen).

Nota: Canvas ahora tiene Blend Compositing que también puede cambiar el color de una imagen en algunos casos.

"Elimine" el fondo alrededor de una persona/elemento en una imagen, cree una herramienta

de cubo de pintura para detectar y rellenar parte de una imagen (por ejemplo, cambiar el color de un pétalo de flor en el que el usuario hizo clic de

verde a amarillo).

Examine una imagen en busca de contenido (por ejemplo, reconocimiento facial).

Problemas comunes:

Por razones de seguridad, getImageData está deshabilitado si ha dibujado una imagen que se origina en un dominio diferente al de la propia página web.

getImageData es un método relativamente costoso porque crea una gran matriz de datos de píxeles y porque no utiliza la GPU para ayudar en sus

esfuerzos. Nota: Canvas ahora tiene una composición combinada que puede hacer algo de la misma manipulación de píxeles que hace getImageData .

Para las imágenes .png, es posible que getImageData no informe exactamente los mismos colores que en el archivo .png original porque el navegador

puede realizar la corrección gamma y la premultiplicación alfa al dibujar imágenes en el


lienzo.

Obtener colores de píxeles

Utilice getImageData para obtener los colores de píxeles de todo o parte del contenido de su lienzo.

El método getImageData devuelve un objeto imageData

El objeto imageData tiene una propiedad .data que contiene la información de color de los píxeles.

La propiedad de datos es un Uint8ClampedArray que contiene los datos de color rojo, verde, azul y alfa (opacidad) para todos los píxeles solicitados.

// determina qué píxeles recuperar (esto recupera todos los píxeles del lienzo) var x=0; var
y=0; var ancho=lienzo.ancho; var altura=lienzo.altura;

// Obtener el objeto imageData var


imageData = context.getImageData(x,y,width,height);

// Extraiga la matriz de datos de color de píxeles del objeto imageData var


pixelDataArray = imageData.data;

Puede obtener la posición de cualquier píxel [x, y] dentro de una matriz de datos como esta:

GoalKicker.com – Notas HTML5 Canvas para profesionales 173


Machine Translated by Google

// la posición de la matriz data[] para el píxel [x,y] var n = y *


canvas.width + x;

Y luego puede obtener los valores rojo, verde, azul y alfa de ese píxel de esta manera:

// la información RGBA para el píxel [x,y] var


red=data[n]; var verde=datos[n+1]; var
azul=datos[n+2]; var alfa=datos[n+3];

Una ilustración que muestra cómo se estructura la matriz de datos de píxeles

context.getImageData se ilustra a continuación para un lienzo pequeño de 2x3 píxeles:

GoalKicker.com – Notas HTML5 Canvas para profesionales 174


Machine Translated by Google

Créditos
Muchas gracias a todas las personas de Stack Overflow Documentation que ayudaron a proporcionar este contenido.
se pueden enviar más cambios a web@petercv.com para que se publique o actualice nuevo contenido

almcd Capitulo 2
bjanes Capítulo 12
ciego67 Capítulos 1, 2, 3, 4, 6, 7, 9, 10, 11, 12, 13, 15, 16 y 17
Marca Capítulos 1, 4, 9 y 12
KaiidoE Capítulos 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 13, 14, 15, 16, 17 y 18
mike c Capítulo 12
Ronen Ness Capítulo 12
Spencer Wieczorek Capítulo 1
usuario2314737 Capítulo 1

GoalKicker.com – Notas HTML5 Canvas para profesionales 175


Machine Translated by Google

También te puede interesar

También podría gustarte