mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-13 15:49:53 +00:00
## What Added GSAP CDN imports to all template compositions and improved project linting to skip template placeholders. ## Why Templates were missing GSAP script imports, causing JavaScript errors when GSAP animations tried to execute. The linter was also incorrectly flagging template placeholder values like `__VIDEO_SRC__` as missing audio sources. ## How - Added `<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>` to all composition HTML files - Updated `lintAudioSrcNotFound` function to skip template placeholders matching the pattern `__[A-Z_]+__` - Fixed GSAP animation properties by adding `overwrite: "auto"` to prevent conflicts - Replaced CSS transforms with GSAP `gsap.set()` calls for better animation control - Moved video elements outside timed containers in some templates to avoid nesting issues - Standardized transcript data format to use double quotes for JSON consistency - Added proper timeline initialization with `window.__timelines = window.__timelines || {}` - Enhanced caption styling with overflow handling and max-width constraints ## Test plan - [x] Manual testing performed - [x] Verified GSAP animations work correctly in all templates - [x] Confirmed linter no longer flags template placeholders as errors - [x] Tested video playback and audio synchronization
228 lines
7.1 KiB
HTML
228 lines
7.1 KiB
HTML
<template id="scene5-logo-outro-template">
|
|
<div
|
|
data-composition-id="scene5-logo-outro"
|
|
data-width="1920"
|
|
data-height="1080"
|
|
data-duration="6"
|
|
>
|
|
<div class="canvas">
|
|
<!-- Glow effect behind the logo -->
|
|
<div class="logo-glow"></div>
|
|
|
|
<!-- Logo Container -->
|
|
<div class="logo-container">
|
|
<svg viewBox="0 0 160 240" width="160" height="240" class="logo-svg">
|
|
<!-- Figma Logo Pieces -->
|
|
<!-- Top-left: Red (#F24E1E) -->
|
|
<path
|
|
class="logo-piece logo-tl"
|
|
d="M 0 40 C 0 17.9 17.9 0 40 0 L 80 0 L 80 80 L 40 80 C 17.9 80 0 62.1 0 40 Z"
|
|
fill="#F24E1E"
|
|
/>
|
|
<!-- Top-right: Purple (#A259FF) -->
|
|
<path
|
|
class="logo-piece logo-tr"
|
|
d="M 80 0 L 120 0 C 142.1 0 160 17.9 160 40 C 160 62.1 142.1 80 120 80 L 80 80 L 80 0 Z"
|
|
fill="#A259FF"
|
|
/>
|
|
<!-- Middle-left: Pink (#FF7262) -->
|
|
<path
|
|
class="logo-piece logo-ml"
|
|
d="M 0 120 C 0 97.9 17.9 80 40 80 L 80 80 L 80 160 L 40 160 C 17.9 160 0 142.1 0 120 Z"
|
|
fill="#FF7262"
|
|
/>
|
|
<!-- Middle-right: Blue (#1ABCFE) -->
|
|
<circle class="logo-piece logo-mr" cx="120" cy="120" r="40" fill="#1ABCFE" />
|
|
<!-- Bottom-left: Green (#0ACF83) -->
|
|
<path
|
|
class="logo-piece logo-bl"
|
|
d="M 0 200 C 0 177.9 17.9 160 40 160 L 80 160 L 80 200 C 80 222.1 62.1 240 40 240 C 17.9 240 0 222.1 0 200 Z"
|
|
fill="#0ACF83"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
|
|
<div class="text-container">
|
|
<div class="tagline">Nothing great is made alone.</div>
|
|
<div class="url-pill">
|
|
<span class="url-text">figma.com</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
[data-composition-id="scene5-logo-outro"] .canvas {
|
|
width: 1920px;
|
|
height: 1080px;
|
|
background: #0a0a0f;
|
|
position: relative;
|
|
overflow: hidden;
|
|
font-family: "Inter", sans-serif;
|
|
}
|
|
|
|
[data-composition-id="scene5-logo-outro"] .logo-container {
|
|
position: absolute;
|
|
left: 880px; /* Center X: 960 - 80 */
|
|
top: 380px; /* Adjusted for tagline space: 540 - 120 - 40 */
|
|
width: 160px;
|
|
height: 240px;
|
|
z-index: 2;
|
|
}
|
|
|
|
[data-composition-id="scene5-logo-outro"] .logo-piece {
|
|
transform-origin: center center;
|
|
opacity: 0;
|
|
}
|
|
|
|
[data-composition-id="scene5-logo-outro"] .logo-glow {
|
|
position: absolute;
|
|
left: 760px; /* 960 - 200 */
|
|
top: 300px; /* 500 - 200 */
|
|
width: 400px;
|
|
height: 400px;
|
|
background: radial-gradient(circle, rgba(162, 89, 255, 0.25) 0%, rgba(10, 10, 15, 0) 70%);
|
|
border-radius: 50%;
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
z-index: 1;
|
|
}
|
|
|
|
[data-composition-id="scene5-logo-outro"] .text-container {
|
|
position: absolute;
|
|
top: 720px;
|
|
width: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
z-index: 3;
|
|
}
|
|
|
|
[data-composition-id="scene5-logo-outro"] .tagline {
|
|
font-size: 44px;
|
|
font-weight: 500;
|
|
color: white;
|
|
opacity: 0;
|
|
margin-bottom: 32px;
|
|
text-align: center;
|
|
}
|
|
|
|
[data-composition-id="scene5-logo-outro"] .url-pill {
|
|
background: #1a1a1f;
|
|
border: 1px solid #2c2c2c;
|
|
border-radius: 100px;
|
|
padding: 8px 24px;
|
|
opacity: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
[data-composition-id="scene5-logo-outro"] .url-text {
|
|
font-size: 18px;
|
|
color: #666;
|
|
letter-spacing: 0.5px;
|
|
}
|
|
</style>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
|
<script>
|
|
(function () {
|
|
const tl = gsap.timeline({ paused: true });
|
|
|
|
const pieces = {
|
|
tl: '[data-composition-id="scene5-logo-outro"] .logo-tl',
|
|
tr: '[data-composition-id="scene5-logo-outro"] .logo-tr',
|
|
ml: '[data-composition-id="scene5-logo-outro"] .logo-ml',
|
|
mr: '[data-composition-id="scene5-logo-outro"] .logo-mr',
|
|
bl: '[data-composition-id="scene5-logo-outro"] .logo-bl',
|
|
};
|
|
|
|
const pieceArray = [pieces.tl, pieces.tr, pieces.ml, pieces.mr, pieces.bl];
|
|
const logoGlow = '[data-composition-id="scene5-logo-outro"] .logo-glow';
|
|
const tagline = '[data-composition-id="scene5-logo-outro"] .tagline';
|
|
const urlPill = '[data-composition-id="scene5-logo-outro"] .url-pill';
|
|
|
|
// 1. Pieces slide in from the RIGHT (x: 1920 -> 0) with a ripple stagger (0.08s). Duration: 1.2s.
|
|
pieceArray.forEach((piece, i) => {
|
|
tl.fromTo(
|
|
piece,
|
|
{ x: 1920, opacity: 0 },
|
|
{ x: 0, opacity: 1, duration: 1.2, ease: "power4.out" },
|
|
i * 0.08,
|
|
);
|
|
});
|
|
|
|
// 2. Once at the center (960, 540), the pieces should simply EXPAND (scale 1 -> 1.15)
|
|
// and then CLOSE back to their done state (scale 1.15 -> 1).
|
|
// NO positional 'explode' or pushing to corners.
|
|
const expandTime = 1.2; // Start right after the first piece arrives, or after all?
|
|
// Let's start it after the last piece has arrived or is near arrival for a fluid motion.
|
|
// The last piece starts at 0.32s and takes 1.2s to arrive (1.52s total).
|
|
// Let's trigger the expand at 1.2s so it feels like a continuous flow.
|
|
|
|
tl.to(
|
|
pieceArray,
|
|
{
|
|
scale: 1.15,
|
|
duration: 0.5,
|
|
ease: "back.out(1.7)",
|
|
stagger: 0.02,
|
|
},
|
|
expandTime,
|
|
);
|
|
|
|
tl.to(
|
|
pieceArray,
|
|
{
|
|
scale: 1,
|
|
duration: 0.4,
|
|
ease: "power2.inOut",
|
|
},
|
|
expandTime + 0.3,
|
|
);
|
|
|
|
// 3. Subtle glow bloom effect (optional but adds polish, keeping it from original as it fits the 'expand')
|
|
tl.fromTo(
|
|
logoGlow,
|
|
{ opacity: 0, scale: 0.5 },
|
|
{ opacity: 1, scale: 1.2, duration: 0.4, ease: "power2.out" },
|
|
expandTime,
|
|
);
|
|
tl.to(
|
|
logoGlow,
|
|
{
|
|
opacity: 0,
|
|
scale: 1.8,
|
|
duration: 1.2,
|
|
ease: "power2.out",
|
|
},
|
|
expandTime + 0.4,
|
|
);
|
|
|
|
// 4. Tagline and URL pill fade in as before.
|
|
const textStartTime = expandTime + 0.8;
|
|
tl.fromTo(
|
|
tagline,
|
|
{ opacity: 0, y: 20 },
|
|
{ opacity: 1, y: 0, duration: 1, ease: "power3.out" },
|
|
textStartTime,
|
|
);
|
|
|
|
tl.fromTo(
|
|
urlPill,
|
|
{ opacity: 0, y: 15 },
|
|
{ opacity: 1, y: 0, duration: 0.8, ease: "power3.out" },
|
|
textStartTime + 0.4,
|
|
);
|
|
|
|
// 5. Remove the final pulse animation. (Omitted)
|
|
// 6. After they return to the done state, there should be NO MORE movement for the logo. (Ensured)
|
|
|
|
// Register timeline
|
|
window.__timelines = window.__timelines || {};
|
|
window.__timelines["scene5-logo-outro"] = tl;
|
|
})();
|
|
</script>
|
|
</div>
|
|
</template>
|