| 1 |
(function () { |
| 2 |
'use strict'; |
| 3 |
|
| 4 |
function typoCssVarsForEl(typo, prefix, el) { |
| 5 |
if (!typo || typeof typo !== 'object') return; |
| 6 |
var map = { |
| 7 |
family:'font-family', weight:'font-weight', style:'font-style', |
| 8 |
decoration:'text-decoration', transform:'text-transform', |
| 9 |
sizeDesktop:'font-size-d', sizeTablet:'font-size-t', sizeMobile:'font-size-m', |
| 10 |
lineHeightDesktop:'line-height-d', lineHeightTablet:'line-height-t', lineHeightMobile:'line-height-m', |
| 11 |
letterSpacingDesktop:'letter-spacing-d', letterSpacingTablet:'letter-spacing-t', letterSpacingMobile:'letter-spacing-m', |
| 12 |
wordSpacingDesktop:'word-spacing-d', wordSpacingTablet:'word-spacing-t', wordSpacingMobile:'word-spacing-m' |
| 13 |
}; |
| 14 |
Object.keys(map).forEach(function(k) { |
| 15 |
if (typo[k] !== undefined && typo[k] !== '') { |
| 16 |
var v = typo[k]; |
| 17 |
if (['sizeDesktop','sizeTablet','sizeMobile'].indexOf(k) !== -1) v = v + (typo.sizeUnit || 'px'); |
| 18 |
else if (['lineHeightDesktop','lineHeightTablet','lineHeightMobile'].indexOf(k) !== -1) v = v + (typo.lineHeightUnit || ''); |
| 19 |
else if (['letterSpacingDesktop','letterSpacingTablet','letterSpacingMobile'].indexOf(k) !== -1) v = v + (typo.letterSpacingUnit || 'px'); |
| 20 |
else if (['wordSpacingDesktop','wordSpacingTablet','wordSpacingMobile'].indexOf(k) !== -1) v = v + (typo.wordSpacingUnit || 'px'); |
| 21 |
el.style.setProperty(prefix + map[k], String(v)); |
| 22 |
} |
| 23 |
}); |
| 24 |
} |
| 25 |
|
| 26 |
var reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches; |
| 27 |
|
| 28 |
/* ── Compute transform per card ──────────────────────────────────── */ |
| 29 |
function getTransform(idx, total, spreadAngle, spreadDist, hovered) { |
| 30 |
var mid = (total - 1) / 2; |
| 31 |
var diff = idx - mid; |
| 32 |
var angle = diff * spreadAngle; |
| 33 |
var tx = diff * 18; |
| 34 |
var ty = Math.abs(diff) * 4; |
| 35 |
var scale = 1 - Math.abs(diff) * 0.03; |
| 36 |
if (hovered) { |
| 37 |
tx = diff * spreadDist; |
| 38 |
angle = 0; |
| 39 |
ty = 0; |
| 40 |
scale = 1; |
| 41 |
} |
| 42 |
return 'translateX(' + tx + 'px) translateY(' + ty + 'px) rotate(' + angle + 'deg) scale(' + scale + ')'; |
| 43 |
} |
| 44 |
|
| 45 |
function initBlock(root) { |
| 46 |
var app = root.querySelector('.bkbg-his-app'); |
| 47 |
if (!app) return; |
| 48 |
|
| 49 |
var raw = app.getAttribute('data-opts') || '{}'; |
| 50 |
var o; |
| 51 |
try { o = JSON.parse(raw); } catch (e) { o = {}; } |
| 52 |
|
| 53 |
var images = Array.isArray(o.images) ? o.images : []; |
| 54 |
var imageWidth = Number(o.imageWidth) || 200; |
| 55 |
var imageHeight = Number(o.imageHeight) || 280; |
| 56 |
var imageRadius = Number(o.imageRadius) || 16; |
| 57 |
var spreadAngle = Number(o.spreadAngle) || 15; |
| 58 |
var spreadDist = Number(o.spreadDist) || 60; |
| 59 |
var borderWidth = Number(o.borderWidth) || 3; |
| 60 |
var borderColor = o.borderColor || '#ffffff'; |
| 61 |
var shadowAlpha = (Number(o.shadowStrength) / 100 || 0.3).toFixed(2); |
| 62 |
var showCaption = !!o.showCaption; |
| 63 |
var captionSize = Number(o.captionSize) || 13; |
| 64 |
var captionColor = o.captionColor || '#0f172a'; |
| 65 |
var captionBg = o.captionBg || '#ffffff'; |
| 66 |
var bgColor = o.bgColor || 'transparent'; |
| 67 |
|
| 68 |
if (images.length === 0) return; |
| 69 |
|
| 70 |
/* typography CSS vars */ |
| 71 |
typoCssVarsForEl(o.captionTypo, '--bkbg-his-cap-', root); |
| 72 |
if (captionSize && captionSize !== 13) root.style.setProperty('--bkbg-his-cap-sz', captionSize + 'px'); |
| 73 |
|
| 74 |
/* ── Outer wrapper ─────────────────────────────────────────── */ |
| 75 |
var outer = document.createElement('div'); |
| 76 |
outer.className = 'bkbg-his-outer'; |
| 77 |
outer.style.background = bgColor; |
| 78 |
|
| 79 |
/* ── Stack ──────────────────────────────────────────────────── */ |
| 80 |
var stack = document.createElement('div'); |
| 81 |
stack.className = 'bkbg-his-stack'; |
| 82 |
var total = images.length; |
| 83 |
var mid = (total - 1) / 2; |
| 84 |
stack.style.height = imageHeight + 'px'; |
| 85 |
stack.style.minWidth = (imageWidth + total * Math.max(spreadDist, 30)) + 'px'; |
| 86 |
|
| 87 |
var cards = images.map(function (img, i) { |
| 88 |
var card = document.createElement('div'); |
| 89 |
card.className = 'bkbg-his-card'; |
| 90 |
card.style.cssText = [ |
| 91 |
'width:' + imageWidth + 'px', |
| 92 |
'height:' + imageHeight + 'px', |
| 93 |
'border-radius:' + imageRadius + 'px', |
| 94 |
'border:' + borderWidth + 'px solid ' + borderColor, |
| 95 |
'box-shadow:0 8px 32px rgba(0,0,0,' + shadowAlpha + ')', |
| 96 |
'z-index:' + i, |
| 97 |
'transform:' + getTransform(i, total, spreadAngle, 0, false) |
| 98 |
].join(';'); |
| 99 |
|
| 100 |
var imgEl = document.createElement('img'); |
| 101 |
imgEl.src = img.url || ''; |
| 102 |
imgEl.alt = img.alt || ''; |
| 103 |
imgEl.loading = 'lazy'; |
| 104 |
imgEl.style.cssText = 'width:100%;height:100%;object-fit:cover;display:block;pointer-events:none'; |
| 105 |
card.appendChild(imgEl); |
| 106 |
|
| 107 |
if (showCaption && img.caption) { |
| 108 |
var cap = document.createElement('div'); |
| 109 |
cap.className = 'bkbg-his-caption'; |
| 110 |
cap.textContent = img.caption; |
| 111 |
cap.style.color = captionColor; |
| 112 |
cap.style.background = captionBg; |
| 113 |
card.appendChild(cap); |
| 114 |
} |
| 115 |
|
| 116 |
stack.appendChild(card); |
| 117 |
return card; |
| 118 |
}); |
| 119 |
|
| 120 |
outer.appendChild(stack); |
| 121 |
app.appendChild(outer); |
| 122 |
|
| 123 |
if (reduced) return; |
| 124 |
|
| 125 |
/* ── Hover interaction ──────────────────────────────────────── */ |
| 126 |
stack.addEventListener('mouseenter', function () { |
| 127 |
cards.forEach(function (card, i) { |
| 128 |
card.style.transform = getTransform(i, total, spreadAngle, spreadDist, true); |
| 129 |
card.style.zIndex = total - i; |
| 130 |
card.style.boxShadow = '0 12px 40px rgba(0,0,0,' + Math.min(parseFloat(shadowAlpha) * 1.5, 0.6) + ')'; |
| 131 |
}); |
| 132 |
}); |
| 133 |
|
| 134 |
stack.addEventListener('mouseleave', function () { |
| 135 |
cards.forEach(function (card, i) { |
| 136 |
card.style.transform = getTransform(i, total, spreadAngle, 0, false); |
| 137 |
card.style.zIndex = i; |
| 138 |
card.style.boxShadow = '0 8px 32px rgba(0,0,0,' + shadowAlpha + ')'; |
| 139 |
}); |
| 140 |
}); |
| 141 |
} |
| 142 |
|
| 143 |
function init() { |
| 144 |
document.querySelectorAll('.wp-block-blockenberg-hovering-image-stack').forEach(initBlock); |
| 145 |
} |
| 146 |
|
| 147 |
if (document.readyState === 'loading') { |
| 148 |
document.addEventListener('DOMContentLoaded', init); |
| 149 |
} else { |
| 150 |
init(); |
| 151 |
} |
| 152 |
})(); |
| 153 |
|