|
| 1 | +@use 'sass:map'; |
| 2 | +@use 'sass:list'; |
| 3 | +@use 'src/variables' as vars; |
| 4 | + |
| 5 | +/// Gets the min/max css function to constrain the size within a min and max size in px. |
| 6 | +/// @param {number} $sizePercentage - The size of the bubble as a percentage of the button size. |
| 7 | +@function sizeCalc($sizePercentage) { |
| 8 | + @return min(max(#{$minBubbleSize}, #{$sizePercentage}), #{$maxBubbleSize}); |
| 9 | +} |
| 10 | + |
| 11 | +$minBubbleSize: 30px; |
| 12 | +$maxBubbleSize: 50px; |
| 13 | +$bubbleColor: vars.$blueMedium; |
| 14 | +$animationDuration: 0.7s; |
| 15 | + |
| 16 | +// Bubble types |
| 17 | +// 1: Large solid bubble |
| 18 | +// 2: Large outlined bubble |
| 19 | +// 3: Small solid bubble |
| 20 | +$bubbleTypes: ( |
| 21 | + 1: radial-gradient(circle, $bubbleColor 20%, transparent 20%), |
| 22 | + 2: radial-gradient(circle, transparent 20%, $bubbleColor 20%, transparent 30%), |
| 23 | + 3: radial-gradient(circle, transparent 10%, $bubbleColor 15%, transparent 20%) |
| 24 | +); |
| 25 | + |
| 26 | +// prettier-ignore |
| 27 | +$bubbles: ( |
| 28 | + // Top bubbles |
| 29 | + (location: 'top', type: 1, size: 10%, pos0: ( 5% 90%), pos50: ( 0% 80%), pos100: ( 0% 70%)), |
| 30 | + (location: 'top', type: 2, size: 20%, pos0: (10% 90%), pos50: ( 0% 20%), pos100: ( 0% 10%)), |
| 31 | + (location: 'top', type: 1, size: 15%, pos0: (10% 90%), pos50: (10% 40%), pos100: (10% 30%)), |
| 32 | + (location: 'top', type: 1, size: 20%, pos0: (15% 90%), pos50: (20% 0%), pos100: (20% -10%)), |
| 33 | + (location: 'top', type: 3, size: 18%, pos0: (25% 90%), pos50: (30% 30%), pos100: (30% 20%)), |
| 34 | + (location: 'top', type: 1, size: 10%, pos0: (25% 90%), pos50: (22% 50%), pos100: (22% 40%)), |
| 35 | + (location: 'top', type: 1, size: 15%, pos0: (40% 90%), pos50: (50% 50%), pos100: (50% 40%)), |
| 36 | + (location: 'top', type: 1, size: 10%, pos0: (55% 90%), pos50: (65% 20%), pos100: (65% 10%)), |
| 37 | + (location: 'top', type: 1, size: 18%, pos0: (70% 90%), pos50: (90% 30%), pos100: (90% 20%)), |
| 38 | + // Bottom bubbles |
| 39 | + (location: 'bottom', type: 1, size: 15%, pos0: (10% -10%), pos50: ( 0% 80%), pos100: ( 0% 90%)), |
| 40 | + (location: 'bottom', type: 1, size: 20%, pos0: (30% 10%), pos50: ( 20% 80%), pos100: ( 20% 90%)), |
| 41 | + (location: 'bottom', type: 3, size: 18%, pos0: (55% -10%), pos50: ( 45% 60%), pos100: ( 45% 70%)), |
| 42 | + (location: 'bottom', type: 1, size: 20%, pos0: (70% -10%), pos50: ( 60% 100%), pos100: ( 60% 110%)), |
| 43 | + (location: 'bottom', type: 1, size: 15%, pos0: (85% -10%), pos50: ( 75% 70%), pos100: ( 75% 80%)), |
| 44 | + (location: 'bottom', type: 1, size: 10%, pos0: (70% -10%), pos50: ( 95% 60%), pos100: ( 95% 70%)), |
| 45 | + (location: 'bottom', type: 1, size: 20%, pos0: (70% 0%), pos50: (105% 0%), pos100: (110% 10%)) |
| 46 | +); |
| 47 | + |
| 48 | +// Initialize empty lists |
| 49 | +$topBubblesImages: (); |
| 50 | +$topBubblesSizes: (); |
| 51 | +$topBubblesPos0s: (); |
| 52 | +$topBubblesPos50s: (); |
| 53 | +$topBubblesPos100s: (); |
| 54 | +$bottomBubblesImages: (); |
| 55 | +$bottomBubblesSizes: (); |
| 56 | +$bottomBubblesPos0s: (); |
| 57 | +$bottomBubblesPos50s: (); |
| 58 | +$bottomBubblesPos100s: (); |
| 59 | + |
| 60 | +@each $bubble in $bubbles { |
| 61 | + $location: map.get($bubble, location); |
| 62 | + $type: map.get($bubble, type); |
| 63 | + $size: sizeCalc(map.get($bubble, size)); |
| 64 | + $pos0: map.get($bubble, pos0); |
| 65 | + $pos50: map.get($bubble, pos50); |
| 66 | + $pos100: map.get($bubble, pos100); |
| 67 | + |
| 68 | + @if $location == 'top' { |
| 69 | + $topBubblesImages: list.append($topBubblesImages, map.get($bubbleTypes, $type), comma); |
| 70 | + $topBubblesSizes: list.append($topBubblesSizes, ($size $size), comma); |
| 71 | + $topBubblesPos0s: list.append($topBubblesPos0s, $pos0, comma); |
| 72 | + $topBubblesPos50s: list.append($topBubblesPos50s, $pos50, comma); |
| 73 | + $topBubblesPos100s: list.append($topBubblesPos100s, $pos100, comma); |
| 74 | + } @else if $location == 'bottom' { |
| 75 | + $bottomBubblesImages: list.append($bottomBubblesImages, map.get($bubbleTypes, $type), comma); |
| 76 | + $bottomBubblesSizes: list.append($bottomBubblesSizes, ($size $size), comma); |
| 77 | + $bottomBubblesPos0s: list.append($bottomBubblesPos0s, $pos0, comma); |
| 78 | + $bottomBubblesPos50s: list.append($bottomBubblesPos50s, $pos50, comma); |
| 79 | + $bottomBubblesPos100s: list.append($bottomBubblesPos100s, $pos100, comma); |
| 80 | + } |
| 81 | +} |
| 82 | + |
| 83 | +.sf-bubble-button { |
| 84 | + position: relative; |
| 85 | + |
| 86 | + // Transition the scale down effect of button press |
| 87 | + transition: transform linear 50ms; |
| 88 | + |
| 89 | + &:active { |
| 90 | + transform: scale(0.95); |
| 91 | + box-shadow: 0 2px 12px -5px $bubbleColor; |
| 92 | + } |
| 93 | + |
| 94 | + // Inner span |
| 95 | + .sf-bubble-button-elements { |
| 96 | + position: absolute; |
| 97 | + top: 0; |
| 98 | + bottom: 0; |
| 99 | + left: 0; |
| 100 | + right: 0; |
| 101 | + |
| 102 | + &:before, |
| 103 | + &:after { |
| 104 | + position: absolute; |
| 105 | + content: ''; |
| 106 | + display: block; |
| 107 | + height: 100%; |
| 108 | + left: -20%; |
| 109 | + right: -20%; |
| 110 | + |
| 111 | + // Needed to make the padding increase the height as the width increases. |
| 112 | + // Long buttons need more height for the animation so the circles don't get cut off. |
| 113 | + box-sizing: content-box; |
| 114 | + padding-top: 30%; // Percentage of width of containing element |
| 115 | + |
| 116 | + z-index: -1; |
| 117 | + background-repeat: no-repeat; |
| 118 | + } |
| 119 | + |
| 120 | + &:before { |
| 121 | + display: none; |
| 122 | + bottom: 65%; |
| 123 | + background-image: $topBubblesImages; |
| 124 | + background-size: $topBubblesSizes; |
| 125 | + } |
| 126 | + |
| 127 | + &:after { |
| 128 | + display: none; |
| 129 | + top: 65%; |
| 130 | + background-image: $bottomBubblesImages; |
| 131 | + background-size: $bottomBubblesSizes; |
| 132 | + } |
| 133 | + |
| 134 | + &.sf-bubble-animate { |
| 135 | + &:before { |
| 136 | + display: block; |
| 137 | + animation: emit-bubbles-top ease-out $animationDuration forwards; |
| 138 | + } |
| 139 | + &:after { |
| 140 | + display: block; |
| 141 | + animation: emit-bubbles-bottom ease-out $animationDuration forwards; |
| 142 | + } |
| 143 | + } |
| 144 | + } |
| 145 | +} |
| 146 | + |
| 147 | +@keyframes emit-bubbles-top { |
| 148 | + 0% { |
| 149 | + background-position: $topBubblesPos0s; |
| 150 | + } |
| 151 | + 50% { |
| 152 | + background-position: $topBubblesPos50s; |
| 153 | + } |
| 154 | + 100% { |
| 155 | + background-position: $topBubblesPos100s; |
| 156 | + background-size: 0% 0%; |
| 157 | + } |
| 158 | +} |
| 159 | + |
| 160 | +@keyframes emit-bubbles-bottom { |
| 161 | + 0% { |
| 162 | + background-position: $bottomBubblesPos0s; |
| 163 | + } |
| 164 | + 50% { |
| 165 | + background-position: $bottomBubblesPos50s; |
| 166 | + } |
| 167 | + 100% { |
| 168 | + background-position: $bottomBubblesPos100s; |
| 169 | + background-size: 0% 0%; |
| 170 | + } |
| 171 | +} |
0 commit comments