Saltar al contenido principal

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ónTipoDescripción
clipClipNameUn clip a reproducir
stillStillNameUna pose a mantener; SMILE si no hay clip ni still
loopbooleanReinicia el clip al terminar
sizenumberLado del cuadrado en px CSS; si falta, el ancho CSS del canvas (160 si tampoco hay)
tonesLogomarkTonesLos 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) => voidSe 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étodoDescripció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
MiembroDescripción
new ClipPlayer(clip, { reduced }?)Crea el reproductor para un Clip y se posiciona en el inicio
poseLa MarkPose del último seek o advance; se pasa a paintMark
accentsLos acentos vivos en ese instante, en unidades canónicas de la marca, que paintAccents pinta
lengthDuración del clip en segundos
elapsedSegundos desde el inicio, según el último seek o advance
nameEl 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ónDescripció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
Deja margen

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 lee readLogomarkTones