diff --git a/website/assets/js/components/parallax-city.component.js b/website/assets/js/components/parallax-city.component.js index c2eee40749..6f3de558ef 100644 --- a/website/assets/js/components/parallax-city.component.js +++ b/website/assets/js/components/parallax-city.component.js @@ -1,7 +1,8 @@ /** * * ----------------------------------------------------------------------------- - * A button with a built-in loading spinner. + * An image of Fleet cloud city with a slight parallax scrolling effect. + * or a static image for mobile devices and browsers with hardware acceleration disabled. * * @type {Component} * @@ -27,6 +28,7 @@ parasails.registerComponent('parallaxCity', { distanceFromTopOfPage: undefined, // Used to check if the image is within the user's viewport. distanceFromBottomOfPage: undefined, // Used to track the amount of distance between the bottom of the image, and the bottom of the page. parallaxLayersAreCurrentlyAnimating: false, + enableAnimation: true,// Whether or not to disable the parallax scrolling animation. }; }, @@ -35,14 +37,16 @@ parasails.registerComponent('parallaxCity', { // ╩ ╩ ╩ ╩ ╩╩═╝ template: `
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
`, @@ -51,28 +55,20 @@ parasails.registerComponent('parallaxCity', { // ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣ // ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝ beforeMount: function() { - + // Disable animation on mobile devices. + if(bowser.isMobile) { + this.enableAnimation = false; + } + // Check for hardware/graphics acceleration. + if(bowser.chrome || bowser.opera) { + this.enableAnimation = this.isHardwareAccelerationEnabledOnChromiumBrowsers(); + } else if(bowser.firefox){ + this.enableAnimation = this.isHardwareAccelerationEnabledOnFirefox(); + } }, mounted: async function(){ - if(!bowser.isMobile){ - // Store a reference to the parent container, we'll use this to determine the elements position relative to the user's viewport. - this.parallaxCityElement = $('div[purpose="parallax-city-container"]')[0]; - // Build an array of parallax layers, and set the initial bottom position of each layer to be negative the layer's scroll amount. - for(let layer of $('div.parallax-layer')) { - let scrollAmount = Number($(layer).attr('scroll-amount')); - $(layer).css('bottom', `-${scrollAmount}px`); - this.parallaxLayers.push({element: layer, scrollAmount}); - } - // Determine the parallax image's position on the page/user's viewport. - this.getElementPositions(); - // If the bottom of the element is within the user's viewport, update the positions of the layers. - if(this.parallaxCityElement.getBoundingClientRect().bottom > this.parallaxCityElement.offsetTop) { - this.scrollParallaxLayers(); - } - // Add a scroll event listener - $(window).scroll(this.throttleParallaxScroll); - // Add a resize event listener. - $(window).resize(this.getElementPositions); + if(!this.enableAnimation) { + this.setupParallaxAnimation(); } }, beforeDestroy: function() { @@ -89,6 +85,26 @@ parasails.registerComponent('parallaxCity', { this.distanceFromBottomOfPage = document.body.scrollHeight - this.distanceFromTopOfPage - (this.elementHeight * .5); this.elementBottomPosition = this.elementHeight + this.distanceFromTopOfPage; }, + setupParallaxAnimation: function() { + // Store a reference to the parent container, we'll use this to determine the elements position relative to the user's viewport. + this.parallaxCityElement = $('div[purpose="parallax-city-container"]')[0]; + // Build an array of parallax layers, and set the initial bottom position of each layer to be negative the layer's scroll amount. + for(let layer of $('div.parallax-layer')) { + let scrollAmount = Number($(layer).attr('scroll-amount')); + $(layer).css('bottom', `-${scrollAmount + 1}px`); + this.parallaxLayers.push({element: layer, scrollAmount}); + } + // Determine the parallax image's position on the page/user's viewport. + this.getElementPositions(); + // If the bottom of the element is within the user's viewport, update the positions of the layers. + if(this.parallaxCityElement.getBoundingClientRect().bottom > this.parallaxCityElement.offsetTop) { + this.scrollParallaxLayers(); + } + // Add a scroll event listener + $(window).scroll(this.throttleParallaxScroll); + // Add a resize event listener. + $(window).resize(this.getElementPositions); + }, scrollParallaxLayers: function() { if(!this.parallaxLayersAreCurrentlyAnimating) { this.parallaxLayersAreCurrentlyAnimating = true; @@ -111,6 +127,45 @@ parasails.registerComponent('parallaxCity', { setTimeout(()=>{ this.parallaxLayersAreCurrentlyAnimating = false; }, 100); - } + }, + isHardwareAccelerationEnabledOnChromiumBrowsers: function() { + let isHardwareAccelerationEnabled = true; + // For Chromium based browsers, we'll check the vendor of the user's graphics card. + // See https://gist.github.com/cvan/042b2448fcecefafbb6a91469484cdf8 for more info about this method. + let canvas = document.createElement('canvas'); + let webGLContext = canvas.getContext('webgl'); + if (!webGLContext) { + // If webGLContext is undefined, we'll assume the user has hardware acceleration disabled, and we won't animate the parallax layers. + isHardwareAccelerationEnabled = false; + } else { + // Otherwise, we'll check to see if the 'Vendor' of this users GPU. + let debugInfo = webGLContext.getExtension('WEBGL_debug_renderer_info'); + let vendor = webGLContext.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL); + // If vendor is "Google Inc. (Google)" or "Google Inc.", we can safely assume this user doesn't have hardware acceleration enabled and we'll disable the parallax animation. + if(vendor === 'Google Inc. (Google)' || vendor === 'Google Inc.') { + isHardwareAccelerationEnabled = false; + } + } + return isHardwareAccelerationEnabled; + }, + isHardwareAccelerationEnabledOnFirefox: function() { + // For Firefox, the method we use for chrome does not always work. + // Instead, we'll run two tests, one with forced software rendering, and one without to see if the results are the same. + // See https://stackoverflow.com/a/77170999 for more info about this method. + let canvas = document.createElement('canvas'); + let ctx = canvas.getContext('2d', { willReadFrequently: false }); + ctx.moveTo(0, 0); + ctx.lineTo(120, 121); + ctx.stroke(); + let firstTestResults = ctx.getImageData(0, 0, 200, 200).data.join(); + let canvasForSoftwareRenderingTest = document.createElement('canvas'); + let ctxWithSoftwareRendering = canvasForSoftwareRenderingTest.getContext('2d', { willReadFrequently: true });// willReadFrequently will force software rendering + ctxWithSoftwareRendering.moveTo(0, 0); + ctxWithSoftwareRendering.lineTo(120, 121); // HWA is bad at obliques + ctxWithSoftwareRendering.stroke(); + let softwareRenderingTestResults = ctxWithSoftwareRendering.getImageData(0, 0, 200, 200).data.join(); + // If the results from the software rendering test are identical to the first test, we can assume the user has hardware acceleration disabled. + return firstTestResults !== softwareRenderingTestResults; + }, } }); diff --git a/website/assets/styles/components/parallax-city.component.less b/website/assets/styles/components/parallax-city.component.less index 922db2d3f2..12f51655df 100644 --- a/website/assets/styles/components/parallax-city.component.less +++ b/website/assets/styles/components/parallax-city.component.less @@ -14,6 +14,14 @@ display: flex; flex-direction: column; justify-content: flex-end; + [purpose='static-cloud-city'] { + background-image: url('/images/parallax-cloud-city/cloud-city-static-7050x600@2x.png'); + background-size: cover; + background-position: center bottom; + background-repeat: no-repeat; + height: 456px; + position: relative; + } [purpose='parallax-city-container'] { height: 456px; position: relative; @@ -75,11 +83,17 @@ [purpose='parallax-city-container'] { height: 400px; } + [purpose='static-cloud-city'] { + height: 400px; + } } @media (max-width: 768px) { [purpose='parallax-city-container'] { height: 300px; } + [purpose='static-cloud-city'] { + height: 300px; + } } @media (max-width: 575px) { [purpose='parallax-city-container'] { @@ -92,10 +106,23 @@ display: none; } } + [purpose='static-cloud-city'] { + height: 300px; + background-image: url('/images/parallax-cloud-city/cloud-city-static-576x300@2x.png'); + background-size: cover; + background-position: center bottom; + background-repeat: no-repeat; + .parallax-layer { + display: none; + } + } } @media (max-width: 375px) { [purpose='parallax-city-container'] { height: 200px; } + [purpose='static-cloud-city'] { + height: 200px; + } } }