import { easeOutBack, easeOutCubic, progress } from "../easing.js"; import type { MotionTemplate } from "../template.js"; import { ctaButton, el, fitText, fontStack, fullBackground, logoEl, stringParam, svgDocument, textEl, unit, } from "../template.js"; export const outroLogo: MotionTemplate = { info: { id: "outro_logo", kind: "outro", name: "Logo outro", minDurationMs: 1500, maxDurationMs: 6000, params: [{ name: "message", type: "string", required: false }], }, frame(t, ctx) { const u = unit(ctx); const d = ctx.durationMs; const cx = ctx.width / 2; const cy = ctx.height * 0.44; const background = easeOutCubic(progress(t, 0, Math.min(500, d * 0.25))); const pop = progress(t, d * 0.1, d * 0.4); const scale = pop <= 0 ? 0 : easeOutBack(pop); const size = u * 0.3 * scale; const mark = ctx.logos.length > 0 ? logoEl(ctx, { x: cx - size / 2, y: cy - size / 2, w: size, h: size }) : el("circle", { cx, cy, r: Math.max(size / 2, 0.01), fill: ctx.kit.colors.primary }); const message = stringParam(ctx, "message") ?? ctx.kit.name; const appear = easeOutCubic(progress(t, d * 0.3, d * 0.55)); const fitted = fitText(message, ctx.width * 0.8, u * 0.07, u * 0.035); const text = textEl(fitted.text, { x: cx, y: cy + u * 0.26 + (1 - appear) * u * 0.03, "text-anchor": "middle", "font-family": fontStack(ctx.fonts.heading, "sans-serif"), "font-weight": 600, "font-size": fitted.size, fill: ctx.kit.colors.text, opacity: appear, }); return svgDocument(ctx, fullBackground(ctx, background) + mark + text); }, }; export const outroSubscribe: MotionTemplate = { info: { id: "outro_subscribe", kind: "outro", name: "Call to action outro", minDurationMs: 2000, maxDurationMs: 8000, params: [ { name: "cta", type: "string", required: true }, { name: "handle", type: "string", required: false }, ], }, frame(t, ctx) { const u = unit(ctx); const d = ctx.durationMs; const cx = ctx.width / 2; const background = easeOutCubic(progress(t, 0, Math.min(500, d * 0.2))); const logoAppear = easeOutCubic(progress(t, d * 0.05, d * 0.3)); const size = u * 0.18; const logo = logoEl( ctx, { x: cx - size / 2, y: ctx.height * 0.18, w: size, h: size }, { opacity: logoAppear }, ); const pop = progress(t, d * 0.15, d * 0.4); // A gentle 1 Hz breathing pulse once the button has landed; no brightness change. const pulse = pop >= 1 ? 1 + 0.03 * Math.sin(((t - d * 0.4) / 1000) * Math.PI * 2) : 1; const button = ctaButton( ctx, stringParam(ctx, "cta") ?? "", cx, ctx.height * 0.55, (pop <= 0 ? 0 : easeOutBack(pop)) * pulse, ); const handle = stringParam(ctx, "handle"); let handleText = ""; if (handle) { const appear = easeOutCubic(progress(t, d * 0.35, d * 0.6)); const fitted = fitText(handle, ctx.width * 0.8, u * 0.05, u * 0.03); handleText = textEl(fitted.text, { x: cx, y: ctx.height * 0.55 + u * 0.16, "text-anchor": "middle", "font-family": fontStack(ctx.fonts.body, "sans-serif"), "font-size": fitted.size, fill: ctx.kit.colors.text, opacity: appear, }); } return svgDocument(ctx, fullBackground(ctx, background) + logo + button + handleText); }, };