Saltar al contenido principal

Exportación de archivos

Paquete: @tiendana/utils/logomark

Archivos del logomark para quien no puede leer tokens CSS: stickers animados y fragmentos de vídeo como APNG, WebP animado o GIF, cada fotograma como PNG en un zip, cualquier still como PNG o WebP, y las expresiones de p18 como SVG. Los fotogramas los pinta paintMark a partir de ClipPlayer, así que un archivo es exactamente lo que reproduce el elemento.

Sin código

Para descargar los archivos ya hechos, en cualquier formato y tamaño, usa Descargas del logomark: genera con estas mismas funciones, en el navegador, también los videos para CapCut.

Características​

✅ APNG - Un archivo, con alfa y sin pérdida; ffmpeg lo lee para vídeo
✅ WebP animado - Lo que reproduce Android de forma nativa y aceptan las apps de chat
✅ GIF - Lo reproduce cualquier app; transparencia de 1 bit
✅ Secuencia PNG - Todos los fotogramas numerados en un zip, para editores y ffmpeg
✅ Stills - Cualquier still, con sus acentos, como PNG o WebP sin pérdida
✅ SVG - Las cuatro expresiones de p18 desde los paths originales
✅ Encuadre común o ajustado - Todo un set a la misma escala, o cada archivo lleno con su expresión
✅ Progreso y cancelación - onProgress y un AbortSignal

Entorno

Los exportadores dibujan en un OffscreenCanvas, así que se ejecutan en el navegador o en un worker. Para generar archivos desde Node, el repositorio incluye desde 0.5.0 un script de stickers que pinta con Skia.

Opciones: ClipExport​

Los exportadores animados reciben el mismo objeto:

CampoTipoDescripción
nameClipNameEl clip a exportar
fpsnumberFotogramas por segundo; el GIF admite hasta 50
sizenumberLado del fotograma, en px
tonesLogomarkTonesLos tonos con que se pinta; ver Colourways
style'flat' | 'sticker'flat por defecto; sticker añade el contorno troquelado y necesita tones.keyline
framing'shared' | 'tight'shared por defecto; ver El encuadre
loopbooleanEn un bucle se descarta el último fotograma, que repite el primero; salvo en un clip que holds, cuyo último fotograma es su expresión
playsnumberVeces que se reproduce el archivo; 0, por defecto, para siempre. Un clip que holds (como form-error) se lee mejor una sola vez, terminando en su expresión
onProgress(done, total) => voidSe llama a medida que avanzan los fotogramas
signalAbortSignalDetiene la exportación entre dos fotogramas; la promesa se rechaza con el motivo de la señal
import { clipApng, readLogomarkTones } from '@tiendana/utils/logomark';

const el = document.querySelector('tdn-logomark')!; // con las propiedades --logomark-* fijadas por la página
const tones = readLogomarkTones(el);

const blob = await clipApng({ name: 'hello', fps: 30, size: 512, tones, style: 'sticker', loop: true });
Tonos

readLogomarkTones(el) solo lee las propiedades --logomark-* que la página fija (con el mixin de Colourways o con CSS); el atributo colourway no las fija. Sin esas propiedades, pasa LOGOMARK_COLOURWAYS.primary (desde 0.5.0) o tonos literales, por ejemplo primary: { face: '#00D47E', body: '#1B434C', track: '#DBE1E2', error: '#F54F4F', keyline: '#FFFFFF', sun: '#FF7834', sunLight: '#FFDECC' }. Sin sun y sunLight, halloween, christmas y warning pierden sus tonos sol (ver Colourways). Ver readLogomarkTones.

Opciones: StillExport​

stillImage recibe name (StillName), size, tones, style y framing como arriba, y type: 'image/png' por defecto o 'image/webp' (sin pérdida).

Funciones​

FunciónDevuelveDescripción
clipApng(options)Promise<Blob> (image/apng)El clip como PNG animado, a fps
clipWebp(options)Promise<Blob> (image/webp)El clip como WebP animado sin pérdida, a fps
clipGif(options)Promise<Blob> (image/gif)El clip como GIF, a fps (hasta 50)
clipPngSequence(options)Promise<Blob> (application/zip)Cada fotograma como PNG numerado (<clip>-000.png...) en un zip sin compresión
stillImage(options)Promise<Blob>Un still, con sus acentos, como PNG o WebP
clipFramePng(player, t, size, tones, style?, type?)Promise<Blob>Un fotograma del clip en t segundos, en el cuadro común, con alfa: PNG, o WebP sin pérdida si type es 'image/webp'
expressionSvg(expression, tones)stringUna de las cuatro expresiones de p18 como SVG, desde los paths originales
clipFrameCount(name, fps)numberFotogramas de un clip a fps, con ambos extremos incluidos: el primero y el último son SMILE
import { clipFrameCount, clipGif, clipPngSequence, clipWebp, stillImage } from '@tiendana/utils/logomark';

clipFrameCount('hello', 30); // 46: Math.round(1.5 * 30) + 1

const webp = await clipWebp({ name: 'hello', fps: 30, size: 512, tones, loop: true });
const gif = await clipGif({ name: 'celebrate', fps: 25, size: 256, tones, style: 'sticker', framing: 'tight', loop: true });
const once = await clipGif({ name: 'form-error', fps: 25, size: 256, tones, plays: 1 });
const zip = await clipPngSequence({ name: 'congrat', fps: 24, size: 512, tones });
const png = await stillImage({ name: 'love', size: 1024, tones, style: 'sticker', framing: 'tight' });

Progreso y cancelación​

const controller = new AbortController();

const webp = await clipWebp({
name: 'christmas', fps: 30, size: 1024, tones, loop: true,
onProgress: (done, total) => (bar.value = done / total),
signal: controller.signal,
});

cancelButton.onclick = () => controller.abort();

GIF​

El GIF usa una paleta para todos los fotogramas: los 255 colores más pintados, que son los tonos del colourway, y el resto del antialiasing en su color más cercano. Su transparencia es de 1 bit: cada píxel se pinta o no, así que el borde del troquelado se ve escalonado sobre fondos oscuros. GIF mide el tiempo en centésimas de segundo y los navegadores ralentizan los fotogramas de menos de dos, así que fps admite hasta 50; 25 fps es exacto.

  • PNG y APNG se codifican en la librería, no con el canvas: Chrome codifica el PNG de un canvas solo en tiempo libre, que una pestaña en segundo plano recibe una vez por segundo.
  • WebP usa el codificador sin pérdida del canvas. Safari no codifica WebP: clipWebp, y stillImage y clipFramePng con 'image/webp', lanzan this browser cannot encode image/webp.

Stills en SVG​

expressionSvg(expression, tones) acepta 'smile', 'congrat', 'play' o 'error' (el tipo ExportExpression) y devuelve el SVG como texto. En ERROR la sonrisa y la X usan tones.error, o el tono de la cara si no hay.

import { expressionSvg } from '@tiendana/utils/logomark';

const svg = expressionSvg('congrat', tones);

El encuadre​

framing decide dónde queda la marca en el cuadrado del archivo:

framingLa marcaÚsalo para
'shared' (por defecto)Siempre al mismo tamaño, más o menos la mitad del archivoUn set de stickers que se ven juntos, como el pack de WhatsApp
'tight'Lo más grande que cabe: el cuadrado rodea lo que pinta ese clip o still, con el contorno y un 4 % de aireUn archivo suelto: una publicación, un video, un icono pequeño

El cuadro común​

Todo export shared usa el mismo cuadrado, para que un conjunto de stickers dibuje la marca al mismo tamaño.

FunciónDescripción
exportFrame()Devuelve { span, focus }: el lado del cuadrado y su centro, en las unidades de la marca
measureExportFrame()Recalcula ese cuadrado reproduciendo todos los clips; es lo que exportFrame() debe igualar

exportFrame() es la unión de lo que pinta cada clip (la marca por su rig, los pops y los disfraces incluidos, y cada accent a su máximo), ampliada por el contorno del sticker y con un margen del 4 % del cuadro en cada lado. Se guarda como constante para que el primer logomark de una página no tenga que reproducir todos los clips; el test del repositorio falla si measureExportFrame() deja de coincidir. measureExportFrame() es costosa y está pensada para ese test, no para uso en producción.

Colocar la marca​

FunciónDescripción
exportPlacement(size)La marca en un cuadrado de size px, en el cuadro común
clipPlacement(name, size, framing?)La marca en el export del clip name; con 'tight' mide el clip una vez y lo recuerda
stillPlacement(name, size, framing?)La marca en el export del still name
clipBounds(name) / stillBounds(name)Lo que pinta un clip a lo largo de toda su duración, o un still, en unidades de la marca

Úsalas para previsualizar un archivo antes de generarlo, pintando con paintMark en la misma colocación.

Ver También​