Html5 Canvas Es
Html5 Canvas Es
#html5-
canvas
Tabla de contenido
Acerca de 1
Examples 2
Hola Mundo 4
Girar 6
Capítulo 2: Animación 9
Examples 9
Examples 22
Sintaxis 34
Observaciones 34
Examples 34
Rectángulos 34
Formas complejas 35
Capítulo 5: Caminos 36
Examples 36
Elipse 36
Examples 39
¿Chocan 2 círculos? 39
¿Chocan 2 rectángulos? 39
Capítulo 7: Compositing 48
Examples 48
Dibuja detrás de las formas existentes con "destino sobre" 48
Composición predeterminada: las nuevas formas se dibujan sobre las formas existentes 49
Examples 55
Examples 59
Cuña 61
Examples 65
El lienzo retenido 65
Observaciones 69
Examples 70
Mostrando 74
Resumen 75
Examples 82
Introducción a "context.getImageData" 82
Examples 85
Ejemplo de uso 94
La funcion dividida 95
Ejemplo de función 98
La función 101
Examples 104
Examples 111
Estrellas 111
Sintaxis 115
Examples 115
Examples 156
El ejemplo 171
Examples 182
Una matriz de transformación para realizar un seguimiento de las formas traducidas, rotada 186
Creditos 193
Acerca de
You can share this PDF with anyone you feel could benefit from it, downloaded the latest version
from: html5-canvas
It is an unofficial and free html5-canvas ebook created for educational purposes. All the content is
extracted from Stack Overflow Documentation, which is written by many hardworking individuals at
Stack Overflow. It is neither affiliated with Stack Overflow nor official html5-canvas.
The content is released under Creative Commons BY-SA, and the list of contributors to each
chapter are provided in the credits section at the end of this book. Images may be copyright of
their respective owners unless otherwise specified. All trademarks and registered trademarks are
the property of their respective company owners.
Use the content presented in this book at your own risk; it is not guaranteed to be correct nor
accurate, please send your feedback and corrections to [email protected]
https://riptutorial.com/es/home 1
Capítulo 1: Empezando con html5-canvas
Examples
Cómo agregar el elemento de lienzo de HTML5 a una página web
Html5-Canvas ...
• Es un elemento html5.
• Es compatible con la mayoría de los navegadores modernos (Internet Explorer 9+).
• Es un elemento visible que es transparente por defecto.
• Tiene un ancho predeterminado de 300 px y una altura predeterminada de 150 px.
• Requiere JavaScript porque todo el contenido debe agregarse programáticamente al Lienzo.
Ejemplo: cree un elemento Html5-Canvas utilizando tanto el marcado Html5 como JavaScript:
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvasHtml5{border:1px solid red; }
#canvasJavascript{border:1px solid blue; }
</style>
<script>
window.onload=(function(){
</body>
</html>
El tamaño de un lienzo es el área que ocupa en la página y está definido por las propiedades de
ancho y alto de CSS.
canvas {
width : 1000px;
height : 1000px;
}
https://riptutorial.com/es/home 2
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
por defecto a 300 por 150 píxeles.
El siguiente lienzo utilizará el tamaño de CSS anterior, pero como no se especifica el width y el
height la resolución será de 300 por 150.
<canvas id="my-canvas"></canvas>
Esto resultará en que cada píxel se estire de manera desigual. El aspecto del píxel es 1: 2.
Cuando el lienzo se estire, el navegador utilizará filtrado bilineal. Esto tiene un efecto de
desenfoque de píxeles que se estiran.
Para obtener los mejores resultados al utilizar el lienzo, asegúrese de que la resolución del lienzo
coincida con el tamaño de visualización.
Siguiendo el estilo CSS anterior para que coincida con el tamaño de visualización, agregue el
lienzo con el width y la height establecidos en el mismo número de píxeles que define el estilo.
Muchas veces, cuando trabaje con el lienzo, necesitará tener un lienzo para contener algunos
datos de píxeles internos. Es fácil crear un lienzo fuera de la pantalla, obtener un contexto 2D. Un
lienzo fuera de la pantalla también utilizará el hardware de gráficos disponible para renderizar.
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.
https://riptutorial.com/es/home 3
}
var myCanvas = createCanvasCTX(256,256); // create a small canvas 256 by 256 pixels
myCanvas.ctx.fillStyle = "blue";
myCanvas.ctx.fillRect(0,0,256,256);
Este ejemplo mostrará cómo obtener la posición del mouse en relación con el lienzo, de modo
que (0,0) será la esquina superior izquierda del HTML5 Canvas. e.clientX y e.clientY obtendrán
las posiciones del mouse en relación con la parte superior del documento. Para cambiar esto y
basarse en la parte superior del lienzo, restamos las posiciones left y right del canvas del cliente
X e Y.
canvas.addEventListener("mousemove", function(e) {
var cRect = canvas.getBoundingClientRect(); // Gets CSS pos, and width/height
var canvasX = Math.round(e.clientX - cRect.left); // Subtract the 'left' of the canvas
var canvasY = Math.round(e.clientY - cRect.top); // from the X/Y positions to make
ctx.clearRect(0, 0, canvas.width, canvas.height); // (0,0) the top left of the canvas
ctx.fillText("X: "+canvasX+", Y: "+canvasY, 10, 20);
});
Ejemplo ejecutable
El uso de Math.round se debe a garantizar que las posiciones x,y sean enteros, ya que el
rectángulo delimitador del lienzo puede no tener posiciones enteras.
Hola Mundo
HTML
Javascript
Resultado
https://riptutorial.com/es/home 4
Un índice de capacidades y usos de lienzo de HTML5
• Imágenes ,
• Textos ,
• Líneas y curvas .
• ancho de trazo ,
• color de trazo ,
• color de relleno de forma ,
• opacidad
• a la sombra
• degradados lineales y degradados radiales ,
• cara de la fuente ,
• tamaño de letra ,
• alineación del texto ,
• el texto se puede trazar, rellenar o tanto el trazo como el relleno ,
• redimensionamiento de imagen ,
• recorte de imágenes ,
• compositing
Canvas le permite manipular los colores de las imágenes de los componentes Rojo, Verde, Azul y
Alfa. Esto permite que el lienzo manipule imágenes con resultados similares a Photoshop.
https://riptutorial.com/es/home 5
• Vuelva a colorear cualquier parte de una imagen a nivel de píxeles (si usa HSL, puede
incluso cambiar el color de una imagen mientras conserva la Iluminación y la Saturación
importantes para que el resultado no se vea como si alguien hubiera pintado la imagen),
• "Knockout" el fondo alrededor de una persona / elemento en una imagen,
• Detecte y llene una parte de una imagen de la inundación (por ejemplo, cambie el color de
un pétalo de flor con clic en el usuario de verde a amarillo, ¡solo ese pétalo con clic!),
• Hacer distorsión de perspectiva (por ejemplo, envolver una imagen alrededor de la curva de
una taza),
• Examinar una imagen para el contenido (por ejemplo, reconocimiento facial),
• Responda 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 (Sobel Edge Detection),
• Combina imágenes. Si la querida abuela Sue no pudo asistir a la reunión familiar,
simplemente "photoshop" ella en la imagen de la reunión. No me gusta el primo Phil, solo "le
saca el photoshop,
• Reproduce un video / Toma un fotograma de un video,
• Exportar el contenido del lienzo como .jpg | .png imagen (incluso puede recortar o anotar
opcionalmente la imagen y exportar el resultado como una nueva imagen),
Acerca de mover y editar dibujos de lienzo (por ejemplo, para crear un juego de acción):
• Después de dibujar algo en el lienzo, ese dibujo existente no se puede mover ni editar. Vale
la pena aclarar esta idea errónea de que los dibujos de lienzo son móviles: ¡ los dibujos de
lienzo existentes no se pueden editar ni mover!
• Lienzo 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 dibujando algo rápida y repetidamente y luego
volviéndolo a dibujar en una nueva posición. Al igual que la televisión, este dibujo constante
le da al ojo la ilusión de movimiento.
Girar
HTML
Javascript
https://riptutorial.com/es/home 6
var ox = canvas.width / 2;
var oy = canvas.height / 2;
ctx.font = "42px serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "#FFF";
ctx.fillText("Hello World", ox, oy);
rotate_ctx = function() {
// translate so that the origin is now (ox, oy) the center of the canvas
ctx.translate(ox, oy);
// convert degrees to radians with radians = (Math.PI/180)*degrees.
ctx.rotate((Math.PI / 180) * 15);
ctx.fillText("Hello World", 0, 0);
// translate back
ctx.translate(-ox, -oy);
};
Aquí dibujamos un lienzo y adjuntamos el URI de datos del lienzo al enlace "Descargar en
myImage.jpg".
HTML
Javascript
download_img = function(el) {
// get image URI from canvas object
var imageURI = canvas.toDataURL("image/jpg");
https://riptutorial.com/es/home 7
el.href = imageURI;
};
https://riptutorial.com/es/home 8
Capítulo 2: Animación
Examples
Animación simple con contexto 2D y requestAnimationFrame
Este ejemplo le mostrará cómo crear una animación simple utilizando el lienzo y el contexto 2D.
Se supone que sabe cómo crear y agregar un lienzo al DOM y obtener el contexto
ctx.font = Math.floor(canvas.height * 0.8) + "px arial"; // size the font to 80% of canvas
height
var textWidth = ctx.measureText(textToDisplay).width; // get the text width
var totalTextSize = (canvas.width + textHorMargin * 2 + textWidth);
ctx.textBaseline = "middle"; // not put the text in the vertical center
ctx.textAlign = "left"; // align to the left
var textX = canvas.width + 8; // start with the text off screen to the right
var textOffset = 0; // how far the text has moved
var startTime;
// this function is call once a frame which is approx 16.66 ms (60fps)
function update(time){ // time is passed by requestAnimationFrame
if(startTime === undefined){ // get a reference for the start time if this is the first
frame
startTime = time;
}
ctx.fillStyle = BGStyle;
ctx.fillRect(0, 0, canvas.width, canvas.height); // clear the canvas by
drawing over it
textOffset = ((time - startTime) * textSpeed) % (totalTextSize); // move the text left
ctx.fillStyle = textStyle; // set the text style
ctx.fillText(textToDisplay, textX - textOffset, canvas.height / 2); // render the text
Este ejemplo agrega un nuevo rectángulo al lienzo cada 1 segundo (== un intervalo de 1
segundo)
Código anotado:
https://riptutorial.com/es/home 9
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
function animate(currentTime){
Este ejemplo anima un reloj que muestra los segundos como una cuña llena.
https://riptutorial.com/es/home 10
Código anotado:
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
function animate(currentTime){
https://riptutorial.com/es/home 11
Use requestAnimationFrame () NOT setInterval () para los bucles de
animación
• Cada bucle siempre se completa antes de que se pueda iniciar otro bucle. Esto evita
"rasgar", donde el usuario ve una versión incompleta del dibujo. El ojo nota particularmente
lagrimeo y se distrae cuando se produce lagrimeo. Por lo tanto, prevenir el desgarro hace
que su animación se vea más suave y más consistente.
Las pantallas del dispositivo se actualizarán aproximadamente 60 veces por segundo, por lo que
requestAnimationFrame puede volver a dibujar continuamente a aproximadamente 60 "cuadros"
por segundo. El ojo ve movimiento a 20-30 cuadros por segundo, por lo que
requestAnimationFrame puede crear fácilmente la ilusión de movimiento.
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
function animate(currentTime){
https://riptutorial.com/es/home 12
var x=Math.random()*canvas.width;
var y=Math.random()*canvas.height;
var radius=10+Math.random()*15;
ctx.beginPath();
ctx.arc(x,y,radius,0,Math.PI*2);
ctx.fillStyle='#'+Math.floor(Math.random()*16777215).toString(16);
ctx.fill();
Para ilustrar las ventajas de requestAnimationFrame, esta pregunta de stackoverflow tiene una
demostración en vivo
Código anotado
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
https://riptutorial.com/es/home 13
// IMPORTANT!!! You must give the image time to load by using img.onload!
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/sun.png";
function start(){
// the image is fully loaded sostart animating
requestAnimationFrame(animate);
}
function animate(time){
// draw
ctx.drawImage(img,x,y);
// update
x += speedX * direction;
// keep "x" inside min & max
if(x<minX){ x=minX; direction*=-1; }
if(x>maxX){ x=maxX; direction*=-1; }
Durante mousemove te mousemove con 30 eventos de ratón por segundo. Es posible que no pueda
volver a dibujar sus dibujos a 30 veces por segundo. Incluso si puede, probablemente esté
desperdiciando poder de cómputo al dibujar cuando el navegador no está listo para dibujar
(desperdiciado == a través de los ciclos de actualización de la pantalla).
Por lo tanto, tiene sentido separar los eventos de entrada de sus usuarios (como mousemove) del
dibujo de sus animaciones.
• En los controladores de eventos, guarde todas las variables de eventos que controlan dónde
se ubican los dibujos en el Lienzo. Pero en realidad no dibujar nada.
https://riptutorial.com/es/home 14
Al hacer todos los dibujos en requestAnimationFrame , obtiene todos los beneficios descritos aquí.
Use 'requestanimationFrame' no 'setInterval' para los bucles de animación .
Código anotado:
<!doctype html>
<html>
<head>
<style>
body{ background-color: ivory; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
function log(){console.log.apply(console,arguments);}
// canvas variables
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
// set canvas styling
ctx.strokeStyle='skyblue';
ctx.lineJoint='round';
ctx.lineCap='round';
ctx.lineWidth=6;
canvas.onmousemove=function(e){handleMouseMove(e);}
function handleMouseMove(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
https://riptutorial.com/es/home 15
points.push({x:mouseX,y:mouseY});
}
function draw(){
// No additional points? Request another frame an return
var length=points.length;
if(length==lastLength){requestAnimationFrame(draw);return;}
Una flexibilización hace que alguna variable cambie de manera desigual a lo largo de una
duración .
"variable" debe poder expresarse como un número y puede representar una variedad notable 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.
La "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 para ser ejecutados,
• cualquier cosa que pueda ser expresada como
"desigual" significa que la variable avanza de manera desigual desde el principio hasta el final:
https://riptutorial.com/es/home 16
• más rápido al principio y más lento al final, o viceversa,
• sobrepasa el final pero retrocede hasta el final a medida que termina la duración,
• Avanza / retrocede repetidamente elásticamente durante la duración,
• "rebota" en el final mientras se detiene a medida que la duración termina.
Cite: https://github.com/danro/jquery-easing/blob/master/jquery.easing.js
https://riptutorial.com/es/home 17
},
easeInOutSine: function (t, b, c, d) {
return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
},
easeInExpo: function (t, b, c, d) {
return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
},
easeOutExpo: function (t, b, c, d) {
return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
},
easeInOutExpo: function (t, b, c, d) {
if (t==0) return b;
if (t==d) return b+c;
if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
},
easeInCirc: function (t, b, c, d) {
return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
},
easeOutCirc: function (t, b, c, d) {
return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
},
easeInOutCirc: function (t, b, c, d) {
if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
},
easeInElastic: function (t, b, c, d) {
var s=1.70158;var p=0;var a=c;
if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
if (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*d-s)*(2*Math.PI)/p )) + b;
},
easeOutElastic: function (t, b, c, d) {
var s=1.70158;var p=0;var a=c;
if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
if (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*d-s)*(2*Math.PI)/p ) + c + b;
},
easeInOutElastic: function (t, b, c, d) {
var s=1.70158;var p=0;var a=c;
if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5);
if (a < Math.abs(c)) { a=c; var s=p/4; }
else var s = p/(2*Math.PI) * Math.asin (c/a);
if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
},
easeInBack: function (t, b, c, d, s) {
if (s == undefined) s = 1.70158;
return c*(t/=d)*t*((s+1)*t - s) + b;
},
easeOutBack: function (t, b, c, d, s) {
if (s == undefined) s = 1.70158;
return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
},
easeInOutBack: function (t, b, c, d, s) {
if (s == undefined) s = 1.70158;
if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
},
https://riptutorial.com/es/home 18
easeInBounce: function (t, b, c, d) {
return c - Easings.easeOutBounce (d-t, 0, c, d) + b;
},
easeOutBounce: function (t, b, c, d) {
if ((t/=d) < (1/2.75)) {
return 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;
}
},
easeInOutBounce: function (t, b, c, d) {
if (t < d/2) return 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:
// Demo
var startTime;
var beginningValue=50; // beginning x-coordinate
var endingValue=450; // ending x-coordinate
var totalChange=endingValue-beginningValue;
var totalDuration=3000; // ms
var keys=Object.keys(Easings);
ctx.textBaseline='middle';
requestAnimationFrame(animate);
function animate(time){
var PI2=Math.PI*2;
if(!startTime){startTime=time;}
var elapsedTime=Math.min(time-startTime,totalDuration);
ctx.clearRect(0,0,cw,ch);
ctx.beginPath();
for(var y=0;y<keys.length;y++){
var key=keys[y];
var easing=Easings[key];
var easedX=easing(
elapsedTime,beginningValue,totalChange,totalDuration);
if(easedX>endingValue){easedX=endingValue;}
ctx.moveTo(easedX,y*15);
ctx.arc(easedX,y*15+10,5,0,PI2);
ctx.fillText(key,460,y*15+10-1);
}
ctx.fill();
if(time<startTime+totalDuration){
requestAnimationFrame(animate);
}
}
https://riptutorial.com/es/home 19
El uso de requestAnimationFrame puede actualizarse en algunos sistemas a más cuadros por
segundo que los 60 fps. 60fps es la tasa predeterminada si la representación puede continuar.
Algunos sistemas se ejecutarán a 120 fps tal vez más.
Si usa el siguiente método, solo debe usar velocidades de cuadro que sean divisiones enteras de
60, de modo que (60 / FRAMES_PER_SECOND) % 1 === 0 sea true o obtendrá velocidades de cuadro
inconsistentes.
Usa vectores para calcular [x, y] incrementales de [startX, startY] a [endX, endY]
Código de ejemplo:
// canvas vars
var canvas=document.createElement("canvas");
document.body.appendChild(canvas);
canvas.style.border='1px solid red';
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
// canvas styles
ctx.strokeStyle='skyblue';
ctx.fillStyle='blue';
https://riptutorial.com/es/home 20
// animating vars
var pct=101;
var startX=20;
var startY=50;
var endX=225;
var endY=100;
var dx=endX-startX;
var dy=endY-startY;
https://riptutorial.com/es/home 21
Capítulo 3: Arrastrando formas de ruta e
imágenes sobre lienzo
Examples
Cómo las formas e imágenes REALMENTE (!) Se "mueven" en el lienzo
Esta es una imagen de una pelota de playa circular y, por supuesto, no puede 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 alrededor del lienzo. Eso es porque el lienzo no recordará donde dibujó el
círculo.
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.
https://riptutorial.com/es/home 22
Esto significa que todo lo dibujado en el lienzo es permanente: inmóvil e inmutable.
• Borrar el lienzo
// create a canvas
var canvas=document.createElement("canvas");
var ctx=canvas.getContext("2d");
ctx.fillStyle='red';
document.body.appendChild(canvas);
function animate(){
// update the X position of the circle
circleX++;
// redraw the circle in it's new position
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.beginPath();
ctx.arc( circleX, 30,15,0,Math.PI*2 );
ctx.fill();
// request another animate() loop
requestAnimationFrame(animate);
}
https://riptutorial.com/es/home 23
¿Qué es una "forma"?
Por lo general, guarda sus formas creando un objeto de "forma" de JavaScript que representa
cada forma.
Por supuesto, no estás realmente guardando formas. En cambio, estás guardando la definición de
cómo dibujar las formas.
Luego ponga cada objeto de forma en una matriz para una referencia fácil.
En mousedown:
Probar si alguna forma está debajo del ratón. Si una forma está debajo del mouse, el usuario
tiene la intención de arrastrar esa forma. Por lo tanto mantener una referencia a la forma y
establecer un verdadero / falso isDragging bandera que indica que un lastre está en proceso.
En mousemove:
Calcule la distancia que ha arrastrado el mouse desde el último evento de mousemove y cambie la
posición de la forma arrastrada por esa distancia. Para cambiar la posición de la forma, cambia
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 completa el arrastre.
https://riptutorial.com/es/home 24
rectángulos en el lienzo
Esta demostración arrastra círculos y rectángulos en el lienzo respondiendo a los eventos del
mouse y dando la ilusión de movimiento al borrar y volver a dibujar.
https://riptutorial.com/es/home 25
return(true);
}
}else if(shape.width){
// this is a rectangle
var rLeft=shape.x;
var rRight=shape.x+shape.width;
var rTop=shape.y;
var rBott=shape.y+shape.height;
// math test to see if mouse is inside rectangle
if( mx>rLeft && mx<rRight && my>rTop && my<rBott){
return(true);
}
}
// the mouse isn't in any of the shapes
return(false);
}
function handleMouseDown(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// calculate the current mouse position
startX=parseInt(e.clientX-offsetX);
startY=parseInt(e.clientY-offsetY);
// test mouse position against all shapes
// post result if mouse is in a shape
for(var i=0;i<shapes.length;i++){
if(isMouseInShape(startX,startY,shapes[i])){
// the mouse is inside this shape
// select this shape
selectedShapeIndex=i;
// set the isDragging flag
isDragging=true;
// and return (==stop looking for
// further shapes under the mouse)
return;
}
}
}
function handleMouseUp(e){
// return if we're not dragging
if(!isDragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// the drag is over -- clear the isDragging flag
isDragging=false;
}
function handleMouseOut(e){
// return if we're not dragging
if(!isDragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// the drag is over -- clear the isDragging flag
isDragging=false;
}
function handleMouseMove(e){
https://riptutorial.com/es/home 26
// return if we're not dragging
if(!isDragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// calculate the current mouse position
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// how far has the mouse dragged from its previous mousemove position?
var dx=mouseX-startX;
var dy=mouseY-startY;
// move the selected shape by the drag distance
var selectedShape=shapes[selectedShapeIndex];
selectedShape.x+=dx;
selectedShape.y+=dy;
// clear the canvas and redraw all shapes
drawAll();
// update the starting drag position (== the current mouse position)
startX=mouseX;
startY=mouseY;
}
La mayoría de los dibujos de lienzo son rectangulares (rectángulos, imágenes, bloques de texto)
o circulares (círculos).
Los círculos y rectángulos tienen pruebas matemáticas para verificar si el mouse está dentro de
ellos. Esto hace que las pruebas de círculos y rectángulos sean fáciles, rápidas y eficientes.
Puedes "probar" cientos de círculos o rectángulos en una fracción de segundo.
También puedes arrastrar formas irregulares. Pero las formas irregulares no tienen una prueba de
golpe matemática rápida. Afortunadamente, las formas irregulares tienen una prueba de impacto
integrada 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 resultados puramente
matemáticas; a menudo es hasta 10 veces más lento que las pruebas de resultados puramente
https://riptutorial.com/es/home 27
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 emitir los
comandos de dibujo de ruta (como anteriormente), pero no necesita trazar () o completar () la ruta
antes de probarla con isPointInPath . De esta manera, puede probar rutas previamente dibujadas
sin tener que sobrescribir (trazar / rellenar) las rutas anteriores en el lienzo.
La forma irregular no necesita ser tan común como el triángulo cotidiano. También puedes hacer
una prueba de golpe en cualquier camino irregularmente salvaje.
Este ejemplo anotado muestra cómo arrastrar formas de ruta irregular, así como círculos y
rectángulos:
https://riptutorial.com/es/home 28
// given mouse X & Y (mx & my) and shape object
// return true/false whether mouse is inside the shape
function isMouseInShape(mx,my,shape){
if(shape.radius){
// this is a circle
var dx=mx-shape.x;
var dy=my-shape.y;
// math test to see if mouse is inside circle
if(dx*dx+dy*dy<shape.radius*shape.radius){
// yes, mouse is inside this circle
return(true);
}
}else if(shape.width){
// this is a rectangle
var rLeft=shape.x;
var rRight=shape.x+shape.width;
var rTop=shape.y;
var rBott=shape.y+shape.height;
// math test to see if mouse is inside rectangle
if( mx>rLeft && mx<rRight && my>rTop && my<rBott){
return(true);
}
}else if(shape.points){
// this is a polyline path
// First redefine the path again (no need to stroke/fill!)
defineIrregularPath(shape);
// Then hit-test with isPointInPath
if(ctx.isPointInPath(mx,my)){
return(true);
}
}
// the mouse isn't in any of the shapes
return(false);
}
function handleMouseDown(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// calculate the current mouse position
startX=parseInt(e.clientX-offsetX);
startY=parseInt(e.clientY-offsetY);
// test mouse position against all shapes
// post result if mouse is in a shape
for(var i=0;i<shapes.length;i++){
if(isMouseInShape(startX,startY,shapes[i])){
// the mouse is inside this shape
// select this shape
selectedShapeIndex=i;
// set the isDragging flag
isDragging=true;
// and return (==stop looking for
// further shapes under the mouse)
return;
}
}
}
function handleMouseUp(e){
// return if we're not dragging
https://riptutorial.com/es/home 29
if(!isDragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// the drag is over -- clear the isDragging flag
isDragging=false;
}
function handleMouseOut(e){
// return if we're not dragging
if(!isDragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// the drag is over -- clear the isDragging flag
isDragging=false;
}
function handleMouseMove(e){
// return if we're not dragging
if(!isDragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// calculate the current mouse position
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// how far has the mouse dragged from its previous mousemove position?
var dx=mouseX-startX;
var dy=mouseY-startY;
// move the selected shape by the drag distance
var selectedShape=shapes[selectedShapeIndex];
selectedShape.x+=dx;
selectedShape.y+=dy;
// clear the canvas and redraw all shapes
drawAll();
// update the starting drag position (== the current mouse position)
startX=mouseX;
startY=mouseY;
}
https://riptutorial.com/es/home 30
ctx.fill();
}
}
}
function defineIrregularPath(shape){
var points=shape.points;
ctx.beginPath();
ctx.moveTo(shape.x+points[0].x,shape.y+points[0].y);
for(var i=1;i<points.length;i++){
ctx.lineTo(shape.x+points[i].x,shape.y+points[i].y);
}
ctx.closePath();
}
Consulte este Ejemplo para obtener una explicación general del arrastre de formas alrededor del
lienzo.
Este ejemplo anotado muestra cómo arrastrar imágenes alrededor del lienzo
https://riptutorial.com/es/home 31
shapes.push( {x:30, y:10, width:127, height:150, image:card} );
// draw the shapes on the canvas
drawAll();
// listen for mouse events
canvas.onmousedown=handleMouseDown;
canvas.onmousemove=handleMouseMove;
canvas.onmouseup=handleMouseUp;
canvas.onmouseout=handleMouseOut;
};
// put your image src here!
card.src='https://dl.dropboxusercontent.com/u/139992952/stackoverflow/card.png';
function handleMouseDown(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// calculate the current mouse position
startX=parseInt(e.clientX-offsetX);
startY=parseInt(e.clientY-offsetY);
// test mouse position against all shapes
// post result if mouse is in a shape
for(var i=0;i<shapes.length;i++){
if(isMouseInShape(startX,startY,shapes[i])){
// the mouse is inside this shape
// select this shape
selectedShapeIndex=i;
// set the isDragging flag
isDragging=true;
// and return (==stop looking for
// further shapes under the mouse)
return;
}
}
}
function handleMouseUp(e){
// return if we're not dragging
if(!isDragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
https://riptutorial.com/es/home 32
// the drag is over -- clear the isDragging flag
isDragging=false;
}
function handleMouseOut(e){
// return if we're not dragging
if(!isDragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// the drag is over -- clear the isDragging flag
isDragging=false;
}
function handleMouseMove(e){
// return if we're not dragging
if(!isDragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// calculate the current mouse position
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// how far has the mouse dragged from its previous mousemove position?
var dx=mouseX-startX;
var dy=mouseY-startY;
// move the selected shape by the drag distance
var selectedShape=shapes[selectedShapeIndex];
selectedShape.x+=dx;
selectedShape.y+=dy;
// clear the canvas and redraw all shapes
drawAll();
// update the starting drag position (== the current mouse position)
startX=mouseX;
startY=mouseY;
}
https://riptutorial.com/es/home 33
Capítulo 4: Borrar la pantalla
Sintaxis
• void clearRect (x, y, ancho, alto)
• ImageData createImageData (ancho, alto)
Observaciones
Ninguno de estos métodos producirá píxeles transparentes si el contexto se creó con el
parámetro alpha: false .
Examples
Rectángulos
Puede utilizar el método clearRect para borrar cualquier sección rectangular del lienzo.
Para solucionar esto, es posible restablecer la matriz de transformación antes de borrar el lienzo.
https://riptutorial.com/es/home 34
Formas complejas
En lugar de utilizar clearRect que hace que todos los píxeles sean transparentes, es posible que
desee un fondo.
Esto es aproximadamente la mitad de 0.008ms rápidos que los 0.008ms ms 0.004ms 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 = 'copy';
https://riptutorial.com/es/home 35
Capítulo 5: Caminos
Examples
Elipse
ctx.beginPath();
var x = cx + radius * Math.cos(0);
var y = cy - ratio * radius * Math.sin(0);
ctx.lineTo(x,y);
ctx.closePath();
ctx.stroke();
}
Dibuja una elipse dado que es la coordenada del punto central deseada:
ctx.beginPath();
https://riptutorial.com/es/home 36
var x = cx + radius * Math.cos(0);
var y = cy - ratio * radius * Math.sin(0);
ctx.lineTo(x,y);
ctx.closePath();
ctx.stroke();
}
Cuando Canvas dibuja una línea, agrega automáticamente suavizado para curar visualmente el
"jaggedness". El resultado es una línea que es menos irregular pero más borrosa.
Esta función dibuja una línea entre 2 puntos sin suavizado utilizando el algoritmo de línea de
Bresenham . El resultado es una línea nítida sin la irregularidad.
Nota importante: este método píxel por píxel es un método de dibujo mucho más lento que
context.lineTo .
// Usage:
bresenhamLine(50,50,250,250);
https://riptutorial.com/es/home 37
y = Math.floor(y);
// BRENSENHAM
var dx = Math.abs(xx-x);
var sx = x < xx ? 1 : -1;
var dy = -Math.abs(yy-y);
var sy = y<yy ? 1 : -1;
var err = dx+dy;
var errC; // error value
var end = false;
var x1 = x;
var y1 = y;
while(!end){
ctx.fillRect(x1, y1, 1, 1); // draw each pixel as a rect
if (x1 === xx && y1 === yy) {
end = true;
}else{
errC = 2*err;
if (errC >= dy) {
err += dy;
x1 += sx;
}
if (errC <= dx) {
err += dx;
y1 += sy;
}
}
}
ctx.fillStyle = oldFill; // restore old fill style
}
https://riptutorial.com/es/home 38
Capítulo 6: Colisiones e Intersecciones
Examples
¿Chocan 2 círculos?
function CirclesColliding(c1,c2){
var dx=c2.x-c1.x;
var dy=c2.y-c1.y;
var rSum=c1.radius+c2.radius;
return(dx*dx+dy*dy<=rSum*rSum);
}
¿Chocan 2 rectángulos?
function RectsColliding(r1,r2){
return !(
r1.x>r2.x+r2.width ||
r1.x+r1.width<r2.x ||
r1.y>r2.y+r2.height ||
r1.y+r1.height<r2.y
);
}
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));
var dx=dx-rect.width;
var dy=dy-rect.height
return(dx*dx+dy*dy<=circle.radius*circle.radius);
}
https://riptutorial.com/es/home 39
¿Están interceptando los segmentos de 2 líneas?
La función en este ejemplo devuelve true si dos segmentos de línea se intersecan y false si no.
El ejemplo está diseñado para el rendimiento y utiliza el cierre para mantener las variables de
trabajo
var v1, v2, v3, cross, u1, u2; // working variable are closed over so they do not
need creation
// each time the function is called. This gives a
significant performance boost.
v1 = {x : null, y : null}; // line p0, p1 as vector
v2 = {x : null, y : null}; // line p2, p3 as vector
v3 = {x : null, y : null}; // the line from p0 to p2 as vector
Ejemplo de uso
https://riptutorial.com/es/home 40
return {
x : p0.x + v1.x * u1,
y : p0.y + v1.y * u1,
};
}
O si desea obtener el punto de intercepción en las líneas, ignorando el inicio y el final de los
segmentos de línea, reemplace el código entre el code point B y el B end con
return {
x : p2.x + v2.x * u2,
y : p2.y + v2.y * u2,
};
// var rect={x:,y:,width:,height:};
// var line={x1:,y1:,x2:,y2:};
// Get interseting point of line segment & rectangle (if any)
function lineRectCollide(line,rect){
https://riptutorial.com/es/home 41
var p2={x:line.x2,y:line.y2};
// Test if Coincident
// If the denominator and numerator for the ua and ub are 0
// then the two lines are coincident.
if(unknownA==0 && unknownB==0 && denominator==0){return(null);}
// Test if Parallel
// If the denominator for the equations for ua and ub is 0
// then the two lines are parallel.
if (denominator == 0) return null;
return(isIntersecting);
}
Use el teorema del eje de separación para determinar si dos polígonos convexos se intersecan
https://riptutorial.com/es/home 42
// polygon objects are an array of vertices forming the polygon
// var polygon1=[{x:100,y:100},{x:150,y:150},{x:50,y:150},...];
// THE POLYGONS MUST BE CONVEX
// return true if the 2 polygons are colliding
// for each polygon, look at each edge of the polygon, and determine if it separates
// the two shapes
var polygon = polygons[i];
for (i1 = 0; i1 < polygon.length; i1++) {
// for each vertex in the second shape, project it onto the line perpendicular to
the edge
// and keep track of the min and max of these values
minB = maxB = undefined;
for (j = 0; j < b.length; j++) {
projected = normal.x * b[j].x + normal.y * b[j].y;
if (minB==undefined || projected < minB) {
minB = projected;
}
if (maxB==undefined || projected > maxB) {
maxB = projected;
}
}
https://riptutorial.com/es/home 43
};
Prueba todos los lados de los polígonos en busca de intersecciones para determinar si 2
polígonos están colisionando.
function polygonsCollide(p1,p2){
// turn vertices into line points
var lines1=verticesToLinePoints(p1);
var lines2=verticesToLinePoints(p2);
// test each poly1 side vs each poly2 side for intersections
for(i=0; i<lines1.length; i++){
for(j=0; j<lines2.length; j++){
// test if sides intersect
var p0=lines1[i][0];
var p1=lines1[i][1];
var p2=lines2[j][0];
var p3=lines2[j][1];
// found an intersection -- polys do collide
if(lineSegmentsCollide(p0,p1,p2,p3)){return(true);}
}}
// none of the sides intersect
return(false);
}
// helper: turn vertices into line points
function verticesToLinePoints(p){
// make sure polys are self-closing
if(!(p[0].x==p[p.length-1].x && p[0].y==p[p.length-1].y)){
p.push({x:p[0].x,y:p[0].y});
}
var lines=[];
for(var i=1;i<p.length;i++){
var p1=p[i-1];
var p2=p[i];
lines.push([
{x:p1.x, y:p1.y},
{x:p2.x, y:p2.y}
]);
}
return(lines);
}
// helper: test line intersections
// point object: {x:, y:}
// p0 & p1 form one segment, p2 & p3 form the second segment
// Get interseting point of 2 line segments (if any)
// Attribution: http://paulbourke.net/geometry/pointlineplane/
function lineSegmentsCollide(p0,p1,p2,p3) {
var unknownA = (p3.x-p2.x) * (p0.y-p2.y) - (p3.y-p2.y) * (p0.x-p2.x);
var unknownB = (p1.x-p0.x) * (p0.y-p2.y) - (p1.y-p0.y) * (p0.x-p2.x);
var denominator = (p3.y-p2.y) * (p1.x-p0.x) - (p3.x-p2.x) * (p1.y-p0.y);
// Test if Coincident
// If the denominator and numerator for the ua and ub are 0
https://riptutorial.com/es/home 44
// then the two lines are coincident.
if(unknownA==0 && unknownB==0 && denominator==0){return(null);}
// Test if Parallel
// If the denominator for the equations for ua and ub is 0
// then the two lines are parallel.
if (denominator == 0) return null;
return(isIntersecting);
}
var arc={
cx:150, cy:150,
innerRadius:75, outerRadius:100,
startAngle:0, endAngle:Math.PI
}
function isPointInArc(x,y,arc){
var dx=x-arc.cx;
var dy=y-arc.cy;
var dxy=dx*dx+dy*dy;
var rrOuter=arc.outerRadius*arc.outerRadius;
var rrInner=arc.innerRadius*arc.innerRadius;
if(dxy<rrInner || dxy>rrOuter){return(false);}
var angle=(Math.atan2(dy,dx)+PI2)%PI2;
return(angle>=arc.startAngle && angle<=arc.endAngle);
}
https://riptutorial.com/es/home 45
// wedge objects: {cx:,cy:,radius:,startAngle:,endAngle:}
// var wedge={
// cx:150, cy:150, // centerpoint
// radius:100,
// startAngle:0, endAngle:Math.PI
// }
// Return true if the x,y point is inside the closed wedge
function isPointInWedge(x,y,wedge){
var PI2=Math.PI*2;
var dx=x-wedge.cx;
var dy=y-wedge.cy;
var rr=wedge.radius*wedge.radius;
if(dx*dx+dy*dy>rr){return(false);}
var angle=(Math.atan2(dy,dx)+PI2)%PI2;
return(angle>=wedge.startAngle && angle<=wedge.endAngle);
}
function isPointInCircle(x,y,circle){
var dx=x-circle.cx;
var dy=y-circle.cy;
return(dx*dx+dy*dy<circle.radius*circle.radius);
}
function isPointInRectangle(x,y,rect){
https://riptutorial.com/es/home 46
return(x>rect.x && x<rect.x+rect.width && y>rect.y && y<rect.y+rect.height);
}
https://riptutorial.com/es/home 47
Capítulo 7: Compositing
Examples
Dibuja detrás de las formas existentes con "destino sobre"
context.globalCompositeOperation = "destination-over"
La composición "destino sobre" coloca el nuevo dibujo debajo de los dibujos existentes .
context.drawImage(rainy,0,0);
context.globalCompositeOperation='destination-over'; // sunny UNDER rainy
context.drawImage(sunny,0,0);
context.globalCompositeOperation = "destination-out"
La composición "destination-out" utiliza nuevas formas para borrar los dibujos existentes.
La nueva forma no está realmente dibujada, solo se utiliza como "cortador de galletas" para borrar
los píxeles existentes.
context.drawImage(apple,0,0);
context.globalCompositeOperation = 'destination-out'; // bitemark erases
context.drawImage(bitemark,100,40);
https://riptutorial.com/es/home 48
Composición predeterminada: las nuevas formas se dibujan sobre las formas
existentes
context.globalCompositeOperation = "source-over"
La composición de "fuente sobre" [predeterminada] , coloca todos los dibujos nuevos sobre los
dibujos existentes.
context.globalCompositeOperation = "destination-in"
La composición de "destino en" recorta los dibujos existentes dentro de una nueva forma.
Nota: se borra cualquier parte del dibujo existente que se encuentre fuera del dibujo nuevo.
https://riptutorial.com/es/home 49
context.drawImage(picture,0,0);
context.globalCompositeOperation='destination-in'; // picture clipped inside oval
context.drawImage(oval,0,0);
context.globalCompositeOperation = "source-in";
source-in composición de source-in clips nuevos dibujos dentro de una forma existente.
Nota: se borra cualquier parte del nuevo dibujo que se encuentre fuera del dibujo existente.
context.drawImage(oval,0,0);
context.globalCompositeOperation='source-in'; // picture clipped inside oval
context.drawImage(picture,0,0);
context.globalCompositeOperation = 'source-atop'
source-atop composición de source-atop sujeta una nueva imagen dentro de una forma existente.
https://riptutorial.com/es/home 50
ctx.shadowColor='black';
ctx.shadowBlur=10;
// restrict new draw to cover existing pixels
ctx.globalCompositeOperation='source-atop';
// shadowed stroke
// "source-atop" clips off the undesired outer shadow
ctx.strokeRect(100,100,100,75);
ctx.strokeRect(100,100,100,75);
ctx.globalCompositeOperation = 'difference';
https://riptutorial.com/es/home 51
Eliminar color de una imagen a través de
ctx.globalCompositeOperation = 'color';
ctx.globalCompositeOperation = 'saturation';
La cantidad del efecto se puede controlar con la configuración alfa o la cantidad de saturación en
la superposición de relleno
https://riptutorial.com/es/home 52
Sepia FX con "luminosidad"
ctx.globalCompositeOperation = 'luminosity';
La cantidad del efecto se puede controlar con la configuración alfa o la cantidad de saturación en
la superposición de relleno
context.globalAlpha=0.50
Puede cambiar la opacidad de los nuevos dibujos configurando globalAlpha en un valor entre 0.00
(totalmente transparente) y 1.00 (totalmente opaco).
https://riptutorial.com/es/home 53
El valor globalAlpha predeterminado es 1.00 (totalmente opaco).
// change alpha to 50% -- all new drawings will have 50% opacity
context.globalAlpha=0.50;
https://riptutorial.com/es/home 54
Capítulo 8: Diseño de respuesta
Examples
Creación de un lienzo de página completa sensible
Código de inicio para crear y eliminar un lienzo de página completa que responde para cambiar el
tamaño de los eventos a través de javascript.
Las aplicaciones de lienzo a menudo dependen en gran medida de la interacción del usuario con
https://riptutorial.com/es/home 55
el mouse, pero cuando se cambia el tamaño de la ventana, las coordenadas del evento del
mouse en las que se basa el lienzo probablemente se cambian porque el cambio de tamaño hace
que el lienzo se desplace en una posición diferente con respecto a la ventana. Por lo tanto, el
diseño responsivo requiere que la posición de desplazamiento del lienzo se vuelva a calcular
cuando se cambie el tamaño de la ventana, y también se vuelva a calcular cuando la ventana se
desplace.
Este código escucha los eventos de cambio de tamaño de la ventana y vuelve a calcular las
compensaciones utilizadas en los controladores de eventos del mouse:
Los eventos de cambio de tamaño de la ventana pueden activarse en respuesta al movimiento del
dispositivo de entrada del usuario. Cuando cambia el tamaño de un lienzo, se borra
automáticamente y se ve obligado a volver a renderizar el contenido. Para las animaciones, haga
esto en cada fotograma a través de la función de bucle principal llamada por 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 tasa
estándar de 60 fps del navegador. Cuando el evento de cambio de tamaño sale del lienzo, el
búfer posterior se presenta al DOM desincronizado con el dispositivo de visualización, lo que
puede causar distorsión y otros efectos negativos. También hay una gran cantidad de memoria y
distribución innecesarias que pueden afectar aún más a la animación cuando GC se limpia un
poco después.
https://riptutorial.com/es/home 56
Evento de cambio de tamaño anunciado
Una forma común de lidiar con las altas tasas de activación del evento de cambio de tamaño es
rebotar el evento de cambio de tamaño.
// Resize function
function debouncedResize () {
clearTimeout(debounceTimeoutHandle); // Clears any pending debounce events
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
efectivamente consume la mayoría de los eventos de cambio de tamaño.
Todavía tiene algunos problemas, el más notable es el retraso 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 pantalla. También tiene la representación de bucle
principal de animación en un lienzo de mal ajuste.
¡Más código puede reducir los problemas! Más código también crea sus propios problemas
nuevos.
KISS es algo de lo que la mayoría de los programadores deberían saber (( K eep I t S ple S tple
Supple) El estúpido se refiere a mí por no haberlo pensado hace años ) y resulta que la mejor
solución es la más simple de todas.
Simplemente redimensiona el lienzo desde dentro del bucle principal de animación. Permanece
sincronizado con el dispositivo de visualización, no hay una representación innecesaria y la
https://riptutorial.com/es/home 57
gestión de recursos es lo mínimo posible mientras se mantiene la velocidad de fotogramas
completa. Tampoco necesita agregar un evento de cambio de tamaño a la ventana o cualquier
función de cambio de tamaño adicional.
Agregue el tamaño del lugar donde normalmente borraría el lienzo al verificar si el tamaño del
lienzo coincide con el tamaño de la ventana. Si no lo cambiamos de tamaño.
requestAnimationFrame(mainLoop);
}
https://riptutorial.com/es/home 58
Capítulo 9: Gráficos y diagramas
Examples
Línea con puntas de flecha
// Usage:
drawLineWithArrows(50,50,150,50,5,8,true,true);
function drawLineWithArrows(x0,y0,x1,y1,aWidth,aLength,arrowStart,arrowEnd){
var dx=x1-x0;
var dy=y1-y0;
var angle=Math.atan2(dy,dx);
var length=Math.sqrt(dx*dx+dy*dy);
//
ctx.translate(x0,y0);
ctx.rotate(angle);
ctx.beginPath();
ctx.moveTo(0,0);
ctx.lineTo(length,0);
if(arrowStart){
ctx.moveTo(aLength,-aWidth);
ctx.lineTo(0,0);
ctx.lineTo(aLength,aWidth);
}
if(arrowEnd){
ctx.moveTo(length-aLength,-aWidth);
ctx.lineTo(length,0);
ctx.lineTo(length-aLength,aWidth);
}
//
ctx.stroke();
ctx.setTransform(1,0,0,1,0,0);
}
https://riptutorial.com/es/home 59
// Usage:
var p0={x:50,y:100};
var p1={x:100,y:0};
var p2={x:200,y:200};
var p3={x:300,y:100};
https://riptutorial.com/es/home 60
norm = pointsToNormalisedVec(p2,p3);
}
if (hasEndArrow) {
x = arrowWidth * norm.x + arrowLength * -norm.y;
y = arrowWidth * norm.y + arrowLength * norm.x;
ctx.moveTo(ex + x, ey + y);
ctx.lineTo(ex, ey);
x = arrowWidth * -norm.x + arrowLength * -norm.y;
y = arrowWidth * -norm.y + arrowLength * norm.x;
ctx.lineTo(ex + x, ey + y);
}
if (hasStartArrow) {
norm = pointsToNormalisedVec(p0,p1);
x = arrowWidth * norm.x - arrowLength * -norm.y;
y = arrowWidth * norm.y - arrowLength * norm.x;
ctx.moveTo(p0.x + x, p0.y + y);
ctx.lineTo(p0.x, p0.y);
x = arrowWidth * -norm.x - arrowLength * -norm.y;
y = arrowWidth * -norm.y - arrowLength * norm.x;
ctx.lineTo(p0.x + x, p0.y + y);
}
ctx.stroke();
}
Cuña
El código solo dibuja la cuña ... círculo dibujado aquí solo para perspectiva.
// Usage
var wedge={
cx:150, cy:150,
radius:100,
startAngle:0,
endAngle:Math.PI*.65
}
https://riptutorial.com/es/home 61
drawWedge(wedge,'skyblue','gray',4);
function drawWedge(w,fill,stroke,strokewidth){
ctx.beginPath();
ctx.moveTo(w.cx, w.cy);
ctx.arc(w.cx, w.cy, w.radius, w.startAngle, w.endAngle);
ctx.closePath();
ctx.fillStyle=fill;
ctx.fill();
ctx.strokeStyle=stroke;
ctx.lineWidth=strokewidth;
ctx.stroke();
}
// Usage:
var arc={
cx:150, cy:150,
innerRadius:75, outerRadius:100,
startAngle:-Math.PI/4, endAngle:Math.PI
}
drawArc(arc,'skyblue','gray',4);
function drawArc(a,fill,stroke,strokewidth){
ctx.beginPath();
ctx.arc(a.cx,a.cy,a.innerRadius,a.startAngle,a.endAngle);
ctx.arc(a.cx,a.cy,a.outerRadius,a.endAngle,a.startAngle,true);
ctx.closePath();
ctx.fillStyle=fill;
ctx.strokeStyle=stroke;
ctx.lineWidth=strokewidth
ctx.fill();
ctx.stroke();
}
https://riptutorial.com/es/home 62
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
pieChart(myData, myColor);
var sweeps = []
for (var i = 0; i < data.length; i++) {
sweeps.push(data[i] / total * PI2);
}
var accumAngle = 0;
for (var i = 0; i < sweeps.length; i++) {
drawWedge(accumAngle, accumAngle + sweeps[i], colors[i], data[i]);
accumAngle += sweeps[i];
}
}
https://riptutorial.com/es/home 63
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.arc(cx, cy, radius, startAngle, endAngle, false);
ctx.closePath();
ctx.fillStyle = fill;
ctx.strokeStyle = 'black';
ctx.fill();
ctx.stroke();
https://riptutorial.com/es/home 64
Capítulo 10: Imágenes
Examples
Recorte de imágenes utilizando lienzo.
Este ejemplo muestra una función de recorte de imagen simple que toma una imagen y recorta
las coordenadas y devuelve la imagen recortada.
Usar
El lienzo retenido
Al agregar contenido de fuentes fuera de su dominio o del sistema de archivos local, el lienzo está
marcado como contaminado. Intentar acceder a los datos de píxeles o convertirlos a un dataURL
producirá un error de seguridad.
Este ejemplo es solo un esbozo para atraer a alguien con una comprensión detallada detallada.
https://riptutorial.com/es/home 65
¿"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.
Ejemplo cargando múltiples imágenes antes de intentar dibujar con alguna de ellas.
Hay más cargadores de imágenes con todas las funciones, pero este ejemplo ilustra cómo
hacerlo.
// first image
var img1=new Image();
img1.onload=start;
img1.onerror=function(){alert(img1.src+' failed to load.');};
img1.src="imageOne.png";
// second image
var img2=new Image();
img2.onload=start;
img1.onerror=function(){alert(img2.src+' failed to load.');};
img2.src="imageTwo.png";
//
var imgCount=2;
// start is called every time an image loads
function start(){
// countdown until all images are loaded
if(--imgCount>0){return;}
// All the images are now successfully loaded
// context.drawImage will successfully draw each one
context.drawImage(img1,0,0);
context.drawImage(img2,50,0);
}
Significa que toda la imagen será visible, pero puede haber algún espacio vacío en los lados o en
https://riptutorial.com/es/home 66
la parte superior e inferior si la imagen no tiene el mismo aspecto que el lienzo. El ejemplo
muestra la imagen a escala para ajustarse. El azul en los lados se debe al hecho de que la
imagen no es el mismo aspecto que el lienzo.
Significa que la imagen se escala para que la imagen cubra todos los píxeles del lienzo. Si el
aspecto de la imagen no es el mismo que el lienzo, entonces se recortarán algunas partes de la
imagen. El ejemplo muestra la imagen a escala para rellenar. Observe cómo la parte superior e
inferior de la imagen ya no son visibles.
function scaleToFit(img){
// get the scale
var scale = Math.min(canvas.width / img.width, canvas.height / img.height);
// get the top left position of the image
var x = (canvas.width / 2) - (img.width / 2) * scale;
var y = (canvas.height / 2) - (img.height / 2) * scale;
ctx.drawImage(img, x, y, img.width * scale, img.height * scale);
}
https://riptutorial.com/es/home 67
Escala de ejemplo para llenar
function scaleToFill(img){
// get the scale
var scale = Math.max(canvas.width / img.width, canvas.height / img.height);
// get the top left position of the image
var x = (canvas.width / 2) - (img.width / 2) * scale;
var y = (canvas.height / 2) - (img.height / 2) * scale;
ctx.drawImage(img, x, y, img.width * scale, img.height * scale);
}
La única diferencia entre las dos funciones es obtener la escala. El ajuste usa la escala de ajuste
mínima, mientras que el relleno utiliza la escala de ajuste máximo.
https://riptutorial.com/es/home 68
Capítulo 11: Los tipos de medios y el lienzo.
Observaciones
Este tema trata sobre los distintos tipos de medios y cómo se pueden utilizar con el lienzo en la
interfaz 2D.
Tipos de medios
• Animaciones
• Videos
• Imágenes
• Imagenes HD
• Imagen vectorial
• Imagenes animadas
Formatos de medios
• Jpg / Jpeg
• Png
• Gif
• SVG
• M-JPEG
• Webm
• Webp
Imágenes
Hay una gran variedad de formatos de imagen compatibles con los navegadores, aunque ningún
navegador los admite a todos. Si tiene formatos de imagen particulares, desea utilizar los
navegadores Wiki y los formatos de imagen compatibles proporciona una buena visión general.
El mejor soporte es para los 3 formatos principales, "jpeg", "png" y "gif" con todos los
navegadores principales que brindan soporte.
JPEG
Las imágenes JPEG se adaptan mejor a las fotos y fotos como imágenes. No se prestan a los
cuadros, diagramas y texto. Las imágenes JPEG no admiten transparencia.
https://riptutorial.com/es/home 69
JPEG en wikipedia
Png
Las imágenes PNG son imágenes de la más alta calidad y también pueden incluir un canal alfa
para píxeles transparentes. Los datos de la imagen están comprimidos pero no producen
artefactos como las imágenes JPG.
Debido a la compresión sin pérdida y el soporte de canal alfa, los PNG se utilizan para juegos,
imágenes de componentes de interfaz de usuario, cuadros, diagramas y texto. Cuando se usan
para fotos como fotos, su tamaño de archivo puede ser mucho más grande que el de JPEG. .
El formato PNG también proporciona soporte de animación, aunque el soporte del navegador es
limitado, y el acceso a la animación para usar en el lienzo solo se puede realizar a través de las
API y bibliotecas de Javascript
El lienzo se puede usar para codificar imágenes PNG a través de canvas.toDataURL y canvas.toBlob
aunque el formato de salida está limitado a RGBA comprimido de 32 bits. El PNG proporcionará
una copia perfecta del pixel del lienzo.
PNG en wikipedia
GIF
Los GIF se usan para animaciones cortas, pero también se pueden usar para proporcionar
gráficos, diagramas y texto de alta calidad como imágenes. Los GIF tienen un soporte de color
muy limitado con un máximo de 256 colores por fotograma. Con el procesamiento de imágenes
cleaver, las imágenes gif pueden producir resultados sorprendentemente buenos, especialmente
cuando se animan. Los gifs también proporcionan transparencia, aunque esto está limitado a
activado o desactivado.
AS con PNG, las animaciones GIF no son accesibles directamente para su uso en el lienzo y
necesitará una API o biblioteca de Javascript para obtener acceso. GIF no se puede guardar a
través del lienzo y requerirá una API o una biblioteca para hacerlo.
GIF en wikipedia
Examples
Cargando y mostrando una imagen
https://riptutorial.com/es/home 70
Creando una imagen
• new Image()
• document.createElement("img")
• <img src = 'imageUrl' id='myImage'> Como parte del cuerpo HTML y recuperado con
document.getElementById('myImage')
La imagen es un HTMLImageElement
Propiedad image.src
La imagen src puede ser cualquier URL de imagen válida o dataURL codificada. Consulte las
Observaciones de este tema para obtener más información sobre los formatos de imagen y el
soporte.
• image.src = "http://my.domain.com/images/myImage.jpg"
• image.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=" *
• cargado
• Hubo un error
• La propiedad src no se ha establecido y es igual a la cadena vacía ""
Si la imagen es de una fuente no confiable y puede no ser accesible por una variedad de razones,
generará un evento de error. Cuando esto suceda, la imagen estará en un estado roto. Si intenta
dibujarlo en el lienzo, se mostrará el siguiente error.
Para dibujar una imagen SVG vectorial, la operación no es diferente de una imagen rasterizada:
Primero debe cargar su imagen SVG en un elemento HTMLImage, luego usar el método
https://riptutorial.com/es/home 71
drawImage() .
Las imágenes SVG tienen algunas ventajas sobre las trama, ya que no perderá calidad,
independientemente de la escala que dibuje en su 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 razones de seguridad, no se puede cargar contenido externo desde una imagen
SVG a la que se hace referencia en un elemento de imagen HTML ( <img> )
Sin hoja de estilo externa, sin imagen externa referenciada en los elementos SVGImage (
<image/> ), sin filtro externo o elemento vinculado por el atributo xlink:href ( <use
xlink:href="anImage.SVG#anElement"/> ) o la funcIRI ( url() ) método de atributo, etc.
Además, las hojas de estilo adjuntas en el documento principal no tendrán ningún efecto en
el documento SVG una vez referenciado en un elemento HTMLImage.
Finalmente, no se ejecutará ningún script dentro de la imagen SVG.
Solución alternativa: deberá adjuntar todos los elementos externos dentro del propio SVG
antes de hacer referencia al elemento HTMLImage. (para imágenes o fuentes, debe
adjuntar 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 qué es relativo. Algunos navegadores (Blink) intentarán hacer una conjetura, pero la
mayoría simplemente ignorará su imagen y no dibujará nada, sin una advertencia.
El lienzo se puede usar para mostrar videos de una variedad de fuentes. Este ejemplo muestra
cómo cargar un video como un recurso de archivo, mostrarlo y agregar un simple clic en la
pantalla para reproducir / pausar para alternar.
Un video es solo una imagen en lo que concierne al lienzo. Puedes dibujarlo como cualquier
imagen. La diferencia es que el video puede reproducirse y tiene sonido.
https://riptutorial.com/es/home 72
Consigue lienzo y configuración básica.
// It is assumed you know how to add a canvas and correctly size it.
var canvas = document.getElementById("myCanvas"); // get the canvas from the page
var ctx = canvas.getContext("2d");
var videoContainer; // object to hold video and associated info
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 proporcionan 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 puede que no haya suficiente para
reproducirlo hasta el final.
https://riptutorial.com/es/home 73
Mostrando
El video no se reproducirá en el lienzo. Necesitas dibujarlo para cada nuevo marco. Como es
difícil saber la velocidad de fotogramas exacta y cuando se producen, el mejor enfoque es
mostrar el video como si se estuviera ejecutando a 60 fps. Si la velocidad de fotogramas es
menor, entonces w simplemente renderiza el mismo fotograma dos veces. Si la velocidad de
fotogramas es mayor, no hay nada que se pueda hacer para ver los fotogramas adicionales, por
lo que simplemente los ignoramos.
El elemento de video es solo un elemento de imagen y puede dibujarse como cualquier imagen,
puede escalar, rotar, panoramizar el video, reflejarlo, desvanecerlo, recortarlo y mostrar solo
partes, dibujarlo dos veces la segunda vez con un modo compuesto global Para añadir efectos
como lighten, screen, etc.
function updateCanvas(){
ctx.clearRect(0,0,canvas.width,canvas.height); // Though not always needed
// you may get bad pixels from
// previous videos so clear to be
// safe
// only draw if loaded and ready
if(videoContainer !== undefined && videoContainer.ready){
// find the top left of the video on the canvas
var scale = videoContainer.scale;
var vidH = videoContainer.video.videoHeight;
var vidW = videoContainer.video.videoWidth;
var top = canvas.height / 2 - (vidH /2 ) * scale;
var left = canvas.width / 2 - (vidW /2 ) * scale;
// now just draw the video the correct size
ctx.drawImage(videoContainer.video, left, top, vidW * scale, vidH * scale);
if(videoContainer.video.paused){ // if not playing show the paused screen
drawPayIcon();
}
}
// all done for display
// request the next frame in 1/60th of a second
requestAnimationFrame(updateCanvas);
}
function drawPayIcon(){
ctx.fillStyle = "black"; // darken display
ctx.globalAlpha = 0.5;
ctx.fillRect(0,0,canvas.width,canvas.height);
ctx.fillStyle = "#DDD"; // colour of play icon
ctx.globalAlpha = 0.75; // partly transparent
https://riptutorial.com/es/home 74
ctx.beginPath(); // create the path for the icon
var size = (canvas.height / 2) * 0.5; // the size of the icon
ctx.moveTo(canvas.width/2 + size/2, canvas.height / 2); // start at the pointy end
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.fill();
ctx.globalAlpha = 1; // restore alpha
}
function playPauseClick(){
if(videoContainer !== undefined && videoContainer.ready){
if(videoContainer.video.paused){
videoContainer.video.play();
}else{
videoContainer.video.pause();
}
}
}
// register the event
canvas.addEventListener("click",playPauseClick);
Resumen
Reproducir un video es muy fácil usando el lienzo, agregar efecto en tiempo real también es fácil.
Sin embargo, existen algunas limitaciones en cuanto a los formatos, cómo puedes 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 utilizar ctx.getImageData para acceder a
los píxeles que contiene. O puede usar canvas.toDataURL para tomar una canvas.toDataURL y
descargarla. (Solo si el video es de una fuente confiable y no mancha el lienzo).
Tenga en cuenta que si el video tiene sonido, si lo reproduce, también reproducirá el sonido.
Feliz videoing
Crear un video de WebM desde marcos de lienzo y reproducir en lienzo, o cargar o descargar.
name = "CanvasCapture"; // Placed into the Mux and Write Application Name fields of the WebM
header
quality = 0.7; // good quality 1 Best < 0.7 ok to poor
fps = 30; // I have tried all sorts of frame rates and all seem to work
// Do some test to workout what your machine can handle as there
// is a lot of variation between machines.
var video = new Groover.Video(fps,quality,name)
https://riptutorial.com/es/home 75
function capture(){
if(video.timecode < 5000){ // 5 seconds
setTimeout(capture,video.frameDelay);
}else{
var videoElement = document.createElement("video");
videoElement.src = URL.createObjectURL(video.toBlob());
document.body.appendChild(videoElement);
video = undefined; // DeReference as it is memory hungry.
return;
}
// first frame sets the video size
video.addFrame(canvas); // Add current canvas frame
}
capture(); // start capture
En lugar de hacer un gran esfuerzo solo para ser rechazado, esta es una inserción rápida para
ver si es aceptable. Dará los detalles completos si es aceptado. También incluye opciones de
captura adicionales para obtener mejores tasas de captura de HD (eliminada de esta versión,
puede capturar HD 1080 a 50 fps en buenas máquinas).
Esto fue inspirado por Wammy pero es una completa reescritura con codificación a medida que
avanza la metodología, reduciendo en gran medida la memoria requerida durante la captura.
Puede capturar más de 30 segundos mejores datos, manejando algoritmos.
Los cuadros de notas están codificados en imágenes webP. Solo Chrome soporta
codificación webP canvas. Para otros navegadores (Firefox y Edge), deberá utilizar un
codificador webP de terceros, como Libwebp. 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
formato si es aceptado).
https://riptutorial.com/es/home 76
const dataTypes = {
object : function(data){ return toBlob(data);},
number : function(data){ return stream.num(data);},
string : function(data){ return stream.str(data);},
array : function(data){ return data;},
double2Str : function(num){
var c = new Uint8Array((new Float64Array([num])).buffer);
return S(c[7]) + S(c[6]) + S(c[5]) + S(c[4]) + S(c[3]) + S(c[2]) + S(c[1]) +
S(c[0]);
}
};
const stream = {
num : function(num){ // writes int
var parts = [];
while(num > 0){ parts.push(num & 0xff); num = num >> 8; }
return new Uint8Array(parts.reverse());
},
str : function(str){ // writes string
var i, len, arr;
len = str.length;
arr = new Uint8Array(len);
for(i = 0; i < len; i++){arr[i] = str.charCodeAt(i);}
return arr;
},
compInt : function(num){ // could not find full details so bit of a guess
if(num < 128){ // number is prefixed with a bit (1000 is on byte 0100 two,
0010 three and so on)
num += 0x80;
return new Uint8Array([num]);
}else
if(num < 0x4000){
num += 0x4000;
return new Uint8Array([num>>8, num])
}else
if(num < 0x200000){
num += 0x200000;
return new Uint8Array([num>>16, num>>8, num])
}else
if(num < 0x10000000){
num += 0x10000000;
return new Uint8Array([num>>24, num>>16, num>>8, num])
}
}
}
const ids = { // header names and values
videoData : 0x1a45dfa3,
Version : 0x4286,
ReadVersion : 0x42f7,
MaxIDLength : 0x42f2,
MaxSizeLength : 0x42f3,
DocType : 0x4282,
DocTypeVersion : 0x4287,
DocTypeReadVersion : 0x4285,
Segment : 0x18538067,
Info : 0x1549a966,
TimecodeScale : 0x2ad7b1,
MuxingApp : 0x4d80,
WritingApp : 0x5741,
Duration : 0x4489,
Tracks : 0x1654ae6b,
https://riptutorial.com/es/home 77
TrackEntry : 0xae,
TrackNumber : 0xd7,
TrackUID : 0x63c5,
FlagLacing : 0x9c,
Language : 0x22b59c,
CodecID : 0x86,
CodecName : 0x258688,
TrackType : 0x83,
Video : 0xe0,
PixelWidth : 0xb0,
PixelHeight : 0xba,
Cluster : 0x1f43b675,
Timecode : 0xe7,
Frame : 0xa3,
Keyframe : 0x9d012a,
FrameBlock : 0x81,
};
const keyframeD64Header = '\x9d\x01\x2a'; //VP8 keyframe header 0x9d012a
const videoDataPos = 1; // data pos of frame data header
const defaultDelay = dataTypes.double2Str(1000/25);
const header = [ // structure of webM header/chunks what ever they are called.
ids.videoData,[
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, // always o
ids.Language, 'und', // undefined I think this means
ids.CodecID, 'V_VP8', // These I think must not change
ids.CodecName, 'VP8', // These I think must not change
ids.TrackType, 1,
ids.Video, [
ids.PixelWidth, 0,
ids.PixelHeight, 0
]
]
]
]
];
function getHeader(){
header[3][2][3] = name;
header[3][2][5] = name;
header[3][2][7] = dataTypes.double2Str(frameDelay);
header[3][3][1][15][1] = width;
header[3][3][1][15][3] = height;
function create(dat){
https://riptutorial.com/es/home 78
var i,kv,data;
data = [];
for(i = 0; i < dat.length; i += 2){
kv = {i : dat[i]};
if(Array.isArray(dat[i + 1])){
kv.d = create(dat[i + 1]);
}else{
kv.d = dat[i + 1];
}
data.push(kv);
}
return data;
}
return create(header);
}
function addCluster(){
webmData[videoDataPos].d.push({ i: ids.Cluster,d: [{ i: ids.Timecode, d:
Math.round(clusterTimecode)}]}); // Fixed bug with Round
clusterCounter = 0;
}
function addFrame(frame){
var VP8, kfS,riff;
riff = getWebPChunks(atob(frame.toDataURL(frameMimeType, quality).slice(23)));
VP8 = riff.RIFF[0].WEBP[0];
kfS = VP8.indexOf(keyframeD64Header) + 3;
frame = {
width: ((VP8.charCodeAt(kfS + 1) << 8) | VP8.charCodeAt(kfS)) & 0x3FFF,
height: ((VP8.charCodeAt(kfS + 3) << 8) | VP8.charCodeAt(kfS + 2)) & 0x3FFF,
data: VP8,
riff: riff
};
if(clusterCounter > CLUSTER_MAX_DURATION){
addCluster();
}
webmData[videoDataPos].d[webmData[videoDataPos].d.length-1].d.push({
i: ids.Frame,
d: S(ids.FrameBlock) + S( Math.round(clusterCounter) >> 8) + S(
Math.round(clusterCounter) & 0xff) + S(128) + frame.data.slice(4),
});
clusterCounter += frameDelay;
clusterTimecode += frameDelay;
webmData[videoDataPos].d[0].d[3].d = dataTypes.double2Str(clusterTimecode);
}
function startEncoding(){
frameNumber = clusterCounter = clusterTimecode = 0;
webmData = getHeader();
addCluster();
}
function toBlob(vidData){
var data,i,vData, len;
vData = [];
for(i = 0; i < vidData.length; i++){
data = dataTypes[typeof vidData[i].d](vidData[i].d);
len = data.size || data.byteLength || data.length;
vData.push(stream.num(vidData[i].i));
vData.push(stream.compInt(len));
vData.push(data)
}
return new Blob(vData, {type: videoMimeType});
}
function getWebPChunks(str){
https://riptutorial.com/es/home 79
var offset, chunks, id, len, data;
offset = 0;
chunks = {};
while (offset < str.length) {
id = str.substr(offset, 4);
// value will have top bit on (bit 32) so not simply a bitwise operation
// Warning little endian (Will not work on big endian systems)
len = new Uint32Array(
new Uint8Array([
str.charCodeAt(offset + 7),
str.charCodeAt(offset + 6),
str.charCodeAt(offset + 5),
str.charCodeAt(offset + 4)
]).buffer)[0];
id = str.substr(offset, 4);
chunks[id] = chunks[id] === undefined ? [] : chunks[id];
if (id === 'RIFF' || id === 'LIST') {
chunks[id].push(getWebPChunks(str.substr(offset + 8, len)));
offset += 8 + len;
} else if (id === 'WEBP') {
chunks[id].push(str.substr(offset + 8));
break;
} else {
chunks[id].push(str.substr(offset + 4));
break;
}
}
return chunks;
}
function Encoder(fps, _quality = 0.8, _name = "Groover"){
this.fps = fps;
this.quality = quality = _quality;
this.frameDelay = frameDelay = 1000 / fps;
this.frame = 0;
this.width = width = null;
this.timecode = 0;
this.name = name = _name;
}
Encoder.prototype = {
addFrame : function(frame){
if('canvas' in frame){
frame = frame.canvas;
}
if(width === null){
this.width = width = frame.width,
this.height = height = frame.height
startEncoding();
}else
if(width !== frame.width || height !== frame.height){
throw RangeError("Frame size error. Frames must be the same size.");
}
addFrame(frame);
this.frame += 1;
this.timecode = clusterTimecode;
},
toBlob : function(){
return toBlob(webmData);
}
}
return {
Video: Encoder,
https://riptutorial.com/es/home 80
}
})()
https://riptutorial.com/es/home 81
Capítulo 12: Manipulación de píxeles con
"getImageData" y "putImageData"
Examples
Introducción a "context.getImageData"
Html5 Canvas le brinda la posibilidad de obtener y cambiar el color de cualquier píxel en el lienzo.
• Cree un selector de color para una imagen o seleccione un color en una rueda de color.
• Cree filtros de imagen complejos, como desenfoque y detección de bordes.
• Vuelva a colorear cualquier parte de una imagen a nivel de píxel (si usa HSL, puede incluso
cambiar el color de una imagen mientras conserva la Iluminación y la Saturación
importantes para que el resultado no se vea como si alguien hubiera pintado la imagen).
Nota: Canvas ahora tiene Blend Compositing que también puede recolorear una imagen en
algunos casos.
• "Knockout" el fondo alrededor de una persona / elemento en una imagen,
• Cree una herramienta de cubeta de pintura para detectar y rellenar parte de una imagen (p.
Ej., Cambie el color de un pétalo de flor con el clic del usuario de verde a amarillo).
• Examine una imagen para el contenido (por ejemplo, reconocimiento facial).
Problemas comunes:
• Por razones de seguridad, getImageData está deshabilitado si ha dibujado una imagen que se
origina en un dominio diferente al de la propia página web.
• getImageData es un método relativamente caro 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 mixta que puede realizar la misma manipulación de píxeles que hace
getImageData .
• Para imágenes .png, es posible que getImageData no informe exactamente los mismos
colores que en el archivo .png original porque el navegador puede realizar correcciones
gamma y multiplicación previa alfa cuando se dibujan imágenes en el lienzo.
Use getImageData para obtener los colores de píxeles para todo o parte del contenido de su lienzo.
El objeto imageData tiene una propiedad .data que contiene la información de color de píxel.
La propiedad de data es un Uint8ClampedArray contiene los datos de color rojo, verde, azul y alfa
(opacidad) para todos los píxeles solicitados.
https://riptutorial.com/es/home 82
// determine which pixels to fetch (this fetches all pixels on the canvas)
var x=0;
var y=0;
var width=canvas.width;
var height=canvas.height;
// Pull the pixel color data array from the imageData object
var pixelDataArray = imageData.data;
Puede obtener la posición de cualquier píxel [x, y] dentro de data matriz de data esta manera:
Y luego puedes obtener los valores de rojo, verde, azul y alfa de ese píxel de esta manera:
https://riptutorial.com/es/home 83
Lea Manipulación de píxeles con "getImageData" y "putImageData" en línea:
https://riptutorial.com/es/html5-canvas/topic/5573/manipulacion-de-pixeles-con--getimagedata--y--
putimagedata-
https://riptutorial.com/es/home 84
Capítulo 13: Navegando por un sendero
Examples
Encontrando puntos a lo largo de una curva Bezier cúbica.
Este ejemplo encuentra una matriz de puntos aproximadamente espaciados a lo largo de una
curva Bezier cúbica.
// Return: an array of approximately evenly spaced points along a cubic Bezier curve
//
// Attribution: Stackoverflow's @Blindman67
// Cite: http://stackoverflow.com/questions/36637211/drawing-a-curved-line-in-css-or-canvas-
and-moving-circle-along-it/36827074#36827074
// As modified from the above citation
//
// ptCount: sample this many points at interval along the curve
// pxTolerance: approximate spacing allowed between points
// Ax,Ay,Bx,By,Cx,Cy,Dx,Dy: control points defining the curve
//
function plotCBez(ptCount,pxTolerance,Ax,Ay,Bx,By,Cx,Cy,Dx,Dy){
var deltaBAx=Bx-Ax;
var deltaCBx=Cx-Bx;
var deltaDCx=Dx-Cx;
var deltaBAy=By-Ay;
var deltaCBy=Cy-By;
var deltaDCy=Dy-Cy;
var ax,ay,bx,by;
var lastX=-10000;
var lastY=-10000;
var pts=[{x:Ax,y:Ay}];
for(var i=1;i<ptCount;i++){
var t=i/ptCount;
ax=Ax+deltaBAx*t;
bx=Bx+deltaCBx*t;
cx=Cx+deltaDCx*t;
ax+=(bx-ax)*t;
bx+=(cx-bx)*t;
//
ay=Ay+deltaBAy*t;
by=By+deltaCBy*t;
cy=Cy+deltaDCy*t;
ay+=(by-ay)*t;
by+=(cy-by)*t;
var x=ax+(bx-ax)*t;
var y=ay+(by-ay)*t;
var dx=x-lastX;
var dy=y-lastY;
if(dx*dx+dy*dy>pxTolerance){
pts.push({x:x,y:y});
lastX=x;
lastY=y;
https://riptutorial.com/es/home 85
}
}
pts.push({x:Dx,y:Dy});
return(pts);
}
Este ejemplo encuentra una matriz de puntos aproximadamente espaciados a lo largo de una
curva cuadrática.
Este ejemplo encuentra una matriz de puntos aproximadamente espaciados a lo largo de una
línea.
https://riptutorial.com/es/home 86
Descompone los segmentos de ruta creados con context.lineTo en puntos a lo largo de esa línea.
Uso
https://riptutorial.com/es/home 87
// Tell the Context to plot waypoint in addition to
// drawing the path
plotPathCommands(ctx,n,tolerance);
Este código modifica los comandos de dibujo de Canvas Context para que los comandos no solo
dibujen la línea o curva, sino que también creen una matriz de puntos a lo largo de toda la ruta:
• startPath,
• mover a,
• lineTo,
• cuadrática
• bezierCurveTo.
¡Nota IMPORTANTE!
Este código modifica las funciones de dibujo reales del Contexto, por lo que cuando
stopPlottingPathCommands trazar puntos a lo largo de la ruta, debe llamar a los
stopPlottingPathCommands para devolver las funciones de dibujo del Contexto a su estado no
modificado.
https://riptutorial.com/es/home 88
Obtiene una copia de la matriz de puntos resultante utilizando la función getPathPoints provista.
Si dibuja múltiples Rutas con el Contexto modificado, la matriz de puntos contendrá un único
conjunto concatenado de puntos 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 provista.
https://riptutorial.com/es/home 89
}
ctx.stopPlottingPathCommands=function(){
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=undefined;
}
}
https://riptutorial.com/es/home 90
////////////////////////////////////////
// A Plug-in
////////////////////////////////////////
https://riptutorial.com/es/home 91
this.moveTo=this.myMoveTo;
this.lineTo=this.myLineTo;
this.quadraticCurveto=this.myQuadraticCurveTo;
this.bezierCurveTo=this.myBezierCurveTo;
this.myBeginPath=undefined;
}
}
////////////////////////////////
// Helper functions
////////////////////////////////
// Return: a set of approximately evenly spaced points along a cubic Bezier curve
//
// Attribution: Stackoverflow's @Blindman67
// Cite: http://stackoverflow.com/questions/36637211/drawing-a-curved-line-in-css-or-canvas-
and-moving-circle-along-it/36827074#36827074
// As modified from the above citation
//
// ptCount: sample this many points at interval along the curve
// pxTolerance: approximate spacing allowed between points
// Ax,Ay,Bx,By,Cx,Cy,Dx,Dy: control points defining the curve
//
function plotCBez(ptCount,pxTolerance,Ax,Ay,Bx,By,Cx,Cy,Dx,Dy){
var deltaBAx=Bx-Ax;
var deltaCBx=Cx-Bx;
var deltaDCx=Dx-Cx;
var deltaBAy=By-Ay;
var deltaCBy=Cy-By;
var deltaDCy=Dy-Cy;
var ax,ay,bx,by;
var lastX=-10000;
var lastY=-10000;
var pts=[{x:Ax,y:Ay}];
for(var i=1;i<ptCount;i++){
var t=i/ptCount;
ax=Ax+deltaBAx*t;
bx=Bx+deltaCBx*t;
cx=Cx+deltaDCx*t;
ax+=(bx-ax)*t;
bx+=(cx-bx)*t;
//
ay=Ay+deltaBAy*t;
by=By+deltaCBy*t;
cy=Cy+deltaDCy*t;
ay+=(by-ay)*t;
by+=(cy-by)*t;
var x=ax+(bx-ax)*t;
var y=ay+(by-ay)*t;
var dx=x-lastX;
var dy=y-lastY;
if(dx*dx+dy*dy>pxTolerance){
pts.push({x:x,y:y});
lastX=x;
lastY=y;
}
}
pts.push({x:Dx,y:Dy});
return(pts);
}
https://riptutorial.com/es/home 92
// Return: an array of approximately evenly spaced points along a Quadratic curve
//
// Attribution: Stackoverflow's @Blindman67
// Cite: http://stackoverflow.com/questions/36637211/drawing-a-curved-line-in-css-or-canvas-
and-moving-circle-along-it/36827074#36827074
// As modified from the above citation
//
// ptCount: sample this many points at interval along the curve
// pxTolerance: approximate spacing allowed between points
// Ax,Ay,Bx,By,Cx,Cy: control points defining the curve
//
function plotQBez(ptCount,pxTolerance,Ax,Ay,Bx,By,Cx,Cy){
var deltaBAx=Bx-Ax;
var deltaCBx=Cx-Bx;
var deltaBAy=By-Ay;
var deltaCBy=Cy-By;
var ax,ay;
var lastX=-10000;
var lastY=-10000;
var pts=[{x:Ax,y:Ay}];
for(var i=1;i<ptCount;i++){
var t=i/ptCount;
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-lastX;
var dy=y-lastY;
if(dx*dx+dy*dy>pxTolerance){
pts.push({x:x,y:y});
lastX=x;
lastY=y;
}
}
pts.push({x:Cx,y:Cy});
return(pts);
}
https://riptutorial.com/es/home 93
}
}
pts.push({x:Bx,y:By});
return(pts);
}
Dados los 3 puntos de una curva cuadrática, la siguiente función devuelve la longitud.
function 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 * Math.sqrt(c1);
a1 = 2 * a * (u = Math.sqrt(a));
u1 = b / u;
a = 4 * c * a - b * b;
c = 2 * Math.sqrt(c);
return (a1 * c1 + u * b * (c1 - c) + a * Math.log((2 * u + u1 + c1) / (u1 + c))) / (4 *
a1);
}
La función splitCurveAt divide la curva en la position 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 x4 . Si no está undefined o es null , se asume que la curva es cúbica, de lo contrario la
curva es una acción cuadrática.
Ejemplo de uso
var p1 = {x : 10 , y : 100};
var p2 = {x : 100, y : 200};
var p3 = {x : 200, y : 0};
var newCurves = splitCurveAt(0.5, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y)
var i = 0;
var p = newCurves
// Draw the 2 new curves
https://riptutorial.com/es/home 94
// Assumes ctx is canvas 2d context
ctx.lineWidth = 1;
ctx.strokeStyle = "black";
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 p1 = {x : 10 , y : 100};
var p2 = {x : 100, y : 200};
var p3 = {x : 200, y : 0};
var p4 = {x : 300, y : 100};
var newCurves = splitCurveAt(0.5, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y)
var i = 0;
var p = newCurves
// Draw the 2 new curves
// Assumes ctx is canvas 2d context
ctx.lineWidth = 1;
ctx.strokeStyle = "black";
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 funcion dividida
splitCurveAt = función (posición, x1, y1, x2, y2, x3, y3, [x4, y4])
Nota: La función tiene algún código /* */ comentado opcional que se ocupa de los
casos de borde en los que las curvas resultantes pueden tener una longitud cero, o
quedar fuera del inicio o final de la curva original. Al intentar dividir una curva fuera del
rango válido para la position >= 0 o la position >= 1 producirá un error de rango. Esto
se puede eliminar y funcionará bien, aunque es posible que tenga curvas resultantes
que tengan una longitud cero.
//
=============================================================================================
// you may remove this as the function will still work and resulting curves will still
render
https://riptutorial.com/es/home 95
// but other curve functions may not like curves with 0 length
//
=============================================================================================
if(position <= 0 || position >= 1){
throw RangeError("spliteCurveAt requires position > 0 && position < 1");
}
//
=============================================================================================
// If you remove the above range error you may use one or both of the following commented
sections
// Splitting curves position < 0 or position > 1 will still create valid curves but they
will
// extend past the end points
//
=============================================================================================
// Lock the position to split on the curve.
/* optional A
position = position < 0 ? 0 : position > 1 ? 1 : position;
optional A end */
//
=============================================================================================
// the next commented section will return the original curve if the split results in 0
length curve
// You may wish to uncomment this If you desire such functionality
/* optional B
if(position <= 0 || position >= 1){
if(x4 === undefined || x4 === null){
return [x1, y1, x2, y2, x3, y3];
}else{
return [x1, y1, x2, y2, x3, y3, x4, y4];
}
}
optional B end */
https://riptutorial.com/es/home 96
retPoints[i++] = v1.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 x4 . Si no está undefined o es null , se
asume que la curva es cúbica, de lo contrario la curva es una acción 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
https://riptutorial.com/es/home 97
Recorte de una curva cuadrática.
var p1 = {x : 10 , y : 100};
var p2 = {x : 100, y : 200};
var p3 = {x : 200, y : 0};
var newCurve = splitCurveAt(0.25, 0.75, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y)
var i = 0;
var p = newCurve
// Draw the trimmed curve
// Assumes ctx is canvas 2d context
ctx.lineWidth = 1;
ctx.strokeStyle = "black";
ctx.beginPath();
ctx.moveTo(p[i++],p[i++]);
ctx.quadraticCurveTo(p[i++], p[i++], p[i++], p[i++]);
ctx.stroke();
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 newCurve = splitCurveAt(0.25, 0.75, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y)
var i = 0;
var p = newCurve
// Draw the trimmed curve
// Assumes ctx is canvas 2d context
ctx.lineWidth = 1;
ctx.strokeStyle = "black";
ctx.beginPath();
ctx.moveTo(p[i++],p[i++]);
ctx.bezierCurveTo(p[i++], p[i++], p[i++], p[i++], p[i++], p[i++]);
ctx.stroke();
Ejemplo de función
trimBezier = function (fromPos, toPos, x1, y1, x2, y2, x3, y3, [x4, y4])
Nota: Esta función requiere la función en el 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;
quad = false;
if(x4 === undefined || x4 === null){
quad = true; // this is a quadratic bezier
}
if(fromPos > toPos){ // swap is from is after to
i = fromPos;
fromPos = toPos
https://riptutorial.com/es/home 98
toPos = i;
}
// clamp to on the curve
toPos = toPos <= 0 ? 0 : toPos >= 1 ? 1 : toPos;
fromPos = fromPos <= 0 ? 0 : fromPos >= 1 ? 1 : fromPos;
if(toPos === fromPos){
s = splitBezierAt(toPos, x1, y1, x2, y2, x3, y3, x4, y4);
i = quad ? 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]);
}
return retBez;
}
if(toPos === 1 && fromPos === 0){ // no trimming required
retBez = [x1, y1, x2, y2, x3, y3]; // return original bezier
if(!quad){
retBez.push(x4, y4);
}
return retBez;
}
if(fromPos === 0){
if(toPos < 1){
s = splitBezierAt(toPos, x1, y1, x2, y2, x3, y3, x4, y4);
i = 0;
retBez = [s[i++], s[i++], s[i++], s[i++], s[i++], s[i++]];
if(!quad){
retBez.push(s[i++], s[i++]);
}
}
return retBez;
}
if(toPos === 1){
if(fromPos < 1){
s = splitBezierAt(toPos, x1, y1, x2, y2, x3, y3, x4, y4);
i = quad ? 4 : 6;
retBez = [s[i++], s[i++], s[i++], s[i++], s[i++], s[i++]];
if(!quad){
retBez.push(s[i++], s[i++]);
}
}
return retBez;
}
s = splitBezierAt(fromPos, x1, y1, x2, y2, x3, y3, x4, y4);
if(quad){
i = 4;
toPos = (toPos - fromPos) / (1 - fromPos);
s = splitBezierAt(toPos, s[i++], s[i++], s[i++], s[i++], s[i++], s[i++]);
i = 0;
retBez = [s[i++], s[i++], s[i++], s[i++], s[i++], s[i++]];
return retBez;
}
i = 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++]);
i = 0;
retBez = [s[i++], s[i++], s[i++], s[i++], s[i++], s[i++], s[i++], s[i++]];
return retBez;
}
https://riptutorial.com/es/home 99
Longitud de una curva de Bezier cúbica (una aproximación cercana)
Dados los 4 puntos de una curva Bezier cúbica, la siguiente función devuelve su longitud.
Método: La longitud de una curva Bezier cúbica no tiene un cálculo matemático directo. Este
método de "fuerza bruta" encuentra una muestra de puntos a lo largo de la curva y calcula la
distancia total que abarcan esos puntos.
https://riptutorial.com/es/home 100
ay += (by - ay) * t;
by += (cy - by) * t;
return({
x:ax +(bx - ax) * t,
y:ay +(by - ay) * t
});
}
Este ejemplo encuentra un punto en una curva bezier o cúbica en la position en la que la position
es la distancia de la unidad en la curva 0 <= position <= 1. La posición se fija al rango, por lo
tanto, si los valores <0 o> 1 se pasan, serán conjunto 0,1 respectivamente.
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 point = {x : null, y : null};
La función
getPointOnCurve = function (posición, x1, y1, x2, y2, x3, y3, [x4, y4], [vec])
Nota: x4 , y4 si es null , o undefined significa que la curva es una curva cuadrática. vec
es opcional y mantendrá el punto devuelto si se proporciona. Si no se creará.
var getPointOnCurve = function(position, x1, y1, x2, y2, x3, y3, x4, y4, vec){
https://riptutorial.com/es/home 101
var vec, quad;
quad = false;
if(vec === undefined){
vec = {};
}
Cuando necesite encontrar el rectángulo delimitador de una curva bezier cuadrática, puede
utilizar el siguiente método de ejecución.
// This method was discovered by Blindman67 and solves by first normalising the control point
thereby reducing the algorithm complexity
// x1,y1, x2,y2, x3,y3 Start, Control, and End coords of bezier
// [extent] is optional and if provided the extent will be added to it allowing you to use the
function
// to get the extent of many beziers.
// returns extent object (if not supplied a new extent is created)
https://riptutorial.com/es/home 102
// Extent object properties
// top, left,right,bottom,width,height
function getQuadraticCurevExtent(x1, y1, x2, y2, x3, y3, extent) {
var brx, bx, x, bry, by, y, px, py;
Para obtener una visión más detallada de cómo resolver para obtener más información, consulte
la respuesta Para obtener la extensión de una versión cuadrática que incluye demostraciones
ejecutables.
https://riptutorial.com/es/home 103
Capítulo 14: Oscuridad
Examples
Efecto adhesivo utilizando sombras.
Este código agrega sombras que aumentan hacia el exterior a una imagen para crear una versión
"adhesiva" de la imagen.
Notas:
var canvas=document.createElement("canvas");
var ctx=canvas.getContext("2d");
document.body.appendChild(canvas);
canvas.style.background='navy';
canvas.style.border='1px solid red;';
// Always(!) wait for your images to fully load before trying to drawImage them!
var img=new Image();
img.onload=start;
// put your img.src here...
img.src='http://i.stack.imgur.com/bXaB6.png';
function start(){
ctx.drawImage(img,20,20);
var sticker=stickerEffect(img,5);
ctx.drawImage(sticker, 150,20);
}
function stickerEffect(img,grow){
var canvas1=document.createElement("canvas");
var ctx1=canvas1.getContext("2d");
var canvas2=document.createElement("canvas");
var ctx2=canvas2.getContext("2d");
canvas1.width=canvas2.width=img.width+grow*2;
canvas1.height=canvas2.height=img.height+grow*2;
ctx1.drawImage(img,grow,grow);
ctx2.shadowColor='white';
https://riptutorial.com/es/home 104
ctx2.shadowBlur=2;
for(var i=0;i<grow;i++){
ctx2.drawImage(canvas1,0,0);
ctx1.drawImage(canvas2,0,0);
}
ctx2.shadowColor='rgba(0,0,0,0)';
ctx2.drawImage(img,grow,grow);
return(canvas2);
}
Una vez que se activa el sombreado, cada nuevo dibujo del lienzo se sombreará.
// start shadowing
context.shadowColor='black';
• Siempre que necesite la versión sombreada, dibuje esa imagen previamente sombreada
desde el lienzo en memoria al lienzo visible: context.drawImage(memoryCanvas,x,y)
var canvas=document.createElement("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
https://riptutorial.com/es/home 105
canvas.style.border='1px solid red;';
document.body.appendChild(canvas);
function cacheShadowedImage(img,shadowcolor,blur){
var c=document.createElement('canvas');
var cctx=c.getContext('2d');
c.width=img.width+blur*2+2;
c.height=img.height+blur*2+2;
cctx.shadowColor=shadowcolor;
cctx.shadowBlur=blur;
cctx.drawImage(img,blur+1,blur+1);
return(c);
}
var canvas=document.createElement("canvas");
var ctx=canvas.getContext("2d");
document.body.appendChild(canvas);
ctx.fillStyle='skyblue';
ctx.strokeStyle='lightgray';
ctx.lineWidth=5;
// without shadow
ctx.beginPath();
ctx.arc(60,60,30,0,Math.PI*2);
ctx.closePath();
ctx.fill();
ctx.stroke();
// with shadow
https://riptutorial.com/es/home 106
ctx.shadowColor='black';
ctx.shadowBlur=4;
ctx.shadowOffsetY=3;
ctx.beginPath();
ctx.arc(175,60,30,0,Math.PI*2);
ctx.closePath();
ctx.fill();
ctx.stroke();
// stop the shadowing
ctx.shadowColor='rgba(0,0,0,0)';
Sombras interiores
Para crear trazos con una sombra interior, use la composición de destination-in , lo que hace que
el contenido existente permanezca solo donde el contenido existente se superpone al contenido
nuevo. El contenido existente que no se superpone con el contenido nuevo se borra.
1. Trazar una forma con una sombra. La sombra se extenderá tanto hacia afuera como
hacia adentro desde el trazo. Debemos deshacernos de la sombra exterior, dejando solo la
sombra interior deseada.
2. Establezca la composición en destination-in que mantiene la sombra trazada existente
solo donde se superponga con cualquier dibujo nuevo.
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 es medio dentro y
medio fuera de la forma rellena, la mitad exterior del trazo también se borrará. La solución
es duplicar el context.lineWidth por lo que la mitad del trazo de doble tamaño aún está
dentro de la forma rellena.
var canvas=document.createElement("canvas");
var ctx=canvas.getContext("2d");
document.body.appendChild(canvas);
https://riptutorial.com/es/home 107
// set shadowing
ctx.shadowColor='black';
ctx.shadowBlur=10;
function defineRoundedRect(x,y,width,height,radius) {
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.lineTo(x + width - radius, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
ctx.lineTo(x + width, y + height - radius);
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
ctx.lineTo(x + radius, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
ctx.lineTo(x, y + radius);
ctx.quadraticCurveTo(x, y, x + radius, y);
ctx.closePath();
}
Para crear rellenos con una sombra interior, siga los pasos 1 a 3 anteriores, pero utilice aún
destination-over composición de destination-over , lo que hace que se dibuje nuevo contenido
bajo el contenido existente .
var canvas=document.createElement("canvas");
var ctx=canvas.getContext("2d");
document.body.appendChild(canvas);
https://riptutorial.com/es/home 108
// set shadowing
ctx.shadowColor='black';
ctx.shadowBlur=10;
// stop shadowing
ctx.shadowColor='rgba(0,0,0,0)';
function defineRoundedRect(x,y,width,height,radius) {
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.lineTo(x + width - radius, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
ctx.lineTo(x + width, y + height - radius);
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
ctx.lineTo(x + radius, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
ctx.lineTo(x, y + radius);
ctx.quadraticCurveTo(x, y, x + radius, y);
ctx.closePath();
}
Para dibujar una forma rellena con una sombra interior, pero sin trazo, puede dibujar el trazo fuera
del lienzo y usar shadowOffsetX para empujar la sombra hacia atrás en el lienzo.
var canvas=document.createElement("canvas");
var ctx=canvas.getContext("2d");
document.body.appendChild(canvas);
https://riptutorial.com/es/home 109
// set shadowing
ctx.shadowColor='black';
ctx.shadowBlur=10;
ctx.shadowOffsetX=500;
// stop shadowing
ctx.shadowColor='rgba(0,0,0,0)';
function defineRoundedRect(x,y,width,height,radius) {
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.lineTo(x + width - radius, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
ctx.lineTo(x + width, y + height - radius);
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
ctx.lineTo(x + radius, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
ctx.lineTo(x, y + radius);
ctx.quadraticCurveTo(x, y, x + radius, y);
ctx.closePath();
}
https://riptutorial.com/es/home 110
Capítulo 15: Poligonos
Examples
Estrellas
// Usage:
drawStar(75,75,5,50,25,'mediumseagreen','gray',9);
drawStar(150,200,8,50,25,'skyblue','gray',3);
drawStar(225,75,16,50,20,'coral','transparent',0);
drawStar(300,200,16,50,40,'gold','gray',3);
https://riptutorial.com/es/home 111
Polígono regular
// Usage:
drawRegularPolygon(3,25,75,50,6,'gray','red',0);
drawRegularPolygon(5,25,150,50,6,'gray','gold',0);
drawRegularPolygon(6,25,225,50,6,'gray','lightblue',0);
drawRegularPolygon(10,25,300,50,6,'gray','lightgreen',0);
function
drawRegularPolygon(sideCount,radius,centerX,centerY,strokeWidth,strokeColor,fillColor,rotationRadians){
var angles=Math.PI*2/sideCount;
ctx.translate(centerX,centerY);
ctx.rotate(rotationRadians);
ctx.beginPath();
ctx.moveTo(radius,0);
for(var i=1;i<sideCount;i++){
ctx.rotate(angles);
ctx.lineTo(radius,0);
}
ctx.closePath();
ctx.fillStyle=fillColor;
ctx.strokeStyle = strokeColor;
ctx.lineWidth = strokeWidth;
ctx.stroke();
ctx.fill();
ctx.rotate(angles*-(sideCount-1));
ctx.rotate(-rotationRadians);
ctx.translate(-centerX,-centerY);
}
https://riptutorial.com/es/home 112
Ejemplo de uso
var triangle = [
{ x: 200, y : 50 },
{ x: 300, y : 200 },
{ x: 100, y : 200 }
];
var cornerRadius = 30;
ctx.lineWidth = 4;
ctx.fillStyle = "Green";
ctx.strokeStyle = "black";
ctx.beginPath(); // start a new path
roundedPoly(triangle, cornerRadius);
ctx.fill();
ctx.stroke();
Función de render
https://riptutorial.com/es/home 113
asVec(p2, p1, v1); // vec back from corner point
asVec(p2, p3, v2); // vec forward from corner point
// get corners cross product (asin of angle)
sinA = v1.nx * v2.ny - v1.ny * v2.nx; // cross product
// get cross product of first line and perpendicular second line
sinA90 = v1.nx * v2.nx - v1.ny * -v2.ny; // cross product to normal of line 2
angle = Math.asin(sinA); // get the angle
radDirection = 1; // may need to reverse the radius
drawDirection = false; // may need to draw the arc anticlockwise
// find the correct quadrant for circle center
if (sinA90 < 0) {
if (angle < 0) {
angle = Math.PI + angle; // add 180 to move us to the 3 quadrant
} else {
angle = Math.PI - angle; // move back into the 2nd quadrant
radDirection = -1;
drawDirection = true;
}
} else {
if (angle > 0) {
radDirection = -1;
drawDirection = true;
}
}
halfAngle = angle / 2;
// get distance from corner to point where round corner touches line
lenOut = Math.abs(Math.cos(halfAngle) * radius / Math.sin(halfAngle));
if (lenOut > Math.min(v1.len / 2, v2.len / 2)) { // fix if longer than half line
length
lenOut = Math.min(v1.len / 2, v2.len / 2);
// ajust the radius of corner rounding to fit
cRadius = Math.abs(lenOut * Math.sin(halfAngle) / Math.cos(halfAngle));
} else {
cRadius = radius;
}
x = p2.x + v2.nx * lenOut; // move out from corner along second line to point where
rounded circle touches
y = p2.y + v2.ny * lenOut;
x += -v2.ny * cRadius * radDirection; // move away from line to circle center
y += v2.nx * cRadius * radDirection;
// x,y is the rounded corner circle center
ctx.arc(x, y, cRadius, v1.ang + Math.PI / 2 * radDirection, v2.ang - Math.PI / 2 *
radDirection, drawDirection); // draw the arc clockwise
p1 = p2;
p2 = p3;
}
ctx.closePath();
}
https://riptutorial.com/es/home 114
Capítulo 16: Ruta (solo sintaxis)
Sintaxis
• context.beginPath ()
• context.moveTo (startX, startY)
• context.lineTo (endX, endY)
• context.arc (centerX, centerY, radio, startingRadianAngle, endingRadianAngle)
• context.quadraticCurveTo (controlX, controlY, endX, endY)
• context.bezierCurveTo (controlX1, controlY1, controlX2, controlY2, endX, endY)
• context.arcTo (pointX1, pointY1, pointX2, pointY2, radio)
• context.rect (leftX, topY, width, height);
• context.closePath ()
Examples
Resumen de los comandos básicos de trazado de trayectos: líneas y curvas.
==================
TODO: vincule cada uno de los comandos de dibujo a continuación a sus ejemplos individuales.
No sé cómo hacerlo, ya que los enlaces a los ejemplos individuales apuntan hacia la carpeta
"borrador".
TODO: Agregar ejemplos para estos comandos de "acción" de ruta: trazo (), relleno (), clip ()
==================
Camino
Una ruta define un conjunto de líneas y curvas que se pueden dibujar visiblemente en el lienzo.
Un trazado no se dibuja automáticamente en el lienzo. Pero las líneas y curvas del camino se
pueden dibujar en el lienzo utilizando un trazo con estilo. Y la forma creada por las líneas y curvas
también se puede rellenar con un relleno estilizable.
• beginPath
• mover a
https://riptutorial.com/es/home 115
• lineTo
• arco
• cuadrática
• bezierCurveTo
• arco a
• rect
• closePath
context.beginPath()
Comienza a ensamblar un nuevo conjunto de comandos de ruta y también descarta cualquier ruta
previamente ensamblada.
El descarte es un punto importante ya menudo pasado por alto. Si no comienza una nueva ruta,
cualquier comando de ruta emitido previamente se volverá a dibujar.
También mueve el "lápiz" del 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 de inicio de la siguiente línea o curva. Esto puede hacer que se dibuje
una línea inesperada que conecta dos dibujos adyacentes. El comando context.moveTo
básicamente "toma el lápiz del dibujo" y lo coloca en una nueva coordenada para que no se
dibuje la línea de conexión automática.
lineTo
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, puedes
ensamblar 3 segmentos de línea para formar un triángulo.
arco
https://riptutorial.com/es/home 116
context.arc(centerX, centerY, radius, startingRadianAngle, endingRadianAngle)
Dibuja un arco circular dado el punto central, el radio y los ángulos de inicio y finalización. Los
ángulos se expresan como radianes. Para convertir grados en radianes, puede utilizar esta
fórmula: radians = degrees * Math.PI / 180; .
El ángulo 0 mira directamente hacia la derecha desde el centro del arco. Para dibujar un círculo
completo, puede hacer endingAngle = startingAngle + 360 grados (360 grados == Math.PI 2):
`context.arc (10,10,20,0, Math.PI 2);
Por defecto, el arco se dibuja en el sentido de las agujas del reloj. Un parámetro opcional [true |
false] indica que el arco se context.arc(10,10,20,0,Math.PI*2,true) sentido contrario a las agujas
del reloj: context.arc(10,10,20,0,Math.PI*2,true)
cuadrática
Dibuja una curva cuadrática que comienza en la ubicación actual de la pluma hasta una
coordenada final dada. Otra coordenada de control dada determina la forma (curvatura) de la
curva.
bezierCurveTo
Dibuja una curva de Bézier cúbica que comienza en la ubicación de la pluma actual hasta una
coordenada final dada. Otras 2 coordenadas de control dadas determinan la forma (curvatura) de
la curva.
arco a
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 del lápiz y se le asignan dos puntos: Punto1 y
Punto2.
Una línea que conecta la ubicación actual del lápiz y el inicio del arco se dibuja automáticamente
antes del arco.
rect
https://riptutorial.com/es/home 117
rectángulos desconectados no se conectan automáticamente por líneas.
closePath
context.closePath()
Dibuja una línea desde la ubicación actual del lápiz hasta la coordenada de la ruta de inicio.
Por ejemplo, si dibuja 2 líneas que forman 2 patas de un triángulo, closePath "cerrará" el triángulo
dibujando la tercera pata del triángulo desde el punto final de la 2a pata hasta el punto inicial de la
primera pata.
El nombre de este comando a menudo hace que sea mal entendido. context.closePath NO es un
delimitador final para context.beginPath . Nuevamente, el comando closePath dibuja una línea, no
"cierra" una beginPath .
context.lineTo(endX, endY)
Dibuja un segmento de línea desde la ubicación actual del lápiz para coordinar [endX, endY]
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
// arguments
var startX=25;
var startY=20;
var endX=125;
var endY=20;
// Draw a single line segment drawn using "moveTo" and "lineTo" commands
ctx.beginPath();
https://riptutorial.com/es/home 118
ctx.moveTo(startX,startY);
ctx.lineTo(endX,endY);
ctx.stroke();
Puede ensamblar varios comandos .lineTo para dibujar una polilínea. Por ejemplo, puedes
ensamblar 3 segmentos de línea para formar un triángulo.
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
// arguments
var topVertexX=50;
var topVertexY=20;
var rightVertexX=75;
var rightVertexY=70;
var leftVertexX=25;
var leftVertexY=70;
https://riptutorial.com/es/home 119
<body>
<canvas id="canvas" width=200 height=150></canvas>
</body>
</html>
Dibuja un arco circular dado el punto central, el radio y los ángulos de inicio y finalización. Los
ángulos se expresan como radianes. Para convertir grados en radianes, puede utilizar esta
fórmula: radians = degrees * Math.PI / 180; .
Por defecto, el arco se dibuja en el sentido de las agujas del reloj. Un parámetro opcional [true |
false] indica que el arco se context.arc(10,10,20,0,Math.PI*2,true) sentido contrario a las agujas
del reloj: context.arc(10,10,20,0,Math.PI*2,true)
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
// arguments
var centerX=50;
var centerY=50;
var radius=30;
var startingRadianAngle=Math.PI*2*; // start at 90 degrees == centerY+radius
var endingRadianAngle=Math.PI*2*.75; // end at 270 degrees (==PI*2*.75 in radians)
https://riptutorial.com/es/home 120
}); // end window.onload
</script>
</head>
<body>
<canvas id="canvas" width=200 height=150></canvas>
</body>
</html>
Para dibujar un círculo completo, puede hacer endingAngle = startingAngle + 360 grados (360
grados == Math.PI2).
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
// arguments
var centerX=50;
var centerY=50;
var radius=30;
var startingRadianAngle=0; // start at 0 degrees
var endingRadianAngle=Math.PI*2; // end at 360 degrees (==PI*2 in radians)
https://riptutorial.com/es/home 121
context.quadraticCurveTo(controlX, controlY, endingX, endingY)
Dibuja una curva cuadrática que comienza en la ubicación actual de la pluma hasta una
coordenada final dada. Otra coordenada de control dada determina la forma (curvatura) de la
curva.
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
// arguments
var startX=25;
var startY=70;
var controlX=75;
var controlY=25;
var endX=125;
var endY=70;
https://riptutorial.com/es/home 122
Dibuja una curva de Bézier cúbica que comienza en la ubicación de la pluma actual hasta una
coordenada final dada. Otras 2 coordenadas de control dadas determinan la forma (curvatura) de
la curva.
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
// arguments
var startX=25;
var startY=50;
var controlX1=75;
var controlY1=10;
var controlX2=75;
var controlY2=90;
var endX=125;
var endY=50;
https://riptutorial.com/es/home 123
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 del lápiz y se le asignan dos puntos: Punto1 y
Punto2.
Una línea que conecta la ubicación actual del lápiz y el inicio del arco se dibuja automáticamente
antes del arco.
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
// arguments
var pointX0=25;
var pointY0=80;
var pointX1=75;
var pointY1=0;
var pointX2=125;
var pointY2=80;
var radius=25;
// A circular arc drawn using the "arcTo" command. The line is automatically drawn.
ctx.beginPath();
ctx.moveTo(pointX0,pointY0);
ctx.arcTo(pointX1, pointY1, pointX2, pointY2, radius);
ctx.stroke();
https://riptutorial.com/es/home 124
Dibuja un rectángulo dado una esquina superior izquierda y un ancho y alto.
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
// arguments
var leftX=25;
var topY=25;
var width=40;
var height=25;
https://riptutorial.com/es/home 125
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
// arguments
var leftX=25;
var topY=25;
var width=40;
var height=25;
context.closePath()
Dibuja una línea desde la ubicación actual del lápiz hasta la coordenada de la ruta de inicio.
Por ejemplo, si dibuja 2 líneas que forman 2 patas de un triángulo, closePath "cerrará" el triángulo
dibujando la tercera pata del triángulo desde el punto final de la 2a pata hasta el punto inicial de la
primera pata.
Este ejemplo dibuja 2 patas de un triángulo y usa closePath para completar (¿cerrar?) El triángulo
dibujando la tercera pata. Lo que closePath realmente está haciendo es dibujar una línea desde el
https://riptutorial.com/es/home 126
punto final del segundo tramo hasta el punto inicial del primer tramo.
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
// arguments
var topVertexX=50;
var topVertexY=50;
var rightVertexX=75;
var rightVertexY=75;
var leftVertexX=25;
var leftVertexY=75;
ctx.stroke();
context.beginPath()
https://riptutorial.com/es/home 127
Comienza a ensamblar un nuevo conjunto de comandos de ruta y también descarta cualquier ruta
previamente ensamblada.
También mueve el "lápiz" del 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 previamente se volverá a dibujar.
Estas dos 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>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
https://riptutorial.com/es/home 128
</head>
<body>
<canvas id="canvas" width=200 height=150></canvas>
</body>
</html>
Pero sin un segundo beginPath , ese mismo segundo stroke() también vuelve a dibujar
incorrectamente el primer trazo.
Dado que el segundo stroke() 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 solid red; }
</style>
<script>
window.onload=(function(){
https://riptutorial.com/es/home 129
}); // end window.onload
</script>
</head>
<body>
<canvas id="canvas" width=200 height=150></canvas>
</body>
</html>
Establece el estilo de tapa de los puntos de inicio de línea y los puntos finales.
• redondeado , muestra mayúsculas redondeadas que se extienden más allá de los puntos
inicial y final de la línea.
• cuadrado , muestra casquillos cuadrados que se extienden más allá de los puntos inicial y
final de la línea.
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
// lineCap: round
ctx.lineCap='round';
drawLine(50,70,200,70);
https://riptutorial.com/es/home 130
// lineCap: square
ctx.lineCap='square';
drawLine(50,100,200,100);
• Mitre , el valor predeterminado, une segmentos de línea con una unión afilada.
• redondear , une segmentos de línea con una junta redondeada.
• Bisel , une segmentos de línea con una junta embotada.
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
https://riptutorial.com/es/home 131
<script>
window.onload=(function(){
// lineJoin: round
ctx.lineJoin='round';
drawPolyline(50,80);
// lineJoin: bevel
ctx.lineJoin='bevel';
drawPolyline(50,130);
context.strokeStyle=color
https://riptutorial.com/es/home 132
• Un color HSLA , por ejemplo context.strokeStyle='hsl(hue,saturation,lightness,alpha)'
donde el tono es un número entero 0-360 en la rueda de color y la saturación y la
luminosidad son porcentajes (0-100%) que indican la fuerza de cada componente y alfa es
un valor decimal de 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>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
https://riptutorial.com/es/home 133
gradient.addColorStop(0,'red');
gradient.addColorStop(1,'green');
ctx.strokeStyle=gradient;
ctx.lineWidth=20;
drawLine(50,110,250,110);
context.fillStyle=color
https://riptutorial.com/es/home 134
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>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
https://riptutorial.com/es/home 135
ctx.fillRect(200,50,100,50);
context.lineWidth=lineWidth
https://riptutorial.com/es/home 136
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
ctx.lineWidth=1;
drawPolyline(50,50);
ctx.lineWidth=5;
drawPolyline(50,100);
ctx.lineWidth=10;
drawPolyline(50,150);
Tanto los caminos rellenos como los trazados pueden tener una sombra.
https://riptutorial.com/es/home 137
• shadowColor indica qué color CSS se usará para crear la sombra.
• shadowBlur es la distancia sobre la cual la sombra se extiende hacia afuera desde el
camino.
• shadowOffsetX es una distancia por la cual la sombra se desplaza horizontalmente
alejándose del camino. Una distancia positiva mueve la sombra hacia la derecha, una
distancia negativa mueve la sombra hacia la izquierda.
• shadowOffsetY es una distancia por la cual la sombra se desplaza verticalmente
alejándose del camino. 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 los caminos rellenos y, por lo tanto, parte de la
sombra no será visible.
Al sombrear un trazo, tanto el interior como el exterior del trazo están sombreados. La sombra es
más oscura en el trazo y se aclara cuando la sombra se extiende hacia afuera en ambas
direcciones desde el trazo.
Después de dibujar sus sombras, es posible que desee desactivar el sombreado para dibujar más
caminos. Para desactivar el sombreado, configure el shadowColor la shadowColor en transparente.
context.shadowColor = 'rgba(0,0,0,0)';
Consideraciones de rendimiento
Las sombras (como los gradientes) requieren cálculos extensos y, por lo tanto, debe usar
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 alternativa si necesita animar rutas sombreadas es
crear previamente la ruta sombreada en un segundo "lienzo de sombras". El lienzo de la sombra
es un lienzo normal que se crea en la memoria con document.createElement , no se agrega al DOM
(solo es un lienzo provisional). Luego dibuja el lienzo de sombras en el lienzo principal. Esto es
mucho más rápido porque los cálculos de sombra no necesitan realizarse muchas veces por
segundo. Todo lo que estás haciendo es copiar un lienzo creado previamente en tu lienzo visible.
https://riptutorial.com/es/home 138
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
// shadowed stroke
ctx.shadowColor='black';
ctx.shadowBlur=6;
ctx.strokeStyle='red';
ctx.strokeRect(50,50,100,50);
// darken the shadow by stroking a second time
ctx.strokeRect(50,50,100,50);
// shadowed fill
ctx.shadowColor='black';
ctx.shadowBlur=10;
ctx.fillStyle='red';
ctx.fillRect(225,50,100,50);
// darken the shadow by stroking a second time
ctx.fillRect(225,50,100,50);
https://riptutorial.com/es/home 139
ctx.fillStyle='red';
ctx.fillRect(225,175,100,50);
Luego, el trazo () o el relleno () colorearán la ruta con los colores degradados del objeto.
1. Crea el objeto degradado en sí. Durante la creación, se define una línea en el lienzo donde
el gradiente comenzará y terminará. El objeto de gradiente se crea con var gradient =
context.createLinearGradient .
2. Luego, agregue 2 (o más) colores que conforman el degradado. Esto se hace agregando
múltiples paradas de color al objeto de gradient.addColorStop con gradient.addColorStop .
Argumentos:
• endX, endY es la coordenada del lienzo donde termina el degradado. En el punto final (y
después), el lienzo es sólidamente el color de gradientPercentPosition más alto.
https://riptutorial.com/es/home 140
• CssColor es un color CSS asignado a esta parada de color en particular.
El objeto de degradado es un objeto que puede usar (y reutilizar) para hacer que los trazos y
rellenos de su trayectoria se vuelvan de color degradado.
Las paradas de color son puntos de referencia (porcentaje) a lo largo de la línea de degradado.
En cada punto de parada de color, el degradado está completamente coloreado (== opacamente)
con su color asignado. Los puntos intermedios a lo largo de la línea de gradiente entre las
paradas de color se colorean como gradientes del color anterior y anterior.
Cuando creas un objeto de degradado, todo el lienzo se rellena "invisiblemente" con ese
degradado.
Cuando traza stroke() o fill() un camino, se revela el gradiente invisible, pero solo se revela
sobre ese camino que se está trazando o rellenando.
// create a linearGradient
var gradient=ctx.createLinearGradient(100,0,canvas.width-100,0);
gradient.addColorStop(0,'red');
gradient.addColorStop(1,'magenta');
ctx.fillStyle=gradient;
3. Pero hasta que stroke() o fill() con el degradado, no verá ninguno del degradado en el
Lienzo.
https://riptutorial.com/es/home 141
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
// Create a linearGradient
// Note: Nothing visually appears during this process
var gradient=ctx.createLinearGradient(100,0,canvas.width-100,0);
gradient.addColorStop(0,'red');
gradient.addColorStop(1,'magenta');
https://riptutorial.com/es/home 142
}); // end window.onload
</script>
</head>
<body>
<canvas id="canvas" width=400 height=150></canvas>
</body>
</html>
Crea un degradado radial reutilizable (objeto). El objeto de degradado es un objeto que puede
usar (y reutilizar) para hacer que los trazos y rellenos de su trayectoria se vuelvan de color
degradado.
Acerca de...
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. Crea el objeto degradado en sí. Durante la creación, se define una línea en el lienzo donde
el gradiente comenzará y terminará. El objeto de gradiente se crea con var gradient =
context.radialLinearGradient .
2. Luego, agregue 2 (o más) colores que conforman el degradado. Esto se hace agregando
múltiples paradas de color al objeto de gradient.addColorStop con gradient.addColorStop .
Argumentos:
https://riptutorial.com/es/home 143
• centerX2, centroY2, radio2 define un segundo círculo que está proyectando luz de
gradiente en el primer círculo.
Las paradas de color son puntos de referencia (porcentaje) a lo largo de la línea de degradado.
En cada punto de parada de color, el degradado está completamente coloreado (== opacamente)
con su color asignado. Los puntos intermedios a lo largo de la línea de gradiente entre las
paradas de color se colorean como gradientes del color anterior y anterior.
Cuando traza stroke() o fill() un camino, se revela el gradiente invisible, pero solo se revela
sobre ese camino que se está trazando o rellenando.
// create a radialGradient
var x1=150;
var y1=150;
var x2=280;
var y2=150;
var r1=100;
var r2=120;
var gradient=ctx.createRadialGradient(x1,y1,r1,x2,y2,r2);
gradient.addColorStop(0,'red');
gradient.addColorStop(1,'green');
ctx.fillStyle=gradient;
https://riptutorial.com/es/home 144
3. Pero hasta que stroke() o fill() con el degradado, no verá ninguno del degradado en el
Lienzo.
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; padding:10px; }
#canvas{border:1px solid blue; }
</style>
<script>
window.onload=(function(){
https://riptutorial.com/es/home 145
// canvas related vars
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
El W3C emite las especificaciones oficiales recomendadas que los navegadores utilizan para
construir el elemento Canvas Html5.
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) utilizando 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 se han tocado con el degradado (negro transparente).
El createRadialGradient(x0, y0, r0, x1, y1, r1) toma seis argumentos, los primeros
https://riptutorial.com/es/home 146
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 cualquiera de 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.
Argumentos:
• imageObject es una imagen que se usará como patrón. La fuente de la imagen puede ser:
https://riptutorial.com/es/home 147
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.
Cuando creas un objeto de patrón, todo el lienzo se rellena "invisiblemente" con ese patrón
(sujeto al argumento de repeat ).
Cuando traza stroke() o fill() un camino, se revela el patrón invisible, pero solo se revela sobre
ese camino que se está trazando o rellenando.
1. Comience con una imagen que desee utilizar como patrón. Importante (!): Asegúrese de que
su imagen se haya cargado completamente (usando patternimage.onload ) antes de intentar
usarla para crear su patrón.
// create a pattern
var pattern = ctx.createPattern(patternImage,'repeat');
ctx.fillStyle=pattern;
4. Pero hasta que stroke() o fill() con el patrón, no verá ninguno del patrón en el lienzo.
5. Finalmente, si trazas o rellenas una ruta utilizando el patrón, el patrón "invisible" se hace
visible en el Lienzo ... pero solo donde se dibuja la ruta.
https://riptutorial.com/es/home 148
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
context.stroke()
https://riptutorial.com/es/home 149
Hace que el perímetro de la ruta sea trazada de acuerdo con el context.strokeStyle actual.
context.strokeStyle y la ruta trazada se dibuja visualmente en el lienzo.
Pero (!) ... ¡El lienzo siempre dibuja trazos a mitad de camino hacia cualquier lado del camino
definido!
Entonces, como la línea está definida en y==5.0 Canvas quiere dibujar la línea entre y==4.5 y
y==5.5
https://riptutorial.com/es/home 150
Pero, otra vez (!) ... ¡La pantalla de la computadora no puede dibujar medio 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 dibujar una línea de 2 píxeles de
ancho de 4.0 a 6.0 . También colorea la línea más clara que el black definido. Este extraño
comportamiento de dibujo es "anti-aliasing" y ayuda a Canvas a evitar trazos que parezcan
irregulares.
https://riptutorial.com/es/home 151
Un truco de ajuste que SOLO funciona para movimientos horizontales y verticales
exactamente
Puede obtener una línea negra continua de 1 píxel especificando que la línea se dibuje en el
medio píxel:
context.moveTo(0,5.5);
context.lineto(5,5.5);
Código de ejemplo que usa context.stroke() para dibujar una ruta trazada en el lienzo:
https://riptutorial.com/es/home 152
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
ctx.beginPath();
ctx.moveTo(50,30);
ctx.lineTo(75,55);
ctx.lineTo(25,55);
ctx.lineTo(50,30);
ctx.lineWidth=2;
ctx.stroke();
context.fill()
Ejemplo de código usando context.fill() para dibujar una ruta llena en el lienzo:
https://riptutorial.com/es/home 153
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
ctx.beginPath();
ctx.moveTo(50,30);
ctx.lineTo(75,55);
ctx.lineTo(25,55);
ctx.lineTo(50,30);
ctx.fillStyle='blue';
ctx.fill();
context.clip
Limita los dibujos futuros para mostrar solo dentro de la ruta actual.
https://riptutorial.com/es/home 154
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
function start(){
// draw a triangle path
ctx.beginPath();
ctx.moveTo(75,50);
ctx.lineTo(125,100);
ctx.lineTo(25,100);
ctx.lineTo(75,50);
// draw an image
ctx.drawImage(img,0,0);
}
https://riptutorial.com/es/home 155
Capítulo 17: Texto
Examples
Dibujo de texto
Dibujar en lienzo no se limita a formas e imágenes. También puede dibujar texto al lienzo.
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 utilizar 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 solo un contorno en su lugar, utilizando el método
strokeText :
Resultado:
https://riptutorial.com/es/home 156
Sin las propiedades de formato de fuente aplicadas, el lienzo representa el texto a 10px 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.
Formato de texto
• Estilo de fuente
• variante de fuente
• peso de fuente
• tamaño de fuente / altura de linea
• Familia tipográfica
Por ejemplo:
Resultado:
https://riptutorial.com/es/home 157
• izquierda
• centrar
• Correcto
• final (igual que a la derecha)
• inicio (igual a la izquierda)
Por ejemplo:
ctx.textAlign = "center";
Native Canvas API no tiene un método para ajustar el texto en la siguiente línea cuando se
alcanza el ancho máximo deseado. Este ejemplo envuelve el texto en párrafos.
ctx.font=fontSize+" "+fontFace;
ctx.textBaseline='top';
Este ejemplo dibuja párrafos de texto en cualquier parte del lienzo que tenga píxeles opacos.
Los píxeles opacos pueden provenir de cualquier fuente: comandos de dibujo de ruta y / o
imágenes.
https://riptutorial.com/es/home 158
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; padding:10px; }
#canvas{border:1px solid red;}
</style>
<script>
window.onload=(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var fontsize=12;
var fontface='verdana';
var lineHeight=parseInt(fontsize*1.286);
var text='It was the best of times, it was the worst of times, it was the age of wisdom,
it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it
was the season of Light, it was the season of Darkness, it was the spring of hope, it was the
winter of despair, we had everything before us, we had nothing before us, we were all going
direct to Heaven, we were all going direct the other way; in short, the period was so far like
the present period, that some of its noisiest authorities insisted on its being received, for
good or for evil, in the superlative degree of comparison only.';
var words=text.split(' ');
var wordWidths=[];
ctx.font=fontsize+'px '+fontface;
for(var i=0;i<words.length;i++){ wordWidths.push(ctx.measureText(words[i]).width); }
var spaceWidth=ctx.measureText(' ').width;
var wordIndex=0
var data=[];
https://riptutorial.com/es/home 159
// Demo: draw Heart
// Note: the shape can be ANY opaque drawing -- even an image
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='red';
ctx.fill();
ctx.setTransform(1,0,0,1,0,0);
https://riptutorial.com/es/home 160
y++;
if(y>=sy+height){
y=sy;
x++;
if(x>=cw){ok=false;}
}
}
return(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.
function drawImageInsideText(canvas,x,y,img,text,font){
var c=canvas.cloneNode();
var ctx=c.getContext('2d');
ctx.font=font;
ctx.fillText(text,x,y);
ctx.globalCompositeOperation='source-atop';
ctx.drawImage(img,0,0);
canvas.getContext('2d').drawImage(c,0,0);
}
Este ejemplo muestra cómo representar texto a lo largo de un arco. Incluye cómo puedes agregar
funcionalidad a CanvasRenderingContext2D al extender su prototipo.
https://riptutorial.com/es/home 161
Representación de ejemplo
Código de ejemplo
El ejemplo agrega 3 nuevas funciones de representación de texto al prototipo de contexto 2D.
(function(){
const FILL = 0; // const to indicate filltext render
const STROKE = 1;
var renderType = FILL; // used internal to set fill or stroke text
const multiplyCurrentTransform = true; // if true Use current transform when rendering
// if false use absolute coordinates which is a
little quicker
// after render the currentTransform is restored to
default transform
https://riptutorial.com/es/home 162
// pixelAngularSize : angular width of a pixel in radians
var measure = function(ctx, text, radius){
var textWidth = ctx.measureText(text).width; // get the width of all the text
return {
width : textWidth,
angularWidth : (1 / radius) * textWidth,
pixelAngularSize : 1 / radius
};
}
https://riptutorial.com/es/home 163
case "left": // intentionally falls through to case "start"
case "start":
end = start + pA * textWidth;
}
}
https://riptutorial.com/es/home 164
// set the prototypes
CanvasRenderingContext2D.prototype.fillCircleText = fillCircleText;
CanvasRenderingContext2D.prototype.strokeCircleText = strokeCircleText;
CanvasRenderingContext2D.prototype.measureCircleText = measureCircleTextExt;
})();
Descripciones de funciones
Este ejemplo agrega 3 funciones al CanvasRenderingContext2D prototype . fillCircleText ,
strokeCircleText , y measureCircleText
CanvasRenderingContext2D.fillCircleText
(texto, x, y, radio, inicio, [final, [adelante]]);
CanvasRenderingContext2D.strokeCircleText
(texto, x, y, radio, inicio, [final, [adelante]]);
• texto: Texto para renderizar como cadena.
• x , y : Posición del centro del círculo como números.
• radio: radio del circulo en pixeles
• inicio: ángulo en radianes para comenzar.
• [final]: opcional. Si se incluye, se ignora ctx.textAlign y el texto se escala para que se
ajuste entre el inicio y el final.
• [adelante]: opción predeterminada 'verdadero'. si la dirección verdadera del texto es hacia
delante, si la dirección "falsa" es hacia atrás.
Ambas funciones utilizan textBaseline para colocar el texto verticalmente alrededor del radio. Para
obtener los mejores resultados, use ctx.TextBaseline .
Las funciones lanzarán un TypeError es cualquiera de los argumentos numéricos como NaN.
CanvasRenderingContext2D.measureCircleText
(texto, radio);
- **text:** String of text to measure.
- **radius:** radius of circle in pixels.
https://riptutorial.com/es/home 165
Devuelve un objeto que contiene varias métricas de tamaño para representar texto circular
Ejemplos de uso
https://riptutorial.com/es/home 166
ctx.stroke();
NOTA: El texto representado es solo una aproximación de 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 un texto circular.
textOnCurve (texto, desplazamiento, x1, y1, x2, y2, x3, y3, x4, y4)
Ejemplo de uso:
https://riptutorial.com/es/home 167
textOnCurve("Hello world!",50,100,100,200,200,300,100); // draws text on quadratic curve
// 50 pixels from start of curve
textOnCurve("Hello world!",50,100,100,200,200,300,100,400,200);
// draws text on cubic curve
// 50 pixels from start of curve
pos += widths[i] / 2;
}
ctx.restore();
}
La función de ayuda de la curva está diseñada para aumentar el rendimiento de los puntos de
búsqueda en el bezier.
https://riptutorial.com/es/home 168
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;
return vec;
}
function 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;
return vec;
}
function forward(dist){
var step;
helper.posAt(currentPos);
}
currentPos -= ((currentDist - dist) / step) * onePix
currentDist -= step;
helper.posAt(currentPos);
currentDist += Math.sqrt((vec.x - vec1.x) * (vec.x - vec1.x) + (vec.y - vec1.y) *
(vec.y - vec1.y));
return currentPos;
}
function tangentQ(pos){
a = (1-pos) * 2;
b = pos * 2;
vect.x = a * (x2 - x1) + b * (x3 - x2);
vect.y = a * (y2 - y1) + b * (y3 - y2);
u = Math.sqrt(vect.x * vect.x + vect.y * vect.y);
vect.x /= u;
vect.y /= u;
}
function tangentC(pos){
a = (1-pos)
b = 6 * a * pos;
https://riptutorial.com/es/home 169
a *= 3 * a;
c = 3 * pos * pos;
vect.x = -x1 * a + x2 * (a - b) + x3 * (b - c) + x4 * c;
vect.y = -y1 * a + y2 * (a - b) + y3 * (b - c) + y4 * c;
u = Math.sqrt(vect.x * vect.x + vect.y * vect.y);
vect.x /= u;
vect.y /= u;
}
var helper = {
vec : vec,
vect : vect,
forward : forward,
}
if(quad){
helper.posAt = posAtQ;
helper.tangent = tangentQ;
}else{
helper.posAt = posAtC;
helper.tangent = tangentC;
}
return helper
}
Texto justificado
Ejemplo de renderizado.
El código para representar esta imagen se encuentra en los ejemplos de uso en la parte inferior .
https://riptutorial.com/es/home 170
El ejemplo
La función como una función anónima inmediatamente invocada.
(function(){
const FILL = 0; // const to indicate filltext render
const STROKE = 1;
const MEASURE = 2;
var renderType = FILL; // used internal to set fill or stroke text
var maxSpaceSize = 3; // Multiplier for max space size. If greater then no justificatoin
applied
var minSpaceSize = 0.5; // Multiplier for minimum space size
var renderTextJustified = function(ctx,text,x,y,width){
var words, wordsWidth, count, spaces, spaceWidth, adjSpace, renderer, i, textAlign,
useSize, totalWidth;
textAlign = ctx.textAlign; // get current align settings
ctx.textAlign = "left";
wordsWidth = 0;
words = text.split(" ").map(word => {
var w = ctx.measureText(word).width;
wordsWidth += w;
return {
width : w,
word : word,
};
});
// count = num words, spaces = number spaces, spaceWidth normal space size
// adjSpace new space size >= min size. useSize Resulting space size used to render
count = words.length;
spaces = count - 1;
spaceWidth = ctx.measureText(" ").width;
adjSpace = Math.max(spaceWidth * minSpaceSize, (width - wordsWidth) / spaces);
useSize = adjSpace > spaceWidth * maxSpaceSize ? spaceWidth : adjSpace;
totalWidth = wordsWidth + useSize * spaces
if(renderType === MEASURE){ // if measuring return size
ctx.textAlign = textAlign;
return totalWidth;
}
renderer = renderType === FILL ? ctx.fillText.bind(ctx) : ctx.strokeText.bind(ctx); //
fill or stroke
switch(textAlign){
case "right":
x -= totalWidth;
break;
case "end":
x += width - totalWidth;
break;
case "center": // intentional fall through to default
x -= totalWidth / 2;
default:
}
if(useSize === spaceWidth){ // if space size unchanged
renderer(text,x,y);
} else {
for(i = 0; i < count; i += 1){
renderer(words[i].word,x,y);
x += words[i].width;
x += useSize;
https://riptutorial.com/es/home 171
}
}
ctx.textAlign = textAlign;
}
// Parse vet and set settings object.
var justifiedTextSettings = function(settings){
var min,max;
var vetNumber = (num, defaultNum) => {
num = num !== null && num !== null && !isNaN(num) ? num : defaultNum;
if(num < 0){
num = defaultNum;
}
return num;
}
if(settings === undefined || settings === null){
return;
}
max = vetNumber(settings.maxSpaceSize, maxSpaceSize);
min = vetNumber(settings.minSpaceSize, minSpaceSize);
if(min > max){
return;
}
minSpaceSize = min;
maxSpaceSize = max;
}
// define fill text
var fillJustifyText = function(text, x, y, width, settings){
justifiedTextSettings(settings);
renderType = FILL;
renderTextJustified(this, text, x, y, width);
}
// define stroke text
var strokeJustifyText = function(text, x, y, width, settings){
justifiedTextSettings(settings);
renderType = STROKE;
renderTextJustified(this, text, x, y, width);
}
// define measure text
var measureJustifiedText = function(text, width, settings){
justifiedTextSettings(settings);
renderType = MEASURE;
return renderTextJustified(this, text, 0, 0, width);
}
// code point A
// set the prototypes
CanvasRenderingContext2D.prototype.fillJustifyText = fillJustifyText;
CanvasRenderingContext2D.prototype.strokeJustifyText = strokeJustifyText;
CanvasRenderingContext2D.prototype.measureJustifiedText = measureJustifiedText;
// code point B
https://riptutorial.com/es/home 172
renderTextJustified(ctx, text, x, y, width);
},
measure : function(ctx, text, width, settings){
justifiedTextSettings(settings);
renderType = MEASURE;
return renderTextJustified(ctx, text, 0, 0, width);
}
}
to here*/
})();
Cómo utilizar
Se agregan tres funciones a CanvasRenderingContext2D y están disponibles para todos los objetos
de contexto 2D creados.
Función de texto de relleno y trazo Texto de relleno o trazo y use los mismos argumentos.
measureJustifiedText devolverá el ancho real al que se representaría el texto. Esto puede ser
igual, menor o mayor que el width del argumento dependiendo de la configuración actual.
Argumentos de función
• texto: cadena que contiene el texto que se va a representar.
• 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), se utilizará el espaciado normal y el texto no llenará el ancho
requerido. Si el espaciado es menor que minSpaceSize (predeterminado = 0.5) el espaciado
normal de tiempo, entonces se usa el tamaño de espacio mínimo y el texto sobrepasará el
ancho solicitado
Tanto el mínimo como el máximo son los tamaños mínimo y máximo para el carácter [espacio]
https://riptutorial.com/es/home 173
que separa las palabras. El valor predeterminado de maxSpaceSize = 6 significa que cuando el
espacio entre los caracteres es> 63 * ctx.measureText ("") .width el texto no se justificará. Si el
texto que se va a justificar tiene espacios menores que minSpaceSize = 0.5 (valor predeterminado
0.5) * ctx.measureText(" ").width el espaciado se establecerá en minSpaceSize * ctx.measureText("
").width y el texto resultante se rebasará El ancho justificante.
Se aplican las siguientes reglas, min y max deben ser números. Si no, entonces los valores
asociados no serán cambiados. Si minSpaceSize es más grande que maxSpaceSize ambas
configuraciones de entrada no son válidas y el máximo máximo no se cambiará.
settings = {
maxSpaceSize : 6; // Multiplier for max space size.
minSpaceSize : 0.5; // Multiplier for minimum space size
};
Nota: las configuraciones (tamaño de espacio mínimo y máximo) son globales para
todos los objetos de contexto 2D.
Ejemplos de uso
var i = 0;
text[i++] = "This text is aligned from the left of the canvas.";
text[i++] = "This text is near the max spacing size";
text[i++] = "This text is way too short.";
text[i++] = "This text is too long for the space provied and will overflow#";
text[i++] = "This text is aligned using 'end' and starts at x + width";
text[i++] = "This text is near the max spacing size";
text[i++] = "This text is way too short.";
text[i++] = "#This text is too long for the space provied and will overflow";
text[i++] = "This is aligned with 'center' and is placed from the center";
text[i++] = "This text is near the max spacing size";
text[i++] = "This text is way too short.";
text[i++] = "This text is just too long for the space provied and will overflow";
ctx.clearRect(0,0,w,h);
ctx.font = "25px arial";
ctx.textAlign = "center"
var left = 20;
var center = canvas.width / 2;
var width = canvas.width-left*2;
var y = 40;
var size = 16;
https://riptutorial.com/es/home 174
var i = 0;
ctx.fillText("Justified text examples.",center,y);
y+= 40;
ctx.font = "14px arial";
ctx.textAlign = "left"
var ww = ctx.measureJustifiedText(text[0], width);
var setting = {
maxSpaceSize : 6,
minSpaceSize : 0.5
}
ctx.strokeStyle = "red"
ctx.beginPath();
ctx.moveTo(left,y - size * 2);
ctx.lineTo(left, y + size * 15);
ctx.moveTo(canvas.width - left,y - size * 2);
ctx.lineTo(canvas.width - left, y + size * 15);
ctx.stroke();
ctx.textAlign = "left";
ctx.fillStyle = "red";
ctx.fillText("< 'left' aligned",left,y - size)
ctx.fillStyle = "black";
ctx.fillJustifyText(text[i++], left, y, width, setting); // settings is remembered
ctx.fillJustifyText(text[i++], left, y+=size, width);
ctx.fillJustifyText(text[i++], left, y+=size, width);
ctx.fillJustifyText(text[i++], left, y+=size, width);
y += 2.3*size;
ctx.fillStyle = "red";
ctx.fillText("< 'end' aligned from x plus the width -------------------->",left,y - size)
ctx.fillStyle = "black";
ctx.textAlign = "end";
ctx.fillJustifyText(text[i++], left, y, width);
ctx.fillJustifyText(text[i++], left, y+=size, width);
ctx.fillJustifyText(text[i++], left, y+=size, width);
ctx.fillJustifyText(text[i++], left, y+=size, width);
y += 40;
ctx.strokeStyle = "red"
ctx.beginPath();
ctx.moveTo(center,y - size * 2);
ctx.lineTo(center, y + size * 5);
ctx.stroke();
ctx.textAlign = "center";
ctx.fillStyle = "red";
ctx.fillText("'center' aligned",center,y - size)
ctx.fillStyle = "black";
ctx.fillJustifyText(text[i++], center, y, width);
ctx.fillJustifyText(text[i++], center, y+=size, width);
ctx.fillJustifyText(text[i++], center, y+=size, width);
ctx.fillJustifyText(text[i++], center, y+=size, width);
Párrafos justificados.
Ejemplo de render
https://riptutorial.com/es/home 175
El párrafo superior tiene setting.compact = true y bottom false y el interlineado es 1.2 en lugar del 1.5
predeterminado. Representado por el código de uso de la parte inferior de este ejemplo.
Código de ejemplo
https://riptutorial.com/es/home 176
lineSpacing = vetNumber(settings.lineSpacing, lineSpacing);
if(min > max){ return; }
minSpaceSize = min;
maxSpaceSize = max;
}
var getFontSize = function(font){ // get the font size.
var numFind = /[0-9]+/;
var number = numFind.exec(font)[0];
if(isNaN(number)){
throw new ReferenceError("justifiedPar Cant find font size");
}
return Number(number);
}
function justifiedPar(ctx, text, x, y, width, settings, stroke){
var spaceWidth, minS, maxS, words, count, lines, lineWidth, lastLineWidth, lastSize,
i, renderer, fontSize, adjSpace, spaces, word, lineWords, lineFound;
spaceWidth = ctx.measureText(" ").width;
minS = spaceWidth * minSpaceSize;
maxS = spaceWidth * maxSpaceSize;
words = text.split(" ").map(word => { // measure all words.
var w = ctx.measureText(word).width;
return {
width : w,
word : word,
};
});
// count = num words, spaces = number spaces, spaceWidth normal space size
// adjSpace new space size >= min size. useSize Resulting space size used to render
count = 0;
lines = [];
// create lines by shifting words from the words array until the spacing is optimal.
If compact
// true then will true and fit as many words as possible. Else it will try and get the
spacing as
// close as possible to the normal spacing
while(words.length > 0){
lastLineWidth = 0;
lastSize = -1;
lineFound = false;
// each line must have at least one word.
word = words.shift();
lineWidth = word.width;
lineWords = [word.word];
count = 0;
while(lineWidth < width && words.length > 0){ // Add words to line
word = words.shift();
lineWidth += word.width;
lineWords.push(word.word);
count += 1;
spaces = count - 1;
adjSpace = (width - lineWidth) / spaces;
if(minS > adjSpace){ // if spacing less than min remove last word and finish
line
lineFound = true;
words.unshift(word);
lineWords.pop();
}else{
if(!compact){ // if compact mode
if(adjSpace < spaceWidth){ // if less than normal space width
if(lastSize === -1){
lastSize = adjSpace;
https://riptutorial.com/es/home 177
}
// check if with last word on if its closer to space width
if(Math.abs(spaceWidth - adjSpace) < Math.abs(spaceWidth -
lastSize)){
lineFound = true; // yes keep it
}else{
words.unshift(word); // no better fit if last word removes
lineWords.pop();
lineFound = true;
}
}
}
}
lastSize = adjSpace; // remember spacing
}
lines.push(lineWords.join(" ")); // and the line
}
// lines have been worked out get font size, render, and render all the lines. last
// line may need to be rendered as normal so it is outside the loop.
fontSize = getFontSize(ctx.font);
renderer = stroke === true ? ctx.strokeJustifyText.bind(ctx) :
ctx.fillJustifyText.bind(ctx);
for(i = 0; i < lines.length - 1; i ++){
renderer(lines[i], x, y, width, settings);
y += lineSpacing * fontSize;
}
if(lines.length > 0){ // last line if left or start aligned for no justify
if(ctx.textAlign === "left" || ctx.textAlign === "start"){
renderer(lines[lines.length - 1], x, y, width, noJustifySetting);
ctx.measureJustifiedText("", width, settings);
}else{
renderer(lines[lines.length - 1], x, y, width);
}
}
// return details about the paragraph.
y += lineSpacing * fontSize;
return {
nextLine : y,
fontSize : fontSize,
lineHeight : lineSpacing * fontSize,
};
}
// define fill
var fillParagraphText = function(text, x, y, width, settings){
justifiedTextSettings(settings);
settings = {
minSpaceSize : minSpaceSize,
maxSpaceSize : maxSpaceSize,
};
return justifiedPar(this, text, x, y, width, settings);
}
// define stroke
var strokeParagraphText = function(text, x, y, width, settings){
justifiedTextSettings(settings);
settings = {
minSpaceSize : minSpaceSize,
maxSpaceSize : maxSpaceSize,
};
return justifiedPar(this, text, x, y, width, settings,true);
}
CanvasRenderingContext2D.prototype.fillParaText = fillParagraphText;
https://riptutorial.com/es/home 178
CanvasRenderingContext2D.prototype.strokeParaText = strokeParagraphText;
})();
Cómo utilizar
Consulte el texto justificado para obtener detalles sobre los argumentos. Los argumentos entre [
y ] son opcionales.
Devolver objeto
Las dos funciones devuelven un objeto que contiene información para ayudarlo a colocar el
siguiente párrafo. El objeto contiene las siguientes propiedades.
Este ejemplo utiliza 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 comienzo de la
siguiente, o desde el inicio hasta el final. La mejor apariencia se logra cuando el espaciado en
https://riptutorial.com/es/home 179
todo el párrafo tiene la variación más pequeña 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 función. Reemplace la función justifiedTextSettings en
el otro ejemplo con la utilizada en este ejemplo. Luego 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 tendrá que
probar las dependencias encontradas en // Code point A Se puede eliminar.
Ejemplo de uso
// Draw paragraph
var line = ctx.fillParaText(para, left, y, width, setting); // settings is remembered
// Next paragraph
y = line.nextLine + line.lineHeight;
setting.compact = false;
ctx.fillParaText(para, left, y, width, setting);
Nota: Para el texto alineado a la left o start la última línea del 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 el inicio del párrafo con espacios. Aunque esto puede no ser
consistente de un párrafo a otro. Siempre es bueno aprender lo que hace una función
https://riptutorial.com/es/home 180
y modificarla. Un ejercicio sería agregar una configuración a la configuración que
sangra la primera línea en una cantidad fija. Indica que el bucle while necesitará hacer
que la primera palabra aparezca más grande (+ sangría) words[0].width += ? y luego,
al renderizar las líneas, sangra la primera línea.
https://riptutorial.com/es/home 181
Capítulo 18: Transformaciones
Examples
Dibujar rápidamente muchas imágenes traducidas, escaladas y rotadas.
Hay muchas situaciones en las que desea dibujar una imagen que se gira, se escala y se traduce.
La rotación debe ocurrir alrededor del centro de la imagen. Esta es la forma más rápida de
hacerlo en el lienzo 2D. Estas funciones se adaptan bien a los juegos en 2D, donde la expectativa
es generar unos cientos de imágenes de hasta más de 1000 imágenes cada 60 segundos.
(Depende del hardware)
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 por otros renderizando mi ser. Cuando
haya terminado de representar las 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, tendrá que asegurarse de
que se restaure el estado alfa global
ctx.globalAlpha = 1;
Un ejemplo del uso de las funciones anteriores para representar algunas partículas y algunas
imágenes.
https://riptutorial.com/es/home 182
drawImageRST_Alpha(p.image, p.x, p.y, p.scale, p.rot, p.alpha);
// no need to rest the alpha in the loop
}
// you need to reset the alpha as it can be any value
ctx.globalAlpha = 1;
Los pasos 1 a 5 a continuación permiten que cualquier imagen o forma de trayectoria se mueva a
cualquier lugar del lienzo y se gire a cualquier ángulo sin cambiar ninguna de las coordenadas del
punto original de la imagen / forma de trayectoria.
context.rotate( radianAngle );
https://riptutorial.com/es/home 183
• Paso # 5, Opción # 1: Deshacer todas las transformaciones en orden inverso
// undo #3
context.translate( shapeCenterX, shapeCenterY );
// undo #2
context.rotate( -radianAngle );
// undo #1
context.translate( -shapeCenterX, shapeCenterY );
Las transformaciones alteran la posición inicial de un punto dado al mover, rotar y escalar ese
punto.
https://riptutorial.com/es/home 184
También puede hacer transformaciones menos comunes, como cizallamiento (sesgo),
configurando directamente la matriz de transformación del lienzo usando context.transform .
Canvas realmente logra transformaciones al alterar todo el sistema de coordenadas del canvas.
• 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 un aumento del tamaño de cada x, y en el lienzo: every x*=scaleX y
every y*=scaleY .
Las transformaciones del lienzo son persistentes. Todos los dibujos nuevos continuarán
transformándose hasta que restablezca la transformación del lienzo a su estado predeterminado
https://riptutorial.com/es/home 185
(== totalmente sin transformar). Puede restablecer la configuración predeterminada con:
Canvas utiliza una matriz de transformación para realizar un seguimiento eficiente de las
transformaciones.
• Probar si el mouse está dentro de una forma que ha traducido, rotado y escalado
Comprobar 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 la prueba de impacto como si la forma no se
hubiera transformado.
• Redibuje una forma que haya sido traducida, girada y escalada extensivamente.
https://riptutorial.com/es/home 186
Puede usar geometría y trigonometría para calcular los puntos que conforman las formas
transformadas, pero es más rápido usar una matriz de transformación para calcular esos
puntos.
Métodos:
Código:
https://riptutorial.com/es/home 187
return({
x:transformedX*im[0]+transformedY*im[2]+im[4],
y:transformedX*im[1]+transformedY*im[3]+im[5]
});
}
var transformedPoint=function(screenX,screenY){
return({
x:screenX*m[0] + screenY*m[2] + m[4],
y:screenX*m[1] + screenY*m[3] + m[5]
});
}
// public
function TransformationMatrix(){
self=this;
}
// shared methods
TransformationMatrix.prototype.translate=function(x,y){
var mat=[ 1, 0, 0, 1, x, y ];
multiply(mat);
};
TransformationMatrix.prototype.rotate=function(rAngle){
var c = Math.cos(rAngle);
var s = Math.sin(rAngle);
var mat=[ c, s, -s, c, 0, 0 ];
multiply(mat);
};
TransformationMatrix.prototype.scale=function(x,y){
var mat=[ x, 0, 0, y, 0, 0 ];
multiply(mat);
};
TransformationMatrix.prototype.skew=function(radianX,radianY){
var mat=[ 1, Math.tan(radianY), Math.tan(radianX), 1, 0, 0 ];
multiply(mat);
};
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=function(screenX,screenY){
return(transformedPoint(screenX,screenY));
}
TransformationMatrix.prototype.getScreenPoint=function(transformedX,transformedY){
return(screenPoint(transformedX,transformedY));
}
TransformationMatrix.prototype.getMatrix=function(){
var clone=[m[0],m[1],m[2],m[3],m[4],m[5]];
return(clone);
}
// return public
return(TransformationMatrix);
})();
Manifestación:
https://riptutorial.com/es/home 188
• Seguimiento (== guardar) la matriz de transformación de un rectángulo.
Código:
<!doctype html>
<html>
<head>
<style>
body{ background-color:white; }
#canvas{border:1px solid red; }
</style>
<script>
window.onload=(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }
https://riptutorial.com/es/home 189
return({
x:screenX*m[0] + screenY*m[2] + m[4],
y:screenX*m[1] + screenY*m[3] + m[5]
});
}
// public
function TransformationMatrix(){
self=this;
}
// shared methods
TransformationMatrix.prototype.translate=function(x,y){
var mat=[ 1, 0, 0, 1, x, y ];
multiply(mat);
};
TransformationMatrix.prototype.rotate=function(rAngle){
var c = Math.cos(rAngle);
var s = Math.sin(rAngle);
var mat=[ c, s, -s, c, 0, 0 ];
multiply(mat);
};
TransformationMatrix.prototype.scale=function(x,y){
var mat=[ x, 0, 0, y, 0, 0 ];
multiply(mat);
};
TransformationMatrix.prototype.skew=function(radianX,radianY){
var mat=[ 1, Math.tan(radianY), Math.tan(radianX), 1, 0, 0 ];
multiply(mat);
};
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=function(screenX,screenY){
return(transformedPoint(screenX,screenY));
}
TransformationMatrix.prototype.getScreenPoint=function(transformedX,transformedY){
return(screenPoint(transformedX,transformedY));
}
TransformationMatrix.prototype.getMatrix=function(){
var clone=[m[0],m[1],m[2],m[3],m[4],m[5]];
return(clone);
}
// return public
return(TransformationMatrix);
})();
https://riptutorial.com/es/home 190
ctx.fillText('Untransformed Rect',rect.x,rect.y-10);
// Demo: instructions
ctx.font='14px arial';
ctx.fillText('Demo: click inside the blue rect',30,200);
https://riptutorial.com/es/home 191
}
https://riptutorial.com/es/home 192
Creditos
S.
Capítulos Contributors
No
Arrastrando formas
3 de ruta e imágenes markE
sobre lienzo
Colisiones e
6 Blindman67, markE
Intersecciones
Manipulación de
píxeles con
12 markE
"getImageData" y
"putImageData"
Navegando por un
13 Blindman67, markE
sendero
14 Oscuridad markE
https://riptutorial.com/es/home 193
18 Transformaciones Blindman67, markE
https://riptutorial.com/es/home 194