1/** 2 * Copyright 2016 Google Inc. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 */ 17 18function initializeParallax(clip) { 19 var parallax = clip.querySelectorAll('*[parallax]'); 20 var parallaxDetails = []; 21 var sticky = false; 22 23 // Edge requires a transform on the document body and a fixed position element 24 // in order for it to properly render the parallax effect as you scroll. 25 // See https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/5084491/ 26 if (getComputedStyle(document.body).transform == 'none') 27 document.body.style.transform = 'translateZ(0)'; 28 var fixedPos = document.createElement('div'); 29 fixedPos.style.position = 'fixed'; 30 fixedPos.style.top = '0'; 31 fixedPos.style.width = '1px'; 32 fixedPos.style.height = '1px'; 33 fixedPos.style.zIndex = 1; 34 document.body.insertBefore(fixedPos, document.body.firstChild); 35 36 for (var i = 0; i < parallax.length; i++) { 37 var elem = parallax[i]; 38 var container = elem.parentNode; 39 if (getComputedStyle(container).overflow != 'visible') { 40 console.error('Need non-scrollable container to apply perspective for', elem); 41 continue; 42 } 43 if (clip && container.parentNode != clip) { 44 console.warn('Currently we only track a single overflow clip, but elements from multiple clips found.', elem); 45 } 46 var clip = container.parentNode; 47 if (getComputedStyle(clip).overflow == 'visible') { 48 console.error('Parent of sticky container should be scrollable element', elem); 49 } 50 // TODO(flackr): optimize to not redo this for the same clip/container. 51 var perspectiveElement; 52 if (sticky || getComputedStyle(clip).webkitOverflowScrolling) { 53 sticky = true; 54 perspectiveElement = container; 55 } else { 56 perspectiveElement = clip; 57 container.style.transformStyle = 'preserve-3d'; 58 } 59 perspectiveElement.style.perspectiveOrigin = 'bottom right'; 60 perspectiveElement.style.perspective = '1px'; 61 if (sticky) 62 elem.style.position = '-webkit-sticky'; 63 if (sticky) 64 elem.style.top = '0'; 65 elem.style.transformOrigin = 'bottom right'; 66 67 // Find the previous and next elements to parallax between. 68 var previousCover = parallax[i].previousElementSibling; 69 while (previousCover && previousCover.hasAttribute('parallax')) 70 previousCover = previousCover.previousElementSibling; 71 var nextCover = parallax[i].nextElementSibling; 72 while (nextCover && !nextCover.hasAttribute('parallax-cover')) 73 nextCover = nextCover.nextElementSibling; 74 75 parallaxDetails.push({'node': parallax[i], 76 'top': parallax[i].offsetTop, 77 'sticky': !!sticky, 78 'nextCover': nextCover, 79 'previousCover': previousCover}); 80 } 81 82 // Add a scroll listener to hide perspective elements when they should no 83 // longer be visible. 84 clip.addEventListener('scroll', function() { 85 for (var i = 0; i < parallaxDetails.length; i++) { 86 var container = parallaxDetails[i].node.parentNode; 87 var previousCover = parallaxDetails[i].previousCover; 88 var nextCover = parallaxDetails[i].nextCover; 89 var parallaxStart = previousCover ? (previousCover.offsetTop + previousCover.offsetHeight) : 0; 90 var parallaxEnd = nextCover ? nextCover.offsetTop : container.offsetHeight; 91 var threshold = 200; 92 var visible = parallaxStart - threshold - clip.clientHeight < clip.scrollTop && 93 parallaxEnd + threshold > clip.scrollTop; 94 // FIXME: Repainting the images while scrolling can cause jank. 95 // For now, keep them all. 96 // var display = visible ? 'block' : 'none' 97 var display = 'block'; 98 if (parallaxDetails[i].node.style.display != display) 99 parallaxDetails[i].node.style.display = display; 100 } 101 }); 102 window.addEventListener('resize', onResize.bind(null, parallaxDetails)); 103 onResize(parallaxDetails); 104 for (var i = 0; i < parallax.length; i++) { 105 parallax[i].parentNode.insertBefore(parallax[i], parallax[i].parentNode.firstChild); 106 } 107} 108 109function onResize(details) { 110 for (var i = 0; i < details.length; i++) { 111 var container = details[i].node.parentNode; 112 113 var clip = container.parentNode; 114 var previousCover = details[i].previousCover; 115 var nextCover = details[i].nextCover; 116 var rate = details[i].node.getAttribute('parallax'); 117 118 var parallaxStart = previousCover ? (previousCover.offsetTop + previousCover.offsetHeight) : 0; 119 var scrollbarWidth = details[i].sticky ? 0 : clip.offsetWidth - clip.clientWidth; 120 var parallaxElem = details[i].sticky ? container : clip; 121 var height = details[i].node.offsetHeight; 122 var depth = 0; 123 if (rate) { 124 depth = 1 - (1 / rate); 125 } else { 126 var parallaxEnd = nextCover ? nextCover.offsetTop : container.offsetHeight; 127 depth = (height - parallaxEnd + parallaxStart) / (height - clip.clientHeight); 128 } 129 if (details[i].sticky) 130 depth = 1.0 / depth; 131 132 var scale = 1.0 / (1.0 - depth); 133 134 // The scrollbar is included in the 'bottom right' perspective origin. 135 var dx = scrollbarWidth * (scale - 1); 136 // Offset for the position within the container. 137 var dy = details[i].sticky ? 138 -(clip.scrollHeight - parallaxStart - height) * (1 - scale) : 139 (parallaxStart - depth * (height - clip.clientHeight)) * scale; 140 141 details[i].node.style.transform = 'scale(' + (1 - depth) + ') translate3d(' + dx + 'px, ' + dy + 'px, ' + depth + 'px)'; 142 } 143}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.