Hace un año que surgió endleZZ en una sala de espera sin cobertura, como una forma de desarrollar un juego offline que no requiriese nada especial: solo un navegador y un fichero .html en tu móvil.
La idea es genial, me ha servido muchas veces para matar el tiempo, pero se merecía una mínima actualización visual y funcional.
Así que aquí está la versión 0.2: actualización de aniversario! 🎉
Contenido
Versión web
Accede a la versión web oficial que hospedo en este servidor:
Estaba preparando un artículo donde quería insertar un modelo 3D para ilustrarlo mejor, y pensaba incluso en hacer un visor yo mismo. Pero no tuve que surfear mucho para encontrarme con Three.js.
Para este ejemplo, haremos un visor "portable" enlazando las librerías al CDN oficial, en lugar de tener que descargarnos los ficheros a nuestro servidor.
De esta forma, el archivo de ejemplo te servirá en cualquier lugar con conexión a internet. Vamos a crear un fichero .html básico como el que nos sugieren en la documentación:
Vamos a seguir con el ejemplo y rellenamos el segundo bloque <script> definiendo una escena con un cubo animado en rotación:
<script type="module">
import * as THREE from 'three';
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );
camera.position.z = 5;
function animate() {
requestAnimationFrame( animate );
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render( scene, camera );
};
animate();
</script>
Todo eso, junto, queda así:
Añade controles de arrastre y un fondo
Ahora tenemos una base para trabajar. Puede añadir más funcionalidad insertando el módulo OrbitControls que maneja la rotación del modelo y de la cámara.
//Importa nuevos módulos al principio del script
import { OrbitControls } from 'https://unpkg.com/[email protected]/examples/jsm/controls/OrbitControls.js';
//luego, añade los controles del cursor después de declarar la cámara y el renderizador
const controls = new OrbitControls( camera, renderer.domElement );
También puedes modificar el fondo fácilmente, pero necesitas hospedar la imagen junto a la aplicación en un servidor, o ejecutarlo localmente, debido al CORS. Yo usaré la imagen de la cabecera del blog, que saqué de Stellarium.
Primero, define una textura. Luego, añádela a la escena:
//añade esto antes de renderizar, mientras defines la escena
//define la textura
const texture = new THREE.TextureLoader().load( "https://theroamingworkshop.cloud/demos/Unity1-north.png" );
//añade la textura a la escena
scene.background=texture;
Código completo:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My first three.js app</title>
<style>
body { margin: 0; }
</style>
</head>
<body>
<script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/[email protected]/build/three.module.js"
}
}
</script>
<body style="margin: 0; width:100%;height:300px;">
<script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/[email protected]/build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'https://unpkg.com/[email protected]/examples/jsm/controls/OrbitControls.js';
const scene = new THREE.Scene();
const texture = new THREE.TextureLoader().load( "https://theroamingworkshop.cloud/demos/Unity1-north.png" );
scene.background=texture;
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
const controls = new OrbitControls( camera, renderer.domElement );
const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );
camera.position.z = 5;
function animate() {
requestAnimationFrame( animate );
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render( scene, camera );
};
animate();
</script>
</body>
</html>
Insertar un modelo 3D
Ahora vamos a sustituir este cubo por un modelo 3D propio, que en el caso de Three.js, debe tener un formato glTF (.GLB o .GLTF), que es el formato más soportado y que renderiza más rápidamente (aunque también hay soporte para .fbx, .stl, .obj y demás).
Yo exportaré a .glb esta carcasa básica de Raspberry Pi 4B que hice hace un tiempo usando Blender:
Ahora, para insertar el modelo sustituimos el bloque <script> anterior basándonos en el ejemplo "webgl_loader_gltf" que se ve al inicio del post:
Pero para tus webapps de uso personal no es necesario perder el tiempo diseñando una imagen, ni tampoco dejar el cutre icono por defecto del navegador.
Puedes usar un Emoji, que le dará un toque más personal a tu página, tal y como nos indica Lea Verou en este tweet:
Now that all modern browsers support SVG favicons, here’s how to turn any emoji into a favicon.svg:
Si quieres comprobar el seguimiento que hace de tí una web haz este sencillo experimento.
Entra en el inspector del navegador con Crtl+I o click derecho «Inspeccionar»
Abre la pestaña de «Red».
Ahora ve a la página principal del blog.
Esto es lo que verás: apenas 8 archivos que cargan todo lo necesario para que navegues por el blog.
Ahora abre cualquier otra página que suelas visitar y observa la misma pestaña de red.
Yo voy a entrar en amazon.es y no voy a entrar en detalles. Solo fíjate que se irá generando un nuevo fichero /1/batch/1/OE al final cada poco tiempo. Ahora mueve despacio el ratón sin bajar la ventana. Verás que el contenido de esos ficheros contiene una variable t de tiempo y unas coordenadas x e y que indican donde tienes el cursor en cada momento. Esto es solo un ejemplo del tracking que se realiza habitualmente: desde dónde entraste a la web, qué clickas, qué ves, donde te detienes, tu IP, tu geolocalización… Aquí no hay nada de eso.
Esto en cuanto a Amazon y su AWS. Los ficheros de rastreo de Google son los famosos «analytics» o «collects» que también verás en la pestaña de Red en las páginas que lo utilicen (que son la mayoría).
Se suele decir que la mejor solución es la más sencilla, y de hecho, como ves en este blog, no necesitas tantas florituras para publicar una web. Así que desconfía de sitios tediosos, con un interminable listado de «partners» y con servicios de Google Analytics o Amazon Web Services.
Uno de mis últimos proyectos es una app de notas para alojarlas en mi servidor y prescindir de [G] Notes.
La característica más interesante que incluye es Rich Text Editor, una completa librería javascript en forma de interfaz para dar formato HTML al texto de cualquier formulario web.
Es de código abierto, gratis para uso personal, y muy simple de usar. Todo un win-win que te traigo hoy.
Contenido
Licencia
He dicho que Rich Text Editor es gratis, y es así si le das un uso no comercial.
Es importante que leas su página de precios para que entiendas las limitaciones de la licencia gratuita:
1 desarrollador
1 dominio
5 usuarios activos
En mi caso, he instalado Rich Text Editor en este servidor que aloja el blog, y lo he restringidomediante php para que solo se pueda acceder desde el propio dominio.
TIP! No hospedes los archivos en cualquier sitio que sea accesible por cualquiera (como un servidor público), ya que podrías dar pie a un incumplimiento de licencia, teniendo que pagar una licencia standard, o dando de baja tu app.
Aun así, las licencias comerciales son de un único pago y puede que te merezca la pena obtener una si vas a darle más uso.
Instalación
Usar Rich Text Editor es muy sencillo, y su página de documentación lo explica a la perfección.
Para instalar Rich Text Editor solo tenemos que descargar los ficheros de su página web a una carpeta local o a una ubicación de tu servidor.
Una funcionalidad básica es capturar el texto que hemos ido editando en el cuadro de texto. Es lo que hace el botón de la parte inferior, usando la función getHTMLcode():
<button onclick="alert(editor1.getHTMLCode())">Show Html Code</button>
Usos
Como comenté anteriormente, el uso que le voy a dar a esta librería es en un editor de notas que se alojará en mi servidor.
El editor de texto me sirve para dar formato a las notas muy fácilmente y guardarlas con todo el formato.
Aquí tienes una demo de cómo va quedando la interfaz, aunque no tiene las funciones habilitadas todavía.
Otras aplicaciones en las que puedes usar este editor es en foros, formularios, o cualquier cuadro de texto que tenga que rellenar un usuario de tu web app.
Espero que te resulte tan útil como a mí y que le des buen uso. Si tienes dudas o comentarios puedes enviarlos por Twitter 🐦 Hasta pronto!
Para prescindir de plugins externos innecesarios, aquí verás cómo hacer tus propios widgets para insertar en tu página de WordPress o dentro de cualquier post.
Sólo tienes que saber algo de HTML y de javascript. Vamos a ello!
Contenido
Custom HTML
Si has visto algún otro post de este blog ya habrás visto la tabla de contenido de arriba, o la notificación de cookies en la barra lateral.
Ambos son elementos dinámicos que he programado con HTML y javascript y que puedes insertar en múltiples sitios de tu WordPress usando un bloque "Custom HTML".
Simplemente inserta este bloque y dentro podrás introducir tu código HTML personalizado y programable con javascript. Veamos unos ejemplos.
Notificación de cookies
La barra lateral de este blog muestra un mensaje de consentimiento de cookies, con un enlace a la política de privacidad.
Puede que hayas visto que al cambiar el idioma de la página, se cambia el contenido de dicho mensaje automáticamente.
Haz click derecho para inspeccionar la estructura de la página.
Para que funcione correctamente, imagina que el bloque Custom HTML que insertas es un iframe o una especie de página insertada; una sub-página dentro de nuestra página.
Editamos el contenido del Custom HTML como si de otra página se tratara, y podemos añadir libremente lo que queramos.
Para la notificación, solo quiero una línea de texto, a la que asigno la id "cookie-txt". Es bueno añadir toda la estructura HTML básica, aunque se puede prescindir de muchos elementos.
Ahora añado una función javascript que lea el idioma de la página y rellene esta línea en consecuencia.
Puedes hacer distintas pruebas usando la consola y el inspector del navegador, haciendo click derecho en la página.
Una forma rápida de obtener el idioma es leyendo la propiedad lang="es-ES" del elemento <html> de la página (o lang="en-GB" para inglés).
El resto es una condición:
Si se detecta "es-ES", definimos el mensaje en Español.
Si se detecta "en-GB", definimos el mensaje en Inglés.
El contenido del mensaje lo definimos mediante la propiedad innerHTML del elemento <p>, por lo que podemos darle cualquier formato HTML, como incrustarle un enlace o resaltar en negrita.
<script>
var lang=document.getElementsByTagName("html")[0].lang;
if(lang=="es-ES"){
document.getElementById("cookie-txt").innerHTML="🍪 <a href='https://theroamingworkshop.cloud/b/?page_id=1225' target='_blank'>Política</a> <b>anti-Cookies</b> aceptada al navegar.</p>";
}else if(lang=="en-GB"){
document.getElementById("cookie-txt").innerHTML="<b>🍪 anti-Cookies</b> <a href='https://theroamingworkshop.cloud/b/?page_id=3' target='_blank'>policy</a> accepted while browsing.";
}
</script>
Por lo que quedaría algo así:
El código completo del Custom HTML que muestra la notificación de cookies es este:
El bloque Custom HTML, en este caso, lo inserto como un elemento de la barra lateral:
Tabla de contenido
La tabla de contenido que aparece en cada post viene a ser más o menos lo mismo que lo anterior.
En primer lugar, defino la tabla dándole formato, y dejando el contenido vacío para rellenar luego con una función.
<div id="menu" style="padding:20px 20px 20px 20px; border-left:2px solid darkgrey;">
<p style="font-weight:bold;">Contenido</p>
</div>
En este caso, lo que haré es buscar los "headings" o títulos de cada apartado, en concreto los de etiqueta <h2>. Por ello, al redactar los posts debo usar siempre este tipo de encabezado (que es el habitual) si quiero que aparezca en la tabla.
Además, defino un ancla o html Anchor, que se asignará como "id", lo cual nos permitirá crear un enlace que nos lleve hasta dicho encabezado.
La función hará lo siguiente:
Buscar los elementos <h2> uno por uno. for (let i=0;i < window.document.getElementsByTagName("h2").length; i++) { element = window.document.getElementsByTagName("h2")[i]; ...
Tomar el texto que contiene. text = "▹ "+element.innerHTML;
Crear el nuevo elemento de tipo hiperenlace (<a>) que irá a la tabla de contenido, con el texto anterior. var newelement = document.createElement("a"); newelement.innerHTML=text;
Tomar la "id" del post y del encabezado para crear un enlace a dicho encabezado. var postid = window.document.getElementsByTagName("article")[0].id; var url = "https://theroamingworkshop.cloud/b/?p="+postid.substr(5,postid.length)+"#"+element.id;
Añadirlo todo en una línea al elemento de la tabla de contenido newelement.setAttribute("href", url); newelement.appendChild(document.createElement("br")); window.document.getElementById("menu").appendChild(newelement);
TIP! Añadiendo #id-de-la-sección al final de una URL te dirije a dicho apartado.
Por último, algunas páginas tardan un tiempo en cargar y no están todos los encabezados disponibles al inicio, por lo que meto todo el código anterior en una función, y la invoco con un tiempo de retardo. window.setTimeout(fillmenu,500);
El código del bloque completo sería el siguiente:
<div id="menu" style="padding:20px 20px 20px 20px; border-left:2px solid darkgrey;">
<p style="font-weight:bold;">Contenido</p>
</div>
<script>
var text;
var element;
function fillmenu(){
for (let i=0; i<window.document.getElementsByTagName("h2").length; i++){
element = window.document.getElementsByTagName("h2")[i];
text = "▹ "+element.innerHTML;
var newelement = document.createElement("a");
newelement.innerHTML=text;
var postid=window.document.getElementsByTagName("article")[0].id;
var url = "https://theroamingworkshop.cloud/b/?p="+postid.substr(5,postid.length)+"#"+element.id;
newelement.setAttribute("href", url);
newelement.appendChild(document.createElement("br"));
window.document.getElementById("menu").appendChild(newelement);
}
}
window.setTimeout(fillmenu,500);
</script>
TIP! Añade este bloque HTML a tus bloques reusables y lo podrás insertar en todos tus posts. Este bloque de TIP! es otro ejemplo.
Bloque de TIP!
TIP! Este bloque de TIP! es otro ejemplo. Tendrás que reconvertir a un bloque regular si quieres alterar el contenido solo en el bloque insertado
Aquí su código:
<p style="border-left:3px solid orange;padding-left:5px;font-size:14px;"><i><b>TIP! </b>Este bloque de <b>TIP!</b> es otro ejemplo. Tendrás que reconvertir a un bloque regular si quieres alterar el contenido <b>solo</b> en el bloque insertado</i></p>
Conclusión
Como verás, las posibilidades son realmente ilimitadas así que queda todo a expensas de tu imaginación.
Se creativo y diseña tus propios widgets, ya verás lo bien complementan tu blog!
Como siempre, dudas o comentarios al Twitter 🐦 ¡Hasta otra!
(el bloque de Twitter también es un reutilizable 👀 )