Skins personalizadas e plugins
Mude a aparência do player, a lista de controles e adicione funcionalidades próprias.
A aparência do player — quais controles existem, em que ordem, com que tamanho, cor e ícones — é uma skin. Funcionalidades próprias (um formulário de leads, um botão de dúvidas, um painel de estatísticas) entram como plugins. Os dois mecanismos são os mesmos que o player usa internamente para seus recursos embutidos.
Skins personalizadas
Como escolher uma skin
A ordem de precedência é: opção do construtor, parâmetro de URL (?skin=minimal, com allowUrlParams: true), configuração do backend e, por fim, uma escolha automática por tipo de dispositivo.
new SpallaPlayer('#player', { skin: 'minimal' });
player.setSkin('tv'); // troca com o vídeo já tocando
player.skin; // 'tv'
SpallaPlayer.skins(); // lista todas as registradas, embarcadas e customizadasRegistrando uma skin própria
Uma skin é uma classe que estende SpallaPlayer.Skin, registrada antes de qualquer new SpallaPlayer(...) que vá usá-la:
class MinhaSkin extends SpallaPlayer.Skin {
mount() {
// roda quando a skin entra — desenhe o que for próprio dela aqui
}
destroy() {
// roda quando a skin sai — desfaça o que mount() criou
}
}
MinhaSkin.skinName = 'minha';
MinhaSkin.controls = {
desktop: ['play_pause', 'time_and_duration', 'spacer', 'plugins', 'fullscreen'],
mobile: ['play_pause', 'spacer', 'plugins', 'fullscreen'],
tv: ['play_pause', 'spacer', 'plugins', 'fullscreen'],
};
SpallaPlayer.registerSkin(MinhaSkin);
new SpallaPlayer('#player', { skin: 'minha' });Por que os campos estáticos ficam fora da classe
Atribuídos fora da declaração de propósito: a sintaxe de campo estático
de classe (static skinName = 'minha' dentro do
corpo) só chegou no Chromium 72, e o player precisa continuar funcionando
em navegadores de TV mais antigos, que não passam por nenhum processo de
build antes de carregar sua skin.
Campos estáticos da skin
| Campo | Para que serve |
|---|---|
skinName | Identificador único, usado em skin: '...' e ?skin=... |
controls | Lista de controles por dispositivo (desktop, mobile, tv) |
overflow | Itens do menu "mais opções", por dispositivo |
contextMenu | Itens do menu do botão direito |
adControls | Controles mostrados durante a reprodução de um anúncio |
styles | CSS como string — sem import './x.css' fora de um bundler |
tokens | Variáveis de tema (--shaka-* e --spalla-*), sobrescritas em :root |
icons | Troca de ícones: chave em MAIÚSCULAS troca um ícone nativo do player; em minúsculas, o de um botão de plugin |
trickPlay | Configuração de avanço/retrocesso rápido (elements, after, sempre) |
hidden | Nomes de controle que nunca aparecem, mesmo disponíveis |
seekBarColors / volumeBarColors | Cores das barras de progresso e volume |
O token plugins, citado em controls/overflow, é onde os botões dos plugins ativos entram automaticamente — sem precisar listar cada plugin por nome.
Ícones
O valor de cada entrada em icons é o atributo d de um <path> SVG, num viewBox de 24x24. Para converter um ícone de outra fonte, normalize o desenho para essa caixa antes de usar — ícones fora dela ficam desproporcionais entre si na mesma barra.
Tokens de tema
tokens sobrescreve variáveis CSS que o player já declara em :root (prefixo --shaka-) e/ou variáveis próprias (prefixo --spalla-), que herdam para qualquer elemento dentro do player:
MinhaSkin.tokens = {
'--shaka-icon-size': '44px',
'--shaka-font-color': '#dce6ff',
'--shaka-bg-menu': 'rgba(16, 22, 43, 0.95)',
'--spalla-accent': '#7ee3ff',
};Plugins
Um plugin injeta o próprio HTML e CSS no player e se desfaz sozinho quando sai — é o mecanismo usado internamente para recursos como legendas inteligentes, formulário de leads, marca d'água e estatísticas.
Ciclo de vida
- O player decide, via
shouldMount(context), se aquele plugin deve existir para o conteúdo atual (por exemplo, só em VOD, ou só quando uma opção foi ligada). - Se sim,
mount()roda uma vez — é onde o plugin desenha sua interface e se inscreve em eventos. - Quando o player é destruído (ou o plugin deixa de se aplicar), o framework
cuida da limpeza dos listeners registrados via
this.listen(...); timers e observers criados pelo plugin devem ser liberados explicitamente por ele.
Registrando um plugin
SpallaPlayer.registerPlugin(
'meu-plugin',
class extends SpallaPlayer.Plugin {
static pluginName = 'meu-plugin';
static styles = '.meu { color: tomato }';
static shouldMount({ config }) {
return config.isVod; // só existe em conteúdo VOD
}
mount() {
this.mountDOM('<div class="meu">olá</div>', 'overlay');
this.listen('timeupdate', () => {
// reage a eventos do player sem precisar desinscrever manualmente
});
}
},
);mountDOM(html, camada)insere o elemento numa das camadas visuais do player:background,content,controls,overlayoumodal— a ordem delas na tela é fixa, então um plugin emoverlaysempre fica acima de um emcontent, independente da ordem em que os plugins montam.this.listen(nomeDoEvento, handler)é a forma recomendada de ouvir eventos dentro de um plugin: a inscrição é cancelada automaticamente quando o plugin é desmontado.
Botão na barra de controles
Um plugin pode expor um botão na barra (que aparece onde a skin ativa citar o token plugins):
this.player.addControlButton({
name: this.constructor.pluginName,
label: 'Meu recurso',
icon: 'M11 18h2v-2h-2v2zm1-16A10…',
onClick: () => this.abrirPainel(),
});Opções e dependências
Qualquer plugin pode ser forçado ligado ou desligado via features no construtor, independente do que shouldMount() decidiria sozinho:
new SpallaPlayer('#player', { features: { 'meu-plugin': false } });Um plugin pode consultar outro já montado via this.player.getPlugin(nome) — trate o retorno como possivelmente undefined: a ordem e a própria existência de outro plugin dependem da configuração daquele conteúdo específico.
Exemplo completo
O repositório do player traz uma skin completa e funcional, com styles, tokens, icons e controls, e um exemplo de plugin em uso no app de React.