diff --git a/offerzen/global/company/easy-steps-scroll@1.0.css b/offerzen/global/company/easy-steps-scroll@1.0.css new file mode 100644 index 0000000..74cb2f4 --- /dev/null +++ b/offerzen/global/company/easy-steps-scroll@1.0.css @@ -0,0 +1,166 @@ +.easy-steps { + margin: auto; + max-width: 1200px; + padding: 0 20px; + display: none; + padding-top: 100px; +} + +.easy-steps.visible { + display: block !important; +} + +.easy-steps-content { + display: flex; + gap: 20px; + flex-direction: row; + margin-top: -220px; + margin-bottom: -20vh; +} + +.easy-steps-content>* { + flex: 1 0; +} + +.easy-steps-header { + position: sticky; + top: 60px; + backdrop-filter: blur(4px); + background: #ffffffb0; + margin: 0; + text-align: center; + z-index: 1; + margin-bottom: 220px; +} + +.easy-steps-header h2 { + margin: 0 auto; + max-width: 650px; + line-height: 120%; + font-weight: 700; + font-size: 28px; + color: #0e204b; + padding: 20px; +} + +@media (min-width: 700px) and (min-height: 600px) { + .easy-steps-header h2 { + font-size: 40px; + } +} + +.easy-steps-header h2 strong { + color: #2f96f4; +} + +.easy-steps-list-image { + position: sticky; + top: 0; + background: #d8deff; + max-height: 100vh; + display: none; +} + +@media (min-width: 700px) and (min-height: 600px) { + .easy-steps-list-image { + display: block; + } +} + +.easy-steps-list-image .easy-steps-list-item { + position: absolute; + padding: 30px; +} + +.easy-steps-list-image .easy-steps-list-item__image { + background: rgba(0, 0, 0, 0.3); + max-width: 100%; + box-shadow: -1px -1px 0 3px rgba(0, 0, 0, 0.3); + border-radius: 5px; + transform-origin: center top; + overflow: hidden; + transition: transform 0.2s ease-out; +} + +@media (min-width: 700px) and (min-height: 600px) { + .easy-steps-list-text { + padding-top: 30vh; + padding-bottom: 30vh; + } +} + +.easy-steps-list-text .easy-steps-list-item { + display: flex; + flex-direction: column; + justify-content: flex-start; + padding-bottom: 10vh; + align-items: center; +} + +@media (min-width: 700px) and (min-height: 600px) { + .easy-steps-list-text .easy-steps-list-item { + height: 80vh; + } + + .easy-steps-list-text .easy-steps-list-item:last-child { + height: 40vh; + } +} + +.easy-steps-list-text .easy-steps-list-item__container { + max-width: 600px; +} + +.easy-steps-list-text .easy-steps-list-item__mobile { + max-width: 100%; +} + +@media (min-width: 700px) and (min-height: 600px) { + .easy-steps-list-text .easy-steps-list-item__mobile { + display: none; + } +} + +.easy-steps-list-text .easy-steps-list-item__mobile-image { + max-width: 100%; +} + +.easy-steps-list-text .easy-steps-list-item__number { + margin-right: 12px; +} + +.easy-steps-list-text .easy-steps-list-item__title { + display: flex; + align-items: center; +} + +.easy-steps-list-text .easy-steps-list-item__number { + border-radius: 50px; + color: #fff; + display: inline-flex; + justify-content: center; + align-items: center; + background: #1e8dee; + min-width: 29px; + min-height: 29px; + border: 3px solid #000; + box-shadow: inset 1px 1px 3px 0 rgba(255, 255, 255, 0.7); +} + +@media (min-width: 700px) and (min-height: 600px) { + .easy-steps-list-text .easy-steps-list-item:first-child { + padding-top: 20vh; + } +} + +.easy-steps-cta { + display: flex; + justify-content: center; + align-items: center; + text-align: center; + padding: 20px; + position: relative; + z-index: 1; + background: #fff; + height: 220px; +} diff --git a/offerzen/global/company/easy-steps-scroll@1.0.js b/offerzen/global/company/easy-steps-scroll@1.0.js new file mode 100644 index 0000000..6b0779c --- /dev/null +++ b/offerzen/global/company/easy-steps-scroll@1.0.js @@ -0,0 +1,83 @@ +window.$loaded(function (window, document, $, undefined) { + const stepsEl = $('[data-js="steps"]') + const textContentEl = $('[data-js="text-list"]') + const textListItemEls = textContentEl.find('[data-js="text-list-item"]') + const imageContentEl = $('[data-js="image-list"]') + const imageListItemEls = imageContentEl.find('[data-js="image-list-item"]') + + function clamp(val, min = 0, max = 1) { + return Math.max(min, Math.min(val, max)) + } + + function clampPercent(val, min, max) { + const clampedVal = clamp(val, min, max) + + const area = max - min + return (clampedVal - min) / area + } + + function domain(val, [min, max], [toMin, toMax]) { + val = clamp(val, min, max) + const range = ((val - min) / (max - min)) * (toMax - toMin) + toMin + return range + } + + function update(index, textNode) { + const deadZoneAbove = 0 // percent + const deadZoneBelow = 0.1 // percent + const textContainerEl = $(textNode) + const textEl = textContainerEl.find(':first-child') // wrapping div + const imageEl = $(imageListItemEls[index]) + const imageInnerEl = imageEl.find(':first-child') + const scrollY = window.scrollY + const position = textEl.offset().top + textEl.height() / 2 - scrollY // position of middle of content + const scrollArea = window.innerHeight + const positionPercent = clamp(position / scrollArea) // percent of text el on screen (0 = top, 1 = bottom) + const fadeSpeed = 3 // integer, higher is faster + const scrollSpeed = 3 // integer, higher is faster + const imageTargetPercent = 0.4 + let adjustedPositionPercent = 1 + let opacity = 0 + let scale = 1 + + if (positionPercent < imageTargetPercent) { + adjustedPositionPercent = + clampPercent(positionPercent, 0, imageTargetPercent - deadZoneAbove) - 1 + opacity = 1 + adjustedPositionPercent + scale = Math.pow(domain(opacity, [0, 1], [0.8, 1]), 2) + } else { + adjustedPositionPercent = clampPercent( + positionPercent, + 0.5 + deadZoneBelow, + 1 + ) + adjustedPositionPercent = Math.pow(adjustedPositionPercent, scrollSpeed) + opacity = 1 - domain(adjustedPositionPercent, [0, 1], [0, 0.6]) + } + opacity = Math.pow(opacity, fadeSpeed) + opacity = domain(opacity, [0, 1], [0.3, 1]) + + const imageAreaPadding = 20 // move image offscreen by this amount + const imageOffset = -imageEl.height() / 2 // center image to imageTargetPercent + const imageArea = scrollArea * (1 - imageTargetPercent) + const adjustedImageArea = imageArea - imageOffset + imageAreaPadding // account for offset and add some padding + const imageTarget = scrollArea * imageTargetPercent + const imagePosition = + imageTarget + adjustedImageArea * adjustedPositionPercent + imageOffset + + imageEl.css({ + transform: `translate3d(0, ${imagePosition}px, 1px)`, + opacity + }) + imageInnerEl.css({ + transform: `scale(${scale})` + }) + } + + // init + textListItemEls.each(update) + window.addEventListener('resize', (e) => textListItemEls.each(update)) + document.addEventListener('scroll', (e) => textListItemEls.each(update)) + + stepsEl.addClass('visible') +})