| 1 |
document.addEventListener('DOMContentLoaded', function () { |
| 2 |
function typoCssVarsForEl(typo, prefix, el) { |
| 3 |
if (!typo || typeof typo !== 'object') return; |
| 4 |
var map = { |
| 5 |
family:'font-family', weight:'font-weight', style:'font-style', |
| 6 |
decoration:'text-decoration', transform:'text-transform', |
| 7 |
sizeDesktop:'font-size-d', sizeTablet:'font-size-t', sizeMobile:'font-size-m', |
| 8 |
lineHeightDesktop:'line-height-d', lineHeightTablet:'line-height-t', lineHeightMobile:'line-height-m', |
| 9 |
letterSpacingDesktop:'letter-spacing-d', letterSpacingTablet:'letter-spacing-t', letterSpacingMobile:'letter-spacing-m', |
| 10 |
wordSpacingDesktop:'word-spacing-d', wordSpacingTablet:'word-spacing-t', wordSpacingMobile:'word-spacing-m' |
| 11 |
}; |
| 12 |
Object.keys(map).forEach(function(k) { |
| 13 |
if (typo[k] !== undefined && typo[k] !== '') { |
| 14 |
var v = typo[k]; |
| 15 |
if (['sizeDesktop','sizeTablet','sizeMobile'].indexOf(k) !== -1) v = v + (typo.sizeUnit || 'px'); |
| 16 |
else if (['lineHeightDesktop','lineHeightTablet','lineHeightMobile'].indexOf(k) !== -1) v = v + (typo.lineHeightUnit || ''); |
| 17 |
else if (['letterSpacingDesktop','letterSpacingTablet','letterSpacingMobile'].indexOf(k) !== -1) v = v + (typo.letterSpacingUnit || 'px'); |
| 18 |
else if (['wordSpacingDesktop','wordSpacingTablet','wordSpacingMobile'].indexOf(k) !== -1) v = v + (typo.wordSpacingUnit || 'px'); |
| 19 |
el.style.setProperty(prefix + map[k], String(v)); |
| 20 |
} |
| 21 |
}); |
| 22 |
} |
| 23 |
|
| 24 |
document.querySelectorAll('.bkbg-cb-app').forEach(function (root) { |
| 25 |
var opts = JSON.parse(root.dataset.opts || '{}'); |
| 26 |
initConfettiButton(root, opts); |
| 27 |
}); |
| 28 |
}); |
| 29 |
|
| 30 |
function initConfettiButton(root, a) { |
| 31 |
var text = a.text || '🎉 Celebrate!'; |
| 32 |
var colors = a.colors || ['#ff6b6b','#ffd93d','#6bcb77','#4d96ff','#c77dff','#ff9f1c']; |
| 33 |
|
| 34 |
/* wrap */ |
| 35 |
var wrap = document.createElement('div'); |
| 36 |
wrap.className = 'bkbg-cb-wrap'; |
| 37 |
wrap.style.cssText = 'text-align:' + (a.align || 'center') + ';padding:24px;'; |
| 38 |
|
| 39 |
/* button */ |
| 40 |
var isLink = a.url && a.url.length > 0; |
| 41 |
var btn = document.createElement(isLink ? 'a' : 'button'); |
| 42 |
btn.className = 'bkbg-cb-btn'; |
| 43 |
|
| 44 |
if (isLink) { |
| 45 |
btn.href = a.url; |
| 46 |
if (a.openInNewTab) { btn.target = '_blank'; btn.rel = 'noopener noreferrer'; } |
| 47 |
} |
| 48 |
|
| 49 |
var label = (a.iconBefore ? a.iconBefore + ' ' : '') + text + (a.iconAfter ? ' ' + a.iconAfter : ''); |
| 50 |
/* Icon before */ |
| 51 |
var IP = window.bkbgIconPicker; |
| 52 |
var beforeType = a.iconBeforeType || 'custom-char'; |
| 53 |
var afterType = a.iconAfterType || 'custom-char'; |
| 54 |
var hasBefore = beforeType !== 'custom-char' ? true : !!a.iconBefore; |
| 55 |
var hasAfter = afterType !== 'custom-char' ? true : !!a.iconAfter; |
| 56 |
|
| 57 |
if (hasBefore) { |
| 58 |
var bSpan = document.createElement('span'); |
| 59 |
bSpan.className = 'bkbg-cb-icon-before'; |
| 60 |
if (beforeType !== 'custom-char' && IP) { |
| 61 |
bSpan.appendChild(IP.buildFrontendIcon(beforeType, a.iconBefore, a.iconBeforeDashicon, a.iconBeforeImageUrl, a.iconBeforeDashiconColor)); |
| 62 |
} else { |
| 63 |
bSpan.textContent = a.iconBefore; |
| 64 |
} |
| 65 |
btn.appendChild(bSpan); |
| 66 |
} |
| 67 |
var lblSpan = document.createElement('span'); |
| 68 |
lblSpan.textContent = text; |
| 69 |
btn.appendChild(lblSpan); |
| 70 |
if (hasAfter) { |
| 71 |
var aSpan = document.createElement('span'); |
| 72 |
aSpan.className = 'bkbg-cb-icon-after'; |
| 73 |
if (afterType !== 'custom-char' && IP) { |
| 74 |
aSpan.appendChild(IP.buildFrontendIcon(afterType, a.iconAfter, a.iconAfterDashicon, a.iconAfterImageUrl, a.iconAfterDashiconColor)); |
| 75 |
} else { |
| 76 |
aSpan.textContent = a.iconAfter; |
| 77 |
} |
| 78 |
btn.appendChild(aSpan); |
| 79 |
} |
| 80 |
|
| 81 |
var shadow = a.showShadow ? '0 8px 24px ' + (a.buttonBg || '#6366f1') + '55, 0 4px 8px rgba(0,0,0,0.15)' : 'none'; |
| 82 |
|
| 83 |
btn.style.cssText = [ |
| 84 |
'display:' + (a.fullWidth ? 'block' : 'inline-block'), |
| 85 |
a.fullWidth ? 'width:100%' : (a.maxWidth ? 'max-width:' + a.maxWidth + 'px' : ''), |
| 86 |
'background:' + (a.buttonBg || '#6366f1'), |
| 87 |
'color:' + (a.buttonColor || '#ffffff'), |
| 88 |
'border-radius:' + (a.buttonRadius || 12) + 'px', |
| 89 |
'padding:' + (a.buttonPaddingV || 16) + 'px ' + (a.buttonPaddingH || 40) + 'px', |
| 90 |
'box-shadow:' + shadow, |
| 91 |
'font-family:inherit', |
| 92 |
'cursor:pointer', |
| 93 |
'border:none', |
| 94 |
'outline:none', |
| 95 |
'text-decoration:none', |
| 96 |
'position:relative', |
| 97 |
'overflow:hidden', |
| 98 |
].filter(Boolean).join(';'); |
| 99 |
|
| 100 |
typoCssVarsForEl(a.typoButton, '--bkcb-btn-', root); |
| 101 |
|
| 102 |
/* hover effect */ |
| 103 |
btn.addEventListener('mouseenter', function () { |
| 104 |
btn.style.background = a.buttonHoverBg || '#4f46e5'; |
| 105 |
btn.style.transform = 'translateY(-2px)'; |
| 106 |
}); |
| 107 |
btn.addEventListener('mouseleave', function () { |
| 108 |
btn.style.background = a.buttonBg || '#6366f1'; |
| 109 |
btn.style.transform = 'translateY(0)'; |
| 110 |
}); |
| 111 |
|
| 112 |
/* ripple */ |
| 113 |
btn.addEventListener('click', function (e) { |
| 114 |
var ripple = document.createElement('span'); |
| 115 |
ripple.className = 'bkbg-cb-ripple'; |
| 116 |
var size = Math.max(btn.offsetWidth, btn.offsetHeight); |
| 117 |
ripple.style.width = ripple.style.height = size + 'px'; |
| 118 |
var rect = btn.getBoundingClientRect(); |
| 119 |
ripple.style.left = (e.clientX - rect.left - size / 2) + 'px'; |
| 120 |
ripple.style.top = (e.clientY - rect.top - size / 2) + 'px'; |
| 121 |
btn.appendChild(ripple); |
| 122 |
setTimeout(function () { ripple.remove(); }, 600); |
| 123 |
}); |
| 124 |
|
| 125 |
wrap.appendChild(btn); |
| 126 |
|
| 127 |
/* sub text */ |
| 128 |
if (a.showSubText && a.subText) { |
| 129 |
var sub = document.createElement('p'); |
| 130 |
sub.className = 'bkbg-cb-sub'; |
| 131 |
sub.textContent = a.subText; |
| 132 |
wrap.appendChild(sub); |
| 133 |
} |
| 134 |
|
| 135 |
root.appendChild(wrap); |
| 136 |
|
| 137 |
/* confetti engine */ |
| 138 |
var firing = false; |
| 139 |
|
| 140 |
btn.addEventListener('click', function (e) { |
| 141 |
if (firing && !a.repeat) return; |
| 142 |
e.preventDefault(); /* prevent nav during confetti if URL set */ |
| 143 |
|
| 144 |
btn.classList.add('bkbg-cb-firing'); |
| 145 |
setTimeout(function () { btn.classList.remove('bkbg-cb-firing'); }, 200); |
| 146 |
|
| 147 |
var btnRect = btn.getBoundingClientRect(); |
| 148 |
var originX = (btnRect.left + btnRect.width / 2) / window.innerWidth; |
| 149 |
var originY = (btnRect.top + btnRect.height / 2) / window.innerHeight; |
| 150 |
|
| 151 |
fireConfetti(a, colors, originX, originY); |
| 152 |
firing = true; |
| 153 |
|
| 154 |
setTimeout(function () { |
| 155 |
firing = false; |
| 156 |
if (isLink && a.url) { |
| 157 |
if (a.openInNewTab) window.open(a.url, '_blank'); |
| 158 |
else window.location.href = a.url; |
| 159 |
} |
| 160 |
}, 300); |
| 161 |
}); |
| 162 |
} |
| 163 |
|
| 164 |
function fireConfetti(a, colors, originX, originY) { |
| 165 |
var canvas = document.createElement('canvas'); |
| 166 |
canvas.className = 'bkbg-cb-canvas'; |
| 167 |
document.body.appendChild(canvas); |
| 168 |
canvas.width = window.innerWidth; |
| 169 |
canvas.height = window.innerHeight; |
| 170 |
var ctx = canvas.getContext('2d'); |
| 171 |
var style = a.confettiStyle || 'burst'; |
| 172 |
var count = a.particleCount || 120; |
| 173 |
var spread = (a.spread || 70) * Math.PI / 180; |
| 174 |
var grav = a.gravity || 1; |
| 175 |
var pSize = a.particleSize || 8; |
| 176 |
var shapes = a.shapes || 'mixed'; |
| 177 |
var duration = a.duration || 3000; |
| 178 |
|
| 179 |
var particles = []; |
| 180 |
|
| 181 |
function randomBetween(lo, hi) { return lo + Math.random() * (hi - lo); } |
| 182 |
|
| 183 |
function makeParticle(ox, oy, angleBase) { |
| 184 |
var angle = angleBase + randomBetween(-spread / 2, spread / 2); |
| 185 |
var speed = randomBetween(4, 14); |
| 186 |
var shap; |
| 187 |
if (shapes === 'rect') shap = 'rect'; |
| 188 |
else if (shapes === 'circle') shap = 'circle'; |
| 189 |
else if (shapes === 'star') shap = 'star'; |
| 190 |
else shap = Math.random() > 0.5 ? 'rect' : 'circle'; |
| 191 |
|
| 192 |
return { |
| 193 |
x: ox * canvas.width, |
| 194 |
y: oy * canvas.height, |
| 195 |
vx: Math.cos(angle) * speed, |
| 196 |
vy: Math.sin(angle) * speed, |
| 197 |
w: randomBetween(pSize * 0.5, pSize * 1.5), |
| 198 |
h: randomBetween(pSize * 0.5, pSize * 1.5), |
| 199 |
color: colors[Math.floor(Math.random() * colors.length)], |
| 200 |
shape: shap, |
| 201 |
rotation: randomBetween(0, Math.PI * 2), |
| 202 |
rotSpeed: randomBetween(-0.15, 0.15), |
| 203 |
opacity: 1, |
| 204 |
life: randomBetween(0.6, 1), |
| 205 |
}; |
| 206 |
} |
| 207 |
|
| 208 |
/* emit per style */ |
| 209 |
if (style === 'burst' || style === 'cannon') { |
| 210 |
var base = style === 'cannon' ? -Math.PI / 2 : 0; |
| 211 |
var spreadFull = style === 'cannon' ? spread : Math.PI * 2; |
| 212 |
for (var i = 0; i < count; i++) { |
| 213 |
var ang = base + (i / count) * spreadFull; |
| 214 |
particles.push(makeParticle(originX, originY, ang)); |
| 215 |
} |
| 216 |
} else if (style === 'rain') { |
| 217 |
for (var i = 0; i < count; i++) { |
| 218 |
particles.push(makeParticle(Math.random(), -0.05, Math.PI / 2)); |
| 219 |
} |
| 220 |
} else if (style === 'sides') { |
| 221 |
for (var i = 0; i < count; i++) { |
| 222 |
var side = i % 2 === 0 ? 0 : 1; |
| 223 |
particles.push(makeParticle(side, randomBetween(0.3, 0.7), side === 0 ? 0 : Math.PI)); |
| 224 |
} |
| 225 |
} else if (style === 'spiral') { |
| 226 |
for (var i = 0; i < count; i++) { |
| 227 |
var ang2 = (i / count) * Math.PI * 4; |
| 228 |
particles.push(makeParticle(originX, originY, ang2)); |
| 229 |
} |
| 230 |
} else { |
| 231 |
for (var i = 0; i < count; i++) { |
| 232 |
particles.push(makeParticle(originX, originY, (i / count) * Math.PI * 2)); |
| 233 |
} |
| 234 |
} |
| 235 |
|
| 236 |
var startTime = performance.now(); |
| 237 |
var raf; |
| 238 |
|
| 239 |
function drawStar(ctx, x, y, r) { |
| 240 |
ctx.save(); |
| 241 |
ctx.beginPath(); |
| 242 |
for (var i = 0; i < 5; i++) { |
| 243 |
var a = (i * 4 * Math.PI / 5) - Math.PI / 2; |
| 244 |
if (i === 0) ctx.moveTo(x + r * Math.cos(a), y + r * Math.sin(a)); |
| 245 |
else ctx.lineTo(x + r * Math.cos(a), y + r * Math.sin(a)); |
| 246 |
} |
| 247 |
ctx.closePath(); |
| 248 |
ctx.fill(); |
| 249 |
ctx.restore(); |
| 250 |
} |
| 251 |
|
| 252 |
function tick(ts) { |
| 253 |
var elapsed = ts - startTime; |
| 254 |
ctx.clearRect(0, 0, canvas.width, canvas.height); |
| 255 |
|
| 256 |
particles.forEach(function (p) { |
| 257 |
if (p.opacity <= 0) return; |
| 258 |
p.x += p.vx; |
| 259 |
p.y += p.vy; |
| 260 |
p.vy += grav * 0.3; |
| 261 |
p.vx *= 0.99; |
| 262 |
p.rotation += p.rotSpeed; |
| 263 |
p.opacity = Math.max(0, p.life - elapsed / duration); |
| 264 |
|
| 265 |
ctx.save(); |
| 266 |
ctx.globalAlpha = p.opacity; |
| 267 |
ctx.fillStyle = p.color; |
| 268 |
ctx.translate(p.x, p.y); |
| 269 |
ctx.rotate(p.rotation); |
| 270 |
|
| 271 |
if (p.shape === 'circle') { |
| 272 |
ctx.beginPath(); |
| 273 |
ctx.arc(0, 0, p.w / 2, 0, Math.PI * 2); |
| 274 |
ctx.fill(); |
| 275 |
} else if (p.shape === 'star') { |
| 276 |
drawStar(ctx, 0, 0, p.w / 2); |
| 277 |
} else { |
| 278 |
ctx.fillRect(-p.w / 2, -p.h / 2, p.w, p.h); |
| 279 |
} |
| 280 |
ctx.restore(); |
| 281 |
}); |
| 282 |
|
| 283 |
if (elapsed < duration + 800) { |
| 284 |
raf = requestAnimationFrame(tick); |
| 285 |
} else { |
| 286 |
canvas.remove(); |
| 287 |
} |
| 288 |
} |
| 289 |
|
| 290 |
raf = requestAnimationFrame(tick); |
| 291 |
} |
| 292 |
|