Instantly share code, notes, and snippets.
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
-
Save akovardin/98f2c8a8d8b33474335d9c9a12a72a7f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package rtbresponse | |
| type BannerData struct { | |
| ImpressionTracker []string | |
| ClickTracker []string | |
| Target string | |
| Icon string | |
| Image string | |
| Title string | |
| Description string | |
| Cta string | |
| W string | |
| H string | |
| } | |
| var bannerTemplate = ` | |
| <!DOCTYPE html> | |
| <html lang="ru"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>{{.Title}}</title> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| -webkit-tap-highlight-color: transparent; | |
| } | |
| body { | |
| width: {{.W}}px; | |
| height: {{.H}}px; | |
| overflow: hidden; | |
| font-family: Arial, sans-serif; | |
| cursor: pointer; | |
| position: relative; | |
| background: transparent; | |
| } | |
| #banner { | |
| width: 100%; | |
| height: 100%; | |
| position: relative; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| } | |
| .background-image { | |
| width: 100%; | |
| height: 100%; | |
| object-fit: cover; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| } | |
| .content { | |
| position: relative; | |
| z-index: 2; | |
| width: 100%; | |
| height: 100%; | |
| display: flex; | |
| align-items: center; | |
| {{if eq .H "50"}} | |
| justify-content: space-between; | |
| padding: 5px 10px; | |
| {{else if eq .H "250"}} | |
| flex-direction: column; | |
| justify-content: center; | |
| text-align: center; | |
| padding: 20px; | |
| {{else}} | |
| flex-direction: column; | |
| justify-content: center; | |
| text-align: center; | |
| padding: 40px; | |
| {{end}} | |
| color: white; | |
| } | |
| .text-content { | |
| {{if eq .H "50"}} | |
| flex: 1; | |
| {{else}} | |
| margin-bottom: 15px; | |
| {{end}} | |
| } | |
| .title { | |
| font-weight: bold; | |
| text-shadow: 1px 1px 2px rgba(0,0,0,0.5); | |
| {{if eq .H "50"}} | |
| font-size: 14px; | |
| margin-bottom: 2px; | |
| {{else if eq .H "250"}} | |
| font-size: 20px; | |
| margin-bottom: 8px; | |
| {{else}} | |
| font-size: 24px; | |
| margin-bottom: 12px; | |
| {{end}} | |
| } | |
| .description { | |
| opacity: 0.9; | |
| text-shadow: 1px 1px 2px rgba(0,0,0,0.5); | |
| {{if eq .H "50"}} | |
| font-size: 10px; | |
| {{else if eq .H "250"}} | |
| font-size: 14px; | |
| {{else}} | |
| font-size: 16px; | |
| {{end}} | |
| } | |
| .cta-button { | |
| background: #FF6B35; | |
| color: white; | |
| border: none; | |
| border-radius: 25px; | |
| font-weight: bold; | |
| box-shadow: 0 2px 4px rgba(0,0,0,0.3); | |
| cursor: pointer; | |
| {{if eq .H "50"}} | |
| padding: 6px 12px; | |
| font-size: 11px; | |
| margin-left: 10px; | |
| {{else if eq .H "250"}} | |
| padding: 10px 24px; | |
| font-size: 16px; | |
| {{else}} | |
| padding: 12px 32px; | |
| font-size: 18px; | |
| {{end}} | |
| } | |
| .logo { | |
| flex-shrink: 0; | |
| {{if eq .H "50"}} | |
| width: 32px; | |
| height: 32px; | |
| margin-right: 8px; | |
| {{else if eq .H "250"}} | |
| width: 64px; | |
| height: 64px; | |
| margin-bottom: 15px; | |
| {{else}} | |
| width: 80px; | |
| height: 80px; | |
| margin-bottom: 20px; | |
| {{end}} | |
| } | |
| .logo img { | |
| width: 100%; | |
| height: 100%; | |
| object-fit: contain; | |
| border-radius: {{if eq .H "50"}}6px{{else}}12px{{end}}; | |
| } | |
| /* Анимации */ | |
| @keyframes fadeIn { | |
| from { opacity: 0; } | |
| to { opacity: 1; } | |
| } | |
| @keyframes slideIn { | |
| from { transform: translateX(-100%); } | |
| to { transform: translateX(0); } | |
| } | |
| @keyframes scaleIn { | |
| from { transform: scale(0.8); opacity: 0; } | |
| to { transform: scale(1); opacity: 1; } | |
| } | |
| .animate-fadeIn { | |
| animation: fadeIn 0.8s ease-out; | |
| } | |
| .animate-slideIn { | |
| animation: slideIn 0.6s ease-out; | |
| } | |
| .animate-scaleIn { | |
| animation: scaleIn 0.7s ease-out; | |
| } | |
| /* Tracking pixels */ | |
| .tracking-pixel { | |
| position: absolute; | |
| width: 1px; | |
| height: 1px; | |
| opacity: 0; | |
| pointer-events: none; | |
| } | |
| /* MRAID states */ | |
| .loading, .default, .expanded, .resized, .hidden { | |
| display: none; | |
| } | |
| .default { | |
| display: block; | |
| } | |
| /* Специфичные стили для больших баннеров */ | |
| {{if ne .H "50"}} | |
| .features { | |
| display: flex; | |
| justify-content: center; | |
| gap: 20px; | |
| margin: 20px 0; | |
| flex-wrap: wrap; | |
| } | |
| .feature { | |
| text-align: center; | |
| flex: 1; | |
| min-width: 80px; | |
| } | |
| .feature-icon { | |
| font-size: 24px; | |
| margin-bottom: 5px; | |
| } | |
| .feature-text { | |
| font-size: 12px; | |
| opacity: 0.9; | |
| } | |
| {{end}} | |
| </style> | |
| </head> | |
| <body> | |
| <div id="loadingState" class="loading">Loading...</div> | |
| <div id="defaultState" class="default"> | |
| <div id="banner"> | |
| {{if ne .H "50"}} | |
| <!-- Фоновое изображение для больших баннеров --> | |
| {{if .Image}} | |
| <img src="{{.Image}}" alt="{{.Title}}" class="background-image animate-fadeIn"> | |
| {{end}} | |
| {{end}} | |
| <div class="content"> | |
| {{if or (eq .H "250") (eq .H "480")}} | |
| <!-- Для больших баннеров логотип сверху --> | |
| <div class="logo animate-scaleIn"> | |
| <img src="{{.Icon}}" alt="{{.Title}}"> | |
| </div> | |
| {{else}} | |
| <!-- Для маленького баннера логотип слева --> | |
| <div class="logo"> | |
| <img src="{{.Icon}}" alt="{{.Title}}"> | |
| </div> | |
| {{end}} | |
| <div class="text-content"> | |
| <div class="title {{if eq .H "50"}}animate-slideIn{{else}}animate-scaleIn{{end}}">{{.Title}}</div> | |
| <div class="description">{{.Description}}</div> | |
| {{if and (ne .H "50") .Description}} | |
| <!-- Дополнительный контент для больших баннеров --> | |
| {{/* <div class="features"> | |
| <div class="feature"> | |
| <div class="feature-icon">⭐</div> | |
| <div class="feature-text">Рейтинг 4.8</div> | |
| </div> | |
| <div class="feature"> | |
| <div class="feature-icon">📥</div> | |
| <div class="feature-text">50M+ скачиваний</div> | |
| </div> | |
| <div class="feature"> | |
| <div class="feature-icon">🎮</div> | |
| <div class="feature-text">Бесплатно</div> | |
| </div> | |
| </div> */}} | |
| {{end}} | |
| </div> | |
| {{if eq .H "50"}} | |
| <!-- Для маленького баннера кнопка справа --> | |
| <button class="cta-button">{{.Cta}}</button> | |
| {{else}} | |
| <!-- Для больших баннеров кнопка под текстом --> | |
| <button class="cta-button animate-scaleIn">{{.Cta}}</button> | |
| {{end}} | |
| </div> | |
| <!-- Multiple impression trackers --> | |
| {{range .ImpressionTracker}} | |
| <img src="{{.}}" alt="" class="tracking-pixel"> | |
| {{end}} | |
| </div> | |
| </div> | |
| <script> | |
| // MRAID compliance | |
| (function() { | |
| 'use strict'; | |
| let mraid = window.mraid; | |
| let isMRAID = !!(mraid && mraid.getState); | |
| function trackClick() { | |
| const clickUrls = [{{range .ClickTracker}}'{{.}}',{{end}}]; | |
| clickUrls.forEach(url => { | |
| if (url) { | |
| const pixel = new Image(); | |
| pixel.src = url; | |
| } | |
| }); | |
| } | |
| function handleClick() { | |
| trackClick(); | |
| if (isMRAID) { | |
| // Используем MRAID для открытия | |
| mraid.open('{{.Target}}'); | |
| } else { | |
| // Fallback для web | |
| window.open('{{.Target}}', '_blank'); | |
| } | |
| } | |
| // MRAID инициализация | |
| function init() { | |
| // Добавляем обработчик клика | |
| document.getElementById('banner').addEventListener('click', handleClick); | |
| if (!isMRAID) { | |
| // Не MRAID среда - запускаем сразу | |
| showDefaultState(); | |
| return; | |
| } | |
| // Ждем ready событие MRAID | |
| if (mraid.getState() === 'loading') { | |
| mraid.addEventListener('ready', onMRAIDReady); | |
| } else { | |
| onMRAIDReady(); | |
| } | |
| // Слушаем изменения состояния | |
| mraid.addEventListener('stateChange', onStateChange); | |
| mraid.addEventListener('error', onMRAIDError); | |
| } | |
| function onMRAIDReady() { | |
| showDefaultState(); | |
| } | |
| function onStateChange(state) { | |
| showDefaultState() | |
| } | |
| function onMRAIDError(message, action) { | |
| showDefaultState(); | |
| } | |
| function showDefaultState() { | |
| hideAllStates(); | |
| document.getElementById('defaultState').style.display = 'block'; | |
| } | |
| function hideAllStates() { | |
| const states = ['loadingState', 'defaultState']; | |
| states.forEach(stateId => { | |
| document.getElementById(stateId).style.display = 'none'; | |
| }); | |
| } | |
| // Запуск | |
| document.addEventListener('DOMContentLoaded', function() { | |
| init(); | |
| }); | |
| })(); | |
| </script> | |
| </body> | |
| </html> | |
| ` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment