Saltearse al contenido

Creando un Navegador: Parte III MVP recap

Revisemos lo que hicimos la semana pasada.

Creamos nuestro MVP de navegador.

  1. Definimos como se escriben nuestras interfaces. JSON
  2. Interpretamos el contenidoParser
  3. Guardamos la interpretacion en el appState
  4. Dibujamos en pantalla con los componentes que implementamos. Renderer

En otras palabras: Creamos un loader, un parser y un renderer

diagrama-navegador

link diagrama

link demo

link repositorio



Versión simplificada portable

Pega el siguiente código en la consola de tu navegador y tendrás lo que hicimos la semana pasada.

(function () {
const canvasHelper = {
dot: ({ ctx, element }) => {
console.log('dot');
console.log({ element });
element.radius = 1;
canvasHelper.arc({ ctx, element });
},
line: ({ ctx, element }) => {
console.log('line');
},
rect: ({ ctx, element }) => {
console.log('rect');
},
arc: ({ ctx, element }) => {
const { radius, position } = element;
const [x, y] = position;
ctx.fillStyle = 'black';
//
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fill();
},
polygon: ({ ctx, element }) => {
console.log('polygon');
},
text: ({ ctx, element }) => {
console.log('text');
},
};
const navegador = {
loader: {
getFile: (fileReference) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsText(fileReference);
});
},
},
parser: {
toUI: (file) => {
return JSON.parse(file);
},
},
renderer: {
draw: (elements, engine) => {
engine.width = 300;
engine.height = 400;
engine.style.border = '1px solid #ccc';
const ctx = engine.getContext('2d');
// background
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, engine.width, engine.height);
// draw process
elements.forEach((element) => {
element.type = element.type.toLowerCase().trim();
if (element.position) {
element.position = element.position
.split(',')
.map((coord) => Number(coord));
}
// draw
canvasHelper[element.type]({ ctx, element });
});
},
},
};
const appState = {
currentUI: undefined,
history: [],
};
const startNavegador = async (event) => {
//loader
const fileRef = event.target.files[0];
const file = await navegador.loader.getFile(fileRef);
// parser
const ui = navegador.parser.toUI(file);
//state
appState.currentUI = ui;
appState.history.push({ ui, ts: Date.now() });
// renderer
const { elements } = appState.currentUI.screen;
const engine = document.querySelector('#engine');
navegador.renderer.draw(elements, engine);
};
// start
const template = `
<!-- renderer -->
<canvas id="engine" style=" border-radius:4px; padding:1rem 1rem;"></canvas>
<!-- trigger process -->
<div style="max-width:300px">
<label for="file-input" style="width:100%; display:block; border:1px solid #ccc; border-radius:4px; padding:1rem 1rem;cursor:pointer" >Import file</label>
<input
style="width: 0; height: 0; position: absolute"
type="file"
name="file-input"
id="file-input"
accept=".json"
/>
<div/>
`;
document.body.innerHTML = template;
// listeners
const fileInput = document.querySelector('#file-input');
fileInput.addEventListener('change', startNavegador);
})();




Que podemos hacer con esto?

mmmm…

Si quisieras hacer aplicaciones como:

  • Figma
  • Miro
  • draw.io
  • Canva
  • Wix
  • Webflow

Flujo base

  1. defines el modelo de tu interfaz
  2. expones este modelo para que el usuario lo modifique
  3. dibujas el modelo segun tus necesidades en un Renderer

Figma

figma-sections

figma-canvas-meme

Miro

tambien usa un <canvas>

miro-canvas-meme

draw.io

Maneja su renderer con <svg> y no con <canvas>.

drawio-canvas-meme

Canva

Crea elementos html directamente en el DOM.

canva-html

Wix

Crea elementos html y te los muestra en un iframe.

cada vez que haces un cambio en el Editor, manda un mensaje al Backend, e backend actualiza el archivo y se actualiza el iframe

wix-

WebFlow

Hace lo mismo que Wix, HTML elements en un iframe

webflow

Para este tipo de aplicaciones puede que el Parser no sea tan relevante como el Renderer y la gestión de los elementos del lienzo.

Podemos decir que la complejidad se inclina hacia el Renderer


🤔 que otra cosa podríamos hacer ?

inclinarnos hacia el Parser.

Basados en un texto podemos extraer su significado segun nuestras reglas.

  • Podemos simplificar la forma de publicar documentos en la web escribiendo archivos markdown
  • Podemos crear nuestro propio lenguaje de templating, como handlebar, Razor,etc.
  • Podemos definir la sintaxis de nuestro propio lenguaje de programación.
  • Podemos crear Linters y Prettiers

y muchas cosas más!.

Todo va a depender donde y que interpretar. (Puede ser ‘web’ o no).


Estos Legos (loader,parser,renderer), son la puerta de entrada para crear y comprender sistemas/productos más complejos.


Links para profundizar:



Volviendo con nuestro MVP

Nos faltan un millón de cosas xD!

  • Actualmente solo leemos y mostramos la interfaz.
  • No soportamos interacciones del usuario
  • No se puede definir la apariencia y composición de los elementos de la interfaz
  • Solo soportamos formas basicas (puntos, lineas, poligonos) , no soportamos imagenes, audio,…
  • No podemos incorporar recursos externos (fuentes, imagenes,…)
  • No leemos interfaces de maquinas externas.

Todavia no alcanzamos a ser la Web 1.0.

Somos la Web 0.6, mas-menos xD


📒Backlog

  • Permitir al usuario interactuar con la interfaz Our own Event system.
  • Permitir definir la apariencia y composición de las elementos de la interfaz our own CSS implementation.
  • Permitir la ejecución de código durante el consumo del contenido our own scripting environment.
  • Cargar nuestros archivos de interfaces desde un maquina remota/externa.
  • LLevar registro del contenido consumido por usuario
  • Agregar metricas de los procesos internos de nuestro navegador.

Si les tinca algo en particular, avisen.

🤔 esta semana estaré pensando con que seguir

😁✌️!