HTML5 Canvas Notes Traducido
HTML5 Canvas Notes Traducido
Lienzo
Notas para 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
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
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
Sección 4.3: Recorte de imágenes usando el lienzo Sección .................................................... .................................................... ........... 36
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
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
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
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
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
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
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
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
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
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
Sección 15.3: Curva Bézier cúbica y cuadrática con puntas de flecha .................................................... ......................... 153
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
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
Sobre
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.
});
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.
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.
HTML
JavaScript
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); };
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
JavaScript
download_img = function(el) { //
obtener el URI de la imagen del objeto canvas
var imageURI = canvas.toDataURL("image/jpg"); el.href =
imageURI; };
Es un elemento Html5.
<!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(){
</
cuerpo> </html>
Imágenes,
Textos,
Líneas y Curvas.
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
Los dibujos se pueden combinar y colocar en cualquier parte del lienzo para que se puedan usar para crear:
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
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.
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.
} 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);
JavaScript
Resultado
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
(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 => {
};
}); //
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
switch(textAlign){ case
"right": x -= totalWidth;
descanso; caso "fin":
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;
} minSpaceSize = min;
maxSpaceSize = max;
renderType = LLENAR;
renderTextJustified(este, texto, x, y, 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 = {
},
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.
Rellenar y trazar la función de texto Rellenar o trazar texto y usar los mismos argumentos. MeasureJustifiedText devolverá el
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.
Argumentos de función
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
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á.
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á#";
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.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";
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);
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.
Código de ejemplo
} 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,
};
}); //
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;
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;
}
}
}
} // 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 ++){
} 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);
};
} // 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);
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.
Cómo utilizar
Consulte Texto justificado para obtener detalles sobre los argumentos. Los argumentos entre [ y ] son opcionales.
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.
fontSize Tamaño de la fuente. (Tenga en cuenta que solo use fuentes definidas en píxeles, por ejemplo, 14px arial)
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.
ejemplo de uso
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";
// 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.
Este ejemplo muestra cómo representar texto a lo largo de un arco. Incluye cómo puede agregar funcionalidad a
CanvasRenderingContext2D extendiendo su prototipo.
Representación de ejemplo
Código de ejemplo
(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
""
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
} }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);
}
Descripciones de funciones
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);
Devuelve un objeto que contiene varias métricas de tamaño para representar texto circular
Ejemplos de uso
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
textoEnCurva(texto,desplazamiento,x1,y1,x2,y2,x3,y3,x4,y4)
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
pos += anchos[i] / 2;
} ctx.restaurar();
}
La función de asistente de curvas está diseñada para aumentar el rendimiento de la búsqueda de puntos en el Bézier.
} 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;
} 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
}
Para dibujar texto en el lienzo, obtenga una referencia al lienzo y luego llame al método fillText en el contexto.
Además, hay un cuarto argumento opcional , que puede usar para especificar el ancho máximo de su texto en
píxeles En el siguiente ejemplo, el valor de 200 restringe el ancho máximo del texto a 200 px:
Resultado:
También puede dibujar texto sin relleno y, en su lugar, solo un contorno, utilizando el método strokeText :
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.
estilo de fuente
variante de fuente
font-weight
font-size / line-height font-
family
Por ejemplo:
ctx.font = "cursiva minúscula negrita 40px Helvetica, Arial, sans-serif"; ctx.fillText("Mi texto", 20, 50);
Resultado:
izquierda
centrar
extremo
Por ejemplo:
ctx.textAlign = "centro";
ctx.font=TamañoFuente+" "+CaraFuente;
ctx.textBaseline='superior';
' ';
} más
{ línea = línea de prueba;
}
} ctx.fillText(línea, x, y);
}
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 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
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);
} colocarPalabras();
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;
} y=(wordIndex>startingIndex)?y+lineHeight:y+1;
}
}
y=sy;
x++;
si(x>=cw){ok=falso;}
}
} retorno(x);
}
¡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.
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 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();
}
// 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);
// Uso:
dibujarPolígonoRegular(3,25,75,50,6,'gris','rojo',0);
dibujarPolígonoRegular(5,25,150,50,6,'gris','dorado',0);
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);
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
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;}
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.
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.
Usar
});
documento.cuerpo.appendChild(esto); // Agrega la imagen al DOM
};
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.
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.
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);
función scaleToFill(img){
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.
Argumentos:
imageObject es una imagen que se utilizará como patrón. La fuente de la imagen puede ser:
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:
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 (!)
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.
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(){
} patternImage.src='http://i.stack.imgur.com/K9EZl.png';
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.
Considere este ejemplo de Ruta que dibuja una línea negra de 1 píxel de [0,5] a [5,5]:
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)?
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);
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(){
ctx.beginPath();
ctx.moveTo(50,30);
ctx.lineTo(75,55);
ctx.lineTo(25,55);
ctx.lineTo(50,30);
ctx.lineWidth=2;
ctx.stroke();
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(){
ctx.beginPath();
ctx.moveTo(50,30);
ctx.lineTo(75,55);
ctx.lineTo(25,55);
ctx.lineTo(50,30);
ctx.fillStyle='azul';
ctx.llenar();
Limita cualquier dibujo futuro para que se muestre solo dentro de la Ruta actual.
<!doctype html>
<html> <título>
<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){
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.
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
comenzarPath
moveTo
línea a
arco
cuadráticaCurvaTo
bezierCurvaTo
arcto
rectificar
cerrarRuta
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)
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
línea a
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
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
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
Dibuja una curva cuadrática que comienza en la ubicación actual de la pluma hasta una coordenada final determinada. Otra coordenada de control
bezierCurveTo
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
arcto
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
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
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
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.
Dibuja un segmento de línea desde la ubicación actual del lápiz para coordinar [endX,endY]
<cabeza>
<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){
// 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();
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(){
// argumentos
var topVertexX=50; var
topVertexY=20; var vértice
derechoX=75; var
vérticederechoY=70; var
vérticeizquierdoX=25; var
vérticeizquierdoY=70;
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;.
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(){
// 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)
Para dibujar un círculo completo, puede hacer un ángulo final = un ángulo inicial + 360 grados (360 grados == Math.PI2).
<!doctype html>
<html> <título>
<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){
// 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)
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.
<!doctype html>
<html> <título>
<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){
// 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();
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.
<cabeza>
<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){
// 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();
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.
<!doctype html>
<html> <título>
<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){
// 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();
<!doctype html>
<html> <título>
<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){
// argumentos
var leftX=25; var
topY=25; ancho de
variable = 40; var
altura=25;
<cabeza>
<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){
// argumentos
var leftX=25; var
topY=25; ancho de
variable = 40; var
altura=25;
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.
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.
<!doctype html>
<html> <título>
<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){
// argumentos
var topVertexX=50; var
topVertexY=50; var vértice
derechoX=75; var
vérticederechoY=75; var
vérticeizquierdoX=25; var
vérticeizquierdoY=75;
ctx.stroke();
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]).
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(){
Esta segunda demostración omite incorrectamente beginPath en el segundo trazo. El resultado es que la "X" tiene incorrectamente
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(){
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(){
// lineCap: redondo
ctx.lineCap='redondo';
dibujarLínea(50,70,200,70);
// lineCap: cuadrado
ctx.lineCap='cuadrado';
dibujarLínea(50,100,200,100);
// 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);
<cabeza>
<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){
// lineJoin: ronda
ctx.lineJoin='ronda';
dibujarPolyline(50,80);
// lineJoin: bisel
ctx.lineJoin='bisel';
dibujarPolyline(50,130);
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 .
También puede especificar estas opciones de color (estas opciones son objetos creados por el contexto):
<!doctype html>
<html> <título>
<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){
} patternImage.src='https://dl.dropboxusercontent.com/u/139992952/stackoverflow/BooMu1.png';
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):
<!doctype html>
<html> <título>
<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){
} patternImage.src='http://i.stack.imgur.com/ixrWe.png';
<!doctype html>
<html> <título>
<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){
ctx.lineWidth=1;
dibujarPolyline(50,50);
ctx.lineWidth=5;
dibujarPolyline(50,100);
ctx.lineWidth=10;
dibujarPolyline(50,150);
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.
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.
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.
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.
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)';
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(){
// 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);
Luego, stroke() o fill() colorearán la ruta con los colores degradados del objeto.
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 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.
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.
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.
<!doctype html>
<html> <head> <style>
body{ background-
color:white; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){
</script> </
head>
<cuerpo>
<lienzo id="lienzo" ancho=400 alto=150></lienzo> </cuerpo> </html>
) 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...
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!!
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:
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.
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.
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.
gradiente=ctx.createRadialGradient(x1,y1,r1,x2,y2,r2);
degradado.addColorStop(0,'rojo'); degradado.addColorStop(1,'verde');
ctx.fillStyle=gradiente;
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. ...
<!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");
gradiente=ctx.createRadialGradient(x1,y1,r1,x2,y2,r2);
degradado.addColorStop(0,'rojo'); degradado.addColorStop(1,'verde');
ctx.fillStyle=gradiente;
El W3C emite las especificaciones oficiales recomendadas que utilizan los navegadores para crear el elemento HTML5 Canvas.
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).
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.
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.
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.
ctx.beginPath(); var
x = cx + radio * Math.cos(0); var y = cy -
razón * radio * Math.sin(0); ctx.lineTo(x,y);
ctx.closePath();
ctx.stroke();
}
// 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);
ctx.closePath();
ctx.stroke();
}
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);
while(!fin)
{ ctx.fillRect(x1, y1, 1, 1); // dibujar cada píxel como un rect
}
}
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};
La función
getPointOnCurve = función (posición, x1, y1, x2, y2, x3, y3, [x4, y4], [vec])
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 = {};
}
y4 = y3;
}
} 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;
// 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
px = x1; // establece los valores predeterminados en caso de que los máximos estén fuera del
rango py = y1;
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.
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
} puntos.push({x:Dx,y:Dy});
retorno(puntos);
}
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 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);
}
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);
}
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.
Uso
// 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();
}
}
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.
¡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 .
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);
} 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;
}
}
function ptsToRects(pts)
{ ctx.fillStyle='red'; var i=0;
requestAnimationFrame(animar);
función 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);
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){
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;
} 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++){
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);
}
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
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();
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.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: 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 ;
// ================================================ =============================================
// 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
: posición;
}
}
extremo B opcional */
} c = posición;
retPuntos[i++] = v1.x; // punto de inicio retPoints[i++] =
v1.y;
} 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; //================================================
=======
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
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();
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])
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
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;
} 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;
} 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;
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.
} dx=Dx-últimoX;
dy=Dy-últimoY;
totDist+=Math.sqrt(dx*dx+dy*dy);
return(parseInt(totDist));
}
});
}
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
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.
...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).
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.
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
Este código da la ilusión de movimiento al volver a dibujar continuamente un círculo en nuevas posiciones.
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);
Por lo general, guarda sus formas creando un objeto de "forma" de JavaScript que representa cada forma.
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.
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.
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.
} 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'} );
} }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 ) ;
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++) {
}
}
}
}
}
}
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:
} 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'} );
dibujarTodo();
} }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);
}
}
}
}
}
}
} ctx.closePath();
}
Este ejemplo anotado muestra cómo arrastrar imágenes alrededor del lienzo.
} 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=[];
}
}
}
}
}
}
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.
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.
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
Alternativamente, puede usar oncanplaythrough , que se activará cuando se haya cargado suficiente video para que pueda reproducirse hasta el final.
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
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);
ctx.closePath();
ctx.llenar();
ctx.globalAlpha = 1; // restaurar alfa
}
}
}
} // 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.
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
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).
} if(!canEncode())
{ devuelve indefinido;
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;
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;
} función addCluster()
{ webmData[videoDataPos].d.push({ i: ids.Cluster,d: [{ i: ids.Timecode, d:
Math.round(clusterTimecode)}]}); // Corregido error con Round clusterCounter = 0;
};
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(
} 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)
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));
descanso;
}
} devuelve fragmentos;
}
Encoder.prototype =
{ addFrame : function(frame){ if('canvas'
in frame){
marco = marco.lienzo;
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,
} })()
} 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
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.
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);
}
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
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á
verdadero si es completo. Esto no significa que la imagen se haya cargado, significa que tiene
cargado
Hubo un error
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
Al proporcionar el evento image.onerror = myImgErrorHandler , puede tomar las medidas adecuadas para evitar errores.
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.
<!doctype html>
<html> <head>
<style>
body{ background-
color:white; } #canvas{border:1px rojo
sólido; } </estilo> <guión>
ventana.onload=(función(){
// inicia la animación
requestAnimationFrame(animate);
ctx.fillStyle='#'+Math.floor(Math.random()*16777215).toString(16);
ctx.llenar();
Para ilustrar las ventajas de requestAnimationFrame, esta pregunta de stackoverflow tiene una demostración en vivo
¡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(){
// 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; }
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.
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.
"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:
Citar: https://github.com/danro/jquery-easing/blob/master/jquery.easing.js
volver -c * ((t=t/d-1)*t*t*t - 1) + b; },
facilidadEnSalidaCuarto: 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) {
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) {
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) {
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:
//
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);
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){
requestAnimationFrame(animar);
}
}
Código anotado:
<cabeza>
<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){
// inicia la animación
requestAnimationFrame(animate);
} // establece nextTime
nextTime=currentTime+duration;
</script> </
head>
<cuerpo>
<lienzo id="lienzo" ancho=512 alto=512></lienzo> </cuerpo> </html>
Código anotado:
<cabeza>
<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){
// inicia la animación
requestAnimationFrame(animate);
// 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);
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:
<cabeza>
<estilo>
cuerpo{ color de fondo: marfil; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){
} var
desplazamientoX,desplazamientoY;
reOffset(); ventana.onscroll=función(e){ reOffset(); }
ventana.onresize=function(e){ reOffset(); }
canvas.onmousemove=function(e){handleMouseMove(e);}
function dibujar(){ //
¿No hay puntos adicionales? Solicite otro marco y devuelva var length=points.length;
if(longitud==últimaLongitud){requestAnimationFrame(dibujar);return;}
} ctx.stroke();
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
// 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;
Código de ejemplo:
);
}
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));
El ejemplo está diseñado para el rendimiento y utiliza el cierre para contener variables de trabajo.
var v1, v2, v3, cruz, u1, u2; // las variables de trabajo están cerradas por lo que no necesitan creació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
} return intercepción de segmentos de línea; // función de retorno con cierre para optimización. })();
ejemplo de uso
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
};
}
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,
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}
var q2={x:rect.x,y:rect.y};
if(lineSegmentsCollide(p,p2,q,q2)){ devuelve verdadero; }
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);}
return(isIntersecting);
}
// 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
// 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++) {
// 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;
};
// 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++){
} 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);
// 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);}
return(isIntersecting);
}
var
arc={ cx:150, cy:150,
radio interno: 75, radio externo: 100, ángulo
inicial: 0, ángulo final: matemáticas.PI
}
función esPuntoEnRectángulo(x,y,rect){
return(x>rect.x && x<rect.x+rect.width && y>rect.y && y<rect.y+rect.height);
}
Para lidiar con esto, es posible restablecer la matriz de transformación antes de borrar el lienzo.
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.
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)
ctx.globalCompositeOperation = 'copiar';
Nota: putImageData no se ve afectado por ninguna transformación aplicada al contexto. Escribirá datos directamente en la región
de píxeles renderizada.
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.
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:
var desplazamientoX,desplazamientoY;
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.
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.
// Función de cambio de
tamaño function debouncedResize ()
{ clearTimeout(debounceTimeoutHandle); // Borra cualquier evento de rebote pendiente
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.
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
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
requestAnimationFrame(mainLoop);
}
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.
// ¡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);
} ctx2.shadowColor='rgba(0,0,0,0)';
ctx2.drawImage(img,crecer,crecer);
retorno (lienzo2);
// comienza a sombrear
context.shadowColor='black';
// desactivar el sombreado.
contexto.shadowColor='rgba(0,0,0,0)';
La aplicación de sombreado es costosa y multiplicativamente costosa si aplica sombreado dentro de un bucle de animación.
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)
// ¡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++){
ctx.drawImage(en caché,i*(img.ancho+10),80);
}
}
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)';
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.
// establece el
sombreado ctx.shadowColor='black';
ctx.shadowBlur=10;
// configura la composición para borrar todo lo que esté fuera del trazo
ctx.globalCompositeOperation='destination-in'; ctx.llenar();
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();
}
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.
// establece el
sombreado ctx.shadowColor='black';
ctx.shadowBlur=10;
// 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();
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();
}
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.
// establece el
sombreado ctx.shadowColor='black';
ctx.shadowBlur=10;
ctx.shadowOffsetX=500;
// 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();
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();
<!doctype html>
<html>
<cabeza>
<estilo> cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </
estilo> <guión> ventana.onload=(función(){
pieChart(misDatos, miColor);
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];
}
// Uso:
dibujarLíneaConFlechas(50,50,150,50,5,8,verdadero,verdadero);
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);
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);
}
// 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};
cabezas de flecha de curva cuadrática (p0, p1, p2, 15, verdadero, verdadero);
} 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);
}
// Uso var
cuña={ cx:150,
cy:150, radio:100,
startAngle:0,
endAngle:Math.PI*.65
// Uso: var
arc={ cx:150,
cy:150, radiointerno:75,
radioexterior:100, ánguloinicial:-Math.PI/4,
ángulofinal:Math.PI
}
dibujarArc(arco,'azul cielo','gris',4);
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.
context.rotate( radianAngle );
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.
Una variante también puede incluir el valor alfa, que es útil para los sistemas de partículas.
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
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
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.
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:
Canvas en sí mismo utiliza una matriz de transformación para realizar un seguimiento eficiente de las transformaciones.
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.
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.
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.
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.
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.
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.
Código:
});
} 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(){
} // devuelve el retorno
público (TransformationMatrix); })();
Manifestación:
Vuelva a dibujar el rectángulo transformado sin utilizar los comandos de transformación de contexto.
Código:
<cabeza>
<estilo>
cuerpo{ color de fondo:blanco; }
#canvas{border:1px rojo sólido; } </estilo>
<guión> ventana.onload=(función(){
} var
desplazamientoX,desplazamientoY;
reOffset(); ventana.onscroll=función(e){ reOffset(); }
ventana.onresize=function(e){ reOffset(); }
} 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); })();
// Demostración: instrucciones
ctx.font='14px arial';
ctx.fillText('Demostración: haga clic dentro del rectángulo azul ',30,200);
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);
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);
La composición "fuente sobre" [predeterminada] coloca todos los dibujos nuevos sobre cualquier dibujo existente.
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);
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);
la composición source-atop recorta una nueva imagen dentro de una forma existente.
Puede cambiar la opacidad de los dibujos nuevos configurando globalAlpha en un valor entre 0,00 (totalmente transparente) y 1,00 (totalmente opaco).
// cambiar alfa a 50% -- todos los dibujos nuevos tendrán 50% de opacidad
context.globalAlpha=0.50;
ctx.globalCompositeOperation = 'diferencia';
// Representar la imagen
ctx.globalCompositeOperation='source-atop';
ctx.drawImage(imagen, 0, 0);
ctx.globalCompositeOperation = 'color';
// Representar la imagen
ctx.globalCompositeOperation='source-atop';
ctx.drawImage(imagen, 0, 0);
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);
ctx.globalCompositeOperation = 'luminosidad';
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);
Cree un selector de color para una imagen o seleccione un color en una rueda de colores.
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).
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
Utilice getImageData para obtener los colores de píxeles de todo o parte del contenido de su lienzo.
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;
Puede obtener la posición de cualquier píxel [x, y] dentro de una matriz de datos como esta:
Y luego puede obtener los valores rojo, verde, azul y alfa de ese píxel de esta manera:
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 [email protected] 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