Corner Logo Bug
The small channel logo that sits in the corner of the screen. It fades in and rests a little see-through so it never gets in the way. Every so often it brightens, a glint of light sweeps across it and a star twinkles on its corner.
Pixel TV
How to use it
- Paste the snippet just before
</head>. - Add
data-corner-logo-bugto your logo (text, an<img>or an<svg>), and style it as you like. Round corners on it round the glint too. - Add
data-corner="top-right"to pin it to that corner of its nearest positioned parent, or place it yourself.
Options
Add any of these attributes to the same element.
data-corner | top-right, top-left, bottom-right or bottom-left. |
data-inset | Gap from the corner, with data-corner. Default 4%. |
data-every | Time between glints. Default 6000 ms. |
data-opacity | How see-through it rests. Default 0.8. |
data-spark="false" | Leave out the twinkling star. |
Call animacious.cornerLogoBug(el) to glint it now, for example when a segment starts.
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/anime.umd.min.js"></script>
<script>
(function () {
// Corner Logo Bug: the small channel logo that sits in the corner of a
// broadcast. It fades in, then rests slightly see-through; every so often it
// brightens, a glint of light sweeps across it and a little star twinkles
// on its corner.
// Usage: <div data-corner-logo-bug>PIXEL TV</div>
// Put any logo inside (text, an <img> or an <svg>). With data-corner it
// places itself in that corner of its nearest positioned parent; without
// it, place it yourself.
// Optional: data-corner ("top-right", "top-left", "bottom-right" or
// "bottom-left"), data-inset (gap from the corner, default "4%"),
// data-every (ms between glints, default 6000), data-opacity (resting
// opacity, default 0.8), data-spark="false" (no twinkling star).
// Script use: animacious.cornerLogoBug(el) glints it now.
var DEFAULTS = { every: 6000, opacity: 0.8, inset: '4%' };
var css =
'.clb{display:inline-flex;align-items:center;isolation:isolate}' +
'.clb-clip{position:absolute;inset:0;border-radius:inherit;overflow:hidden;pointer-events:none;z-index:2}' +
'.clb-glint{position:absolute;top:-30%;bottom:-30%;left:0;width:45%;transform:translateX(-300%) skewX(-22deg);' +
'background:linear-gradient(90deg,transparent,rgba(255,255,255,.12) 30%,rgba(255,255,255,.6) 50%,rgba(255,255,255,.12) 70%,transparent)}' +
'.clb-glint2{width:14%;opacity:.7}' +
'.clb-spark{position:absolute;right:-0.45em;top:-0.45em;width:0.9em;height:0.9em;z-index:3;pointer-events:none;fill:#fff;filter:drop-shadow(0 0 0.25em rgba(255,255,255,.9));opacity:0}';
var style = document.createElement('style');
style.textContent = css;
document.head.appendChild(style);
var STAR = '<svg class="clb-spark" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 0c.6 5.6 4.4 9.4 10 10-5.6.6-9.4 4.4-10 10-.6-5.6-4.4-9.4-10-10C5.6 9.4 9.4 5.6 10 0z"/></svg>';
function reduced() { return window.matchMedia('(prefers-reduced-motion: reduce)').matches; }
function num(el, k) { var v = el.dataset[k]; return v !== undefined && v !== '' ? +v : DEFAULTS[k]; }
function setup(el) {
var d = el.dataset;
el.classList.add('clb');
if (getComputedStyle(el).position === 'static') el.style.position = 'relative';
if (d.corner) {
var inset = d.inset || DEFAULTS.inset;
var c = d.corner.split('-');
el.style.position = 'absolute';
el.style[c[0] === 'bottom' ? 'bottom' : 'top'] = inset;
el.style[c[1] === 'left' ? 'left' : 'right'] = inset;
el.style.zIndex = '4';
}
el.insertAdjacentHTML('beforeend',
'<span class="clb-clip" aria-hidden="true"><span class="clb-glint"></span><span class="clb-glint clb-glint2"></span></span>' +
(d.spark === 'false' ? '' : STAR));
el._clb = {
glints: [].slice.call(el.querySelectorAll('.clb-glint')),
spark: el.querySelector('.clb-spark'),
vis: false
};
if (reduced()) el.style.opacity = String(num(el, 'opacity'));
else anime.utils.set(el, { opacity: 0 });
}
function glint(el) {
var s = el._clb;
var rest = num(el, 'opacity');
var tl = anime.createTimeline();
var w = el.offsetWidth;
tl.add(el, { opacity: [rest, 1, 1, rest], scale: [1, 1.04, 1], duration: 1500, ease: 'inOut(2)' }, 0);
s.glints.forEach(function (g, i) {
var gw = g.offsetWidth + el.offsetHeight; // clear the slant too
anime.utils.set(g, { x: -gw, skewX: -22 });
tl.add(g, { x: [-gw, w + el.offsetHeight], duration: 900, ease: 'inOut(2)' }, 150 + i * 110);
});
if (s.spark) {
tl.add(s.spark, {
opacity: [0, 1, 0], scale: [0, 1.15, 0], rotate: [0, 180],
duration: 760, ease: 'inOut(2)'
}, 720);
}
return tl;
}
function schedule(el) {
var s = el._clb;
clearTimeout(s.wait);
s.wait = setTimeout(function () {
if (!s.vis) { s.pending = true; return; }
glint(el);
schedule(el);
}, num(el, 'every'));
}
function enter(el) {
var rest = num(el, 'opacity');
anime.animate(el, { opacity: [0, rest], scale: [0.85, 1], duration: 700, ease: 'out(3)' });
setTimeout(function () { glint(el); schedule(el); }, 800);
}
window.animacious = window.animacious || {};
window.animacious.cornerLogoBug = function (el) {
if (el && el._clb && !reduced()) glint(el);
};
function init() {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
var el = e.target, s = el._clb;
s.vis = e.isIntersecting;
if (!s.vis) return;
if (!s.started) { s.started = true; enter(el); }
else if (s.pending) { s.pending = false; glint(el); schedule(el); }
});
}, { threshold: 0.2 });
document.querySelectorAll('[data-corner-logo-bug]').forEach(function (el) {
if (el._clb) return;
setup(el);
if (!reduced()) io.observe(el);
});
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
else init();
})();
</script><div style="position: relative">
<video src="show.mp4" autoplay muted></video>
<div class="logo" data-corner-logo-bug data-corner="top-right">Pixel TV</div>
</div>