Creando un Navegador: Parte III MVP recap
Revisemos lo que hicimos la semana pasada.
Creamos nuestro MVP de navegador.
- Definimos como se escriben nuestras interfaces.
JSON
- Interpretamos el contenido
Parser
- Guardamos la interpretacion en el
appState
- Dibujamos en pantalla con los componentes que implementamos.
Renderer
En otras palabras: Creamos un loader
, un parser
y un renderer
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
- defines el modelo de tu interfaz
- expones este modelo para que el usuario lo modifique
- dibujas el modelo segun tus necesidades en un
Renderer
Figma
Miro
tambien usa un <canvas>
draw.io
Maneja su renderer con <svg>
y no con <canvas>
.
Canva
Crea elementos html
directamente en el DOM.
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
WebFlow
Hace lo mismo que Wix, HTML elements en un iframe
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
, comohandlebar
,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