Canvas propio
Paquete: @tiendana/utils/logomark
Cuando <tdn-logomark> no encaja, el módulo expone sus piezas: mountLogomark es el elemento sin el elemento, y ClipPlayer junto a paintMark es lo mismo fotograma a fotograma, para tu propio bucle de dibujo.
Características
✅ mountLogomark - Dibuja en un <canvas> y mantiene el bucle, la densidad de píxeles, la pausa y el movimiento reducido
✅ ClipPlayer - Reproduce un clip con seek y advance, igual a cualquier frame rate
✅ paintMark y paintAccents - El renderer de referencia, el mismo que usan las exportaciones
✅ Sin framework ni three.js - Solo Canvas 2D
mountLogomark
import { mountLogomark } from '@tiendana/utils/logomark';
const canvas = document.querySelector('canvas')!;
const mounted = mountLogomark(canvas, { clip: 'hello', loop: true, size: 160 });
mounted.play('congrat'); // desde su inicio
mounted.hold('smile'); // detiene cualquier clip y mantiene una pose
mounted.dispose(); // al desmontar
mountLogomark(canvas, options?) dibuja el logomark en el canvas, en el mismo cuadro que usan las exportaciones, y lo mantiene vivo hasta dispose(). Lanza un error si el canvas no tiene contexto 2D. Sin tones, el canvas (o un ancestro) necesita las propiedades --logomark-* fijadas; si no, la marca se pinta en el color del texto.
MountOptions
| Opción | Tipo | Descripción |
|---|---|---|
clip | ClipName | Un clip a reproducir |
still | StillName | Una pose a mantener; SMILE si no hay clip ni still |
loop | boolean | Reinicia el clip al terminar |
size | number | Lado del cuadrado en px CSS; si falta, el ancho CSS del canvas (160 si tampoco hay) |
tones | LogomarkTones | Los tonos; si faltan se leen de las propiedades --logomark-* del canvas |
style | 'flat' | 'sticker' | flat por defecto; sticker añade el contorno troquelado (necesita tones.keyline) |
onEnd | (clip: ClipName) => void | Se llama cuando un clip sin bucle llega al final, e inmediatamente con movimiento reducido para los clips iniciados con play() (no para options.clip) |
El cuadrado del canvas es el cuadro de exportación: deja sitio a los destellos y a los pops, así que la marca ocupa más o menos la mitad. El elemento ajusta esto por ti; con mountLogomark dimensiona el canvas con ese margen en mente.
MountedLogomark
| Método | Descripción |
|---|---|
play(clip, loop?) | Reproduce un clip desde su inicio; loop por defecto es el de las opciones |
hold(still) | Detiene cualquier clip y mantiene la pose |
resize(size) | Repinta con un nuevo lado cuadrado en px CSS |
setTones(tones) | Repinta con tonos nuevos, tras un cambio de colourway o de tema |
dispose() | Detiene el bucle y quita los observadores |
El montaje se pausa mientras el canvas está fuera de pantalla o la pestaña está oculta, y bajo movimiento reducido mantiene la pose que representa cada clip, como el elemento.
Leer los tonos
readLogomarkTones(el, defaults?) resuelve las propiedades --logomark-* tal como se aplican a el y devuelve colores CSS calculados. Lo que la página no fije sale de defaults, si se pasa.
import { readLogomarkTones } from '@tiendana/utils/logomark';
const tones = readLogomarkTones(canvas);
Con mountLogomark sin tones, esta lectura se hace sola sobre el canvas.
Interfaz LogomarkTones
export interface LogomarkTones {
readonly face: string; // la sonrisa y los ojos
readonly body: string; // la d
readonly track: string; // la marca sin dibujar
readonly error?: string; // ERROR; sin él, el tono de la cara
readonly keyline?: string; // el contorno de un sticker
}
ClipPlayer
ClipPlayer reproduce un clip de forma determinista, a cualquier frame rate: la cara corre con el paso fijo del motor (1/60 s) y un instante entre dos pasos se dibuja como la pose intermedia, así que una exportación a 24 o 25 fps se mueve tan uniforme como una a 60.
import { CLIPS, ClipPlayer } from '@tiendana/utils/logomark';
const player = new ClipPlayer(CLIPS['hello']);
player.seek(0.5); // reconstruye desde el inicio hasta 0,5 s
player.advance(1 / 60); // sigue desde el último instante
| Miembro | Descripción |
|---|---|
new ClipPlayer(clip, { reduced }?) | Crea el reproductor para un Clip y se posiciona en el inicio |
pose | La MarkPose del último seek o advance; se pasa a paintMark |
accents | Los acentos vivos en ese instante, en unidades canónicas de la marca, que paintAccents pinta |
length | Duración del clip en segundos |
elapsed | Segundos desde el inicio, según el último seek o advance |
name | El ClipName |
seek(t) | Salta a t segundos, rebobinando y reproduciendo desde el inicio |
advance(dt) | Avanza dt segundos desde el último instante; para el bucle de dibujo de un host |
seek y advance limitan el tiempo a [0, length]. Un advance aterriza en la misma pose que un seek al mismo instante.
paintMark y paintAccents
import { CLIPS, ClipPlayer, exportPlacement, paintAccents, paintMark, readLogomarkTones } from '@tiendana/utils/logomark';
const canvas = document.querySelector('canvas')!;
canvas.width = canvas.height = 320;
const g = canvas.getContext('2d')!;
const tones = readLogomarkTones(canvas); // el canvas o un ancestro debe tener --logomark-* fijadas; si no, se pinta en el color del texto
const place = exportPlacement(canvas.width);
const player = new ClipPlayer(CLIPS['congrat']);
let last = 0;
function frame(now: number) {
const dt = last ? Math.min((now - last) / 1000, 0.1) : 0;
last = now;
player.advance(dt);
g.clearRect(0, 0, canvas.width, canvas.height);
paintMark(g, player.pose, place, tones);
paintAccents(g, player.accents, place, tones);
if (player.elapsed < player.length) {
requestAnimationFrame(frame);
}
}
requestAnimationFrame(frame);
| Función | Descripción |
|---|---|
paintMark(g, pose, place, tones, style?) | Pinta la pose; el estado del contexto se restaura después. En SMILE emite exactamente las cadenas de path del logo, así que un still es el logo mismo |
paintAccents(g, accents, place, tones, lineWidth?, style?) | Pinta los destellos, arcos y líneas de impacto; lineWidth en unidades canónicas (ACCENT_WEIGHT por defecto) |
paintMark es el renderer de referencia y el único que usan las exportaciones: stills, stickers, fotogramas de vídeo y pósters. g es un CanvasRenderingContext2D o un OffscreenCanvasRenderingContext2D. Con style: 'sticker' ambas funciones recortan con el keyline (tones.keyline) para que se lean bien sobre cualquier fondo de chat.
MarkPlacement
Dónde se sitúa la marca en el contexto: la esquina superior izquierda del viewBox y su alto, en las unidades del contexto.
interface MarkPlacement {
readonly kind: 'affine';
readonly x: number;
readonly y: number;
readonly height: number;
}
exportPlacement(size) devuelve la colocación dentro de un cuadrado de size px: el cuadro de exportación, que deja margen para que ningún clip se corte. Si el canvas es solo la marca, crea tu propio MarkPlacement; el alto de la marca en su viewBox es LOGOMARK_VIEWBOX.height.
import { LOGOMARK_VIEWBOX, type MarkPlacement } from '@tiendana/utils/logomark';
const height = 120;
const place: MarkPlacement = {
kind: 'affine',
x: 0,
y: 0,
height
};
// el ancho resultante es height * LOGOMARK_VIEWBOX.width / LOGOMARK_VIEWBOX.height
Un MarkPlacement ajustado a la marca recorta los destellos y los pops, que se salen de su caja. Para clips usa exportPlacement(size) o deja margen alrededor.
Ver También
- Elemento -
<tdn-logomark>, el uso habitual - Exportación - los mismos fotogramas como archivos
- Colourways - las propiedades
--logomark-*que leereadLogomarkTones