Sesion Laboratorio 7 Practica 2 3 - jesusgpa/2023-2024-CSAAI GitHub Wiki

Sesión Laboratorio 7: Práctica 2-3

  • Tiempo: 2h
  • Fecha: Jueves 14 de Marzo de 2024
  • Objetivos de la sesión:
    • Trabajar en la práctica 2

A practicar

Aprovecha para avanzar con la práctica 2 (BOOM) todo lo que puedas.

Función que nos devuelve un número aleatorio

Esta función puede ser muy útil para conseguir los números de la clave secreta.

//-- Generar números aleatorios con un valor máximo
function getRandomInt(max) {
    return Math.floor(Math.random() * max);
}

Crear y gestionar un array de números secretos o cualquier cosa

Si queremos mantener los números secretos ocultos vamos a necesitar una estructura de datos.

Un array es una buena opción.

//-- Array que almacena números secretos
const secretkey = [];

//-- Generar números aleatorios con un valor máximo
function getRandomInt(max) {
    return Math.floor(Math.random() * max);
}

//-- Generamos números secretos y los almacenamos en un array
for (let i = 0; i < 10; i++) {
    let rnum = getRandomInt(9);
    secretkey.push(rnum.toString());
}

//-- Mostramos el contenido del array de números secretos en la consola
for (let j = 0; j < secretkey.length; j++) {
    console.log( j + ' Secret Key ' + secretkey[j]);
}

TIP: Intenta crear funciones que puedas llamar desde tus funciones de callback asociadas a las acciones de los botones.

Este es un ejemplo genérico.

check_entorno() {
  //-- Comprobaciones antes de empezar
  //-- ...
  return ok;
}

gui.start.onclick = () => {
  
  if(check_entorno()) {
    //-- Si todo está ok ejecutamos la funcionalidad de la función de callback asociada al evento click.
  }
}

Canvas: Ondas de colores

En este ejemplo encontrarás diferentes maneras de dibujar líneas y utilizar colores.

En este caso utilizando curvas cuadráticas de Bézier que pueden ser muy útiles para describir movimientos en forma de curva (curva de Bézier).

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Ondas de Colores</title>

    <script src="onda-de-colores.js" defer></script>
</head>
<body>
    <canvas id="canvasGlowing" width="800" height="450"></canvas>
</body>
</html>
var canvas = document.getElementById('canvasGlowing');
var context = canvas.getContext('2d');

var lastX = context.canvas.width * Math.random();
var lastY = context.canvas.height * Math.random();

var hue = 0;

function line() {

    context.save();
    
    context.translate(context.canvas.width/2, context.canvas.height/2);
    context.scale(0.9, 0.9);
    
    context.translate(-context.canvas.width/2, -context.canvas.height/2);
    context.beginPath();
    
    context.lineWidth = 5 + Math.random() * 10;
    context.moveTo(lastX, lastY);

    lastX = context.canvas.width * Math.random();
    lastY = context.canvas.height * Math.random();

    // https://developer.mozilla.org/en-US/docs/Glossary/Bezier_curve    
    context.bezierCurveTo(context.canvas.width * Math.random(),
        context.canvas.height * Math.random(),
        context.canvas.width * Math.random(),
        context.canvas.height * Math.random(),
        lastX, lastY);

    hue = hue + 10 * Math.random();

    // https://es.wikipedia.org/wiki/Modelo_de_color_HSL
    // https://www.w3schools.com/colors/colors_hsl.asp
    // Cambia el color y deja la saturación y la luminosidad fija.
    context.strokeStyle = 'hsl(' + hue + ', 50%, 50%)';
    context.shadowColor = 'white';
    context.shadowBlur = 10;
    context.stroke();
    context.restore();

 }

setInterval(line, 150);


function blank() {
    context.fillStyle = 'rgba(0,0,0,0.1)';
    context.fillRect(0, 0, context.canvas.width, context.canvas.height);
}

setInterval(blank, 140);

Este es el resultado:

Animaciones avanzadas

Dibujando una pelota

Vamos a empezar recordando lo que hemos aprendido sobre el canvas dibujando una pelota azul.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Blue Ball 1</title>

    <link rel="stylesheet" href="canvas-blue-ball-01.css">

    <script src="canva-blue-ball-01.js" defer></script>
</head>
<body>
    <canvas id="canvas" width="600" height="300"></canvas>
</body>
</html>

Utiliza este css para darle un borde al canvas

#canvas {
    border: 1px solid;
}

Nada del otro mundo.

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

const ball = {
  x: 100,
  y: 100,
  radius: 25,
  color: "blue",
  draw() {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
    ctx.closePath();
    ctx.fillStyle = this.color;
    ctx.fill();
  },
};

ball.draw();

Vamos a animar esta pelota

Una vez más, window.requestAnimationFrame() nos ayuda a generar la animación.

La pelota se mueve añadiendo un vector de velocidad a la posición.

Para cada frame, también limpiamos el canvas para eliminar los círculos antiguos de los cuadros anteriores.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Blue Ball 2</title>

    <link rel="stylesheet" href="canvas-blue-ball-01.css">

    <script src="canvas-blue-ball-02.js" defer></script>
</head>
<body>
    <canvas id="canvas" width="600" height="300"></canvas>
</body>
</html>

Vamos a darle velocidad en el eje x e y.

const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let raf;

const ball = {
  x: 100,
  y: 100,
  vx: 5,
  vy: 2,
  radius: 25,
  color: "blue",
  draw() {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
    ctx.closePath();
    ctx.fillStyle = this.color;
    ctx.fill();
  },
};

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ball.draw();
  ball.x += ball.vx;
  ball.y += ball.vy;
  raf = window.requestAnimationFrame(draw);
}

canvas.addEventListener("mouseover", (e) => {
  raf = window.requestAnimationFrame(draw);
});

canvas.addEventListener("mouseout", (e) => {
  window.cancelAnimationFrame(raf);
});

ball.draw();

La bola entra en movimiento cuando ponemos el puntero del ratón sobre el canvas.

Añadiendo límites

Vamos a añadir los límites necesarios para encerrar la bola en el canvas.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Blue Ball 3</title>

    <link rel="stylesheet" href="canvas-blue-ball-01.css">

    <script src="canvas-blue-ball-03.js" defer></script>
</head>
<body>
    <canvas id="canvas" width="600" height="300"></canvas>
</body>
</html>

La pelota iniciará el movimiento al mover el puntero del ratón sobre el canvas, y se para cuando lo sacamos.

const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let raf;

const ball = {
  x: 100,
  y: 100,
  vx: 5,
  vy: 2,
  radius: 25,
  color: "blue",
  draw() {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
    ctx.closePath();
    ctx.fillStyle = this.color;
    ctx.fill();
  },
};

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ball.draw();
  ball.x += ball.vx;
  ball.y += ball.vy;

  // Así limitamos el movimiento de la pelota
  if (
    ball.y + ball.vy > canvas.height - ball.radius ||
    ball.y + ball.vy < ball.radius
  ) {
    ball.vy = -ball.vy;
  }
  if (
    ball.x + ball.vx > canvas.width - ball.radius ||
    ball.x + ball.vx < ball.radius
  ) {
    ball.vx = -ball.vx;
  }

  raf = window.requestAnimationFrame(draw);
}

canvas.addEventListener("mouseover", (e) => {
  raf = window.requestAnimationFrame(draw);
});

canvas.addEventListener("mouseout", (e) => {
  window.cancelAnimationFrame(raf);
});

ball.draw();

Acelerando

Cuando añadimos aceleración, conseguimos un movimiento más realista.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Blue Ball 4</title>

    <link rel="stylesheet" href="canvas-blue-ball-01.css">

    <script src="canvas-blue-ball-04.js" defer></script>
</head>
<body>
    <canvas id="canvas" width="600" height="300"></canvas>
</body>
</html>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let raf;

const ball = {
  x: 100,
  y: 100,
  vx: 5,
  vy: 2,
  radius: 25,
  color: "blue",
  draw() {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
    ctx.closePath();
    ctx.fillStyle = this.color;
    ctx.fill();
  },
};

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ball.draw();
  ball.x += ball.vx;
  ball.y += ball.vy;

  // Así añadimos el efecto de la aceleración al movimiento
  ball.vy *= 0.99;
  ball.vy += 0.25;

  if (
    ball.y + ball.vy > canvas.height - ball.radius ||
    ball.y + ball.vy < ball.radius
  ) {
    ball.vy = -ball.vy;
  }
  if (
    ball.x + ball.vx > canvas.width - ball.radius ||
    ball.x + ball.vx < ball.radius
  ) {
    ball.vx = -ball.vx;
  }

  raf = window.requestAnimationFrame(draw);
}

canvas.addEventListener("mouseover", (e) => {
  raf = window.requestAnimationFrame(draw);
});

canvas.addEventListener("mouseout", (e) => {
  window.cancelAnimationFrame(raf);
});

ball.draw();

Dejando estela

Vamos a ver como sería el efecto de dejar una estela, este efecto refuerza la sensación de movimiento.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Blue Ball 5</title>

    <link rel="stylesheet" href="canvas-blue-ball-01.css">

    <script src="canvas-blue-ball-05.js" defer></script>
</head>
<body>
    <canvas id="canvas" width="600" height="300"></canvas>
</body>
</html>

Hasta ahora hemos utilizado el método clearRect al borrar los frames anteriores.

Si reemplazamos este método con fillRect semitransparente, podemos crear fácilmente un efecto de estela.

ctx.fillStyle = "rgb(255 255 255 / 30%)";
ctx.fillRect(0, 0, canvas.width, canvas.height);

Este es el ejemplo completo.

const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let raf;

const ball = {
  x: 100,
  y: 100,
  vx: 5,
  vy: 2,
  radius: 25,
  color: "blue",
  draw() {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
    ctx.closePath();
    ctx.fillStyle = this.color;
    ctx.fill();
  },
};

function draw() {
  ctx.fillStyle = "rgb(255 255 255 / 30%)";
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  ball.draw();
  ball.x += ball.vx;
  ball.y += ball.vy;
  ball.vy *= 0.99;
  ball.vy += 0.25;

  if (
    ball.y + ball.vy > canvas.height - ball.radius ||
    ball.y + ball.vy < ball.radius
  ) {
    ball.vy = -ball.vy;
  }
  if (
    ball.x + ball.vx > canvas.width - ball.radius ||
    ball.x + ball.vx < ball.radius
  ) {
    ball.vx = -ball.vx;
  }

  raf = window.requestAnimationFrame(draw);
}

canvas.addEventListener("mouseover", (e) => {
  raf = window.requestAnimationFrame(draw);
});

canvas.addEventListener("mouseout", (e) => {
  window.cancelAnimationFrame(raf);
});

ball.draw();

Controlando con el ratón

Para tener control sobre la pelota, podemos hacer que siga el puntero del ratón usando el evento mousemove, por ejemplo.

El evento de clic libera la pelota y la deja rebotar nuevamente.

Vamos a ver como sería.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Blue Ball 6</title>

    <link rel="stylesheet" href="canvas-blue-ball-01.css">

    <script src="canvas-blue-ball-06.js" defer></script>
</head>
<body>
    <canvas id="canvas" width="600" height="300"></canvas>
</body>
</html>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let raf;
let running = false;

const ball = {
  x: 100,
  y: 100,
  vx: 5,
  vy: 1,
  radius: 25,
  color: "blue",
  draw() {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
    ctx.closePath();
    ctx.fillStyle = this.color;
    ctx.fill();
  },
};

function clear() {
  ctx.fillStyle = "rgb(255 255 255 / 30%)";
  ctx.fillRect(0, 0, canvas.width, canvas.height);
}

function draw() {
  clear();
  ball.draw();
  ball.x += ball.vx;
  ball.y += ball.vy;

  if (
    ball.y + ball.vy > canvas.height - ball.radius ||
    ball.y + ball.vy < ball.radius
  ) {
    ball.vy = -ball.vy;
  }
  if (
    ball.x + ball.vx > canvas.width - ball.radius ||
    ball.x + ball.vx < ball.radius
  ) {
    ball.vx = -ball.vx;
  }

  raf = window.requestAnimationFrame(draw);
}

canvas.addEventListener("mousemove", (e) => {
  if (!running) {
    clear();
    ball.x = e.clientX;
    ball.y = e.clientY;
    ball.draw();
  }
});

canvas.addEventListener("click", (e) => {
  if (!running) {
    raf = window.requestAnimationFrame(draw);
    running = true;
  }
});

canvas.addEventListener("mouseout", (e) => {
  window.cancelAnimationFrame(raf);
  running = false;
});

ball.draw();

Ahora tenemos control total sobre la pelota azul.

Física de movimiento

Entender estos ejemplos es clave para definir la física de movimiento de diferentes elementos en el canvas.

Autor

Jesús Parrado Alameda (jesusgpa)

Creditos

Licencia

Enlaces

⚠️ **GitHub.com Fallback** ⚠️