mirror of
https://github.com/fleetdm/fleet
synced 2026-05-24 09:28:54 +00:00
Website: update <parallax-city> component to conditionally disable animation (#17816)
Closes: #17482 Changes: - Updated the `<parallax-city>` component to display a static image if a user's browser has hardware/graphics acceleration disabled.
This commit is contained in:
parent
f0e3259765
commit
14c312361b
2 changed files with 112 additions and 30 deletions
|
|
@ -1,7 +1,8 @@
|
|||
/**
|
||||
* <parallax-city>
|
||||
* -----------------------------------------------------------------------------
|
||||
* 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: `
|
||||
<div>
|
||||
<div purpose="parallax-city-container">
|
||||
<div class="parallax-layer" purpose="background-cloud-2" scroll-amount=4></div>
|
||||
<div class="parallax-layer" purpose="background-cloud-1" scroll-amount=6></div>
|
||||
<div class="parallax-layer" purpose="small-island-2" scroll-amount=16></div>
|
||||
<div class="parallax-layer" purpose="small-island-1" scroll-amount=12></div>
|
||||
<div class="parallax-layer" purpose="large-island" scroll-amount=24></div>
|
||||
<div class="parallax-layer" purpose="foreground-cloud-2" scroll-amount=32></div>
|
||||
<div class="parallax-layer" purpose="foreground-cloud-1" scroll-amount=40></div>
|
||||
<div purpose="parallax-city-container" v-if="enableAnimation">
|
||||
<div class="parallax-layer" purpose="background-cloud-2" scroll-amount="4"></div>
|
||||
<div class="parallax-layer" purpose="background-cloud-1" scroll-amount="6"></div>
|
||||
<div class="parallax-layer" purpose="small-island-2" scroll-amount="16"></div>
|
||||
<div class="parallax-layer" purpose="small-island-1" scroll-amount="12"></div>
|
||||
<div class="parallax-layer" purpose="large-island" scroll-amount="24"></div>
|
||||
<div class="parallax-layer" purpose="foreground-cloud-2" scroll-amount="32"></div>
|
||||
<div class="parallax-layer" purpose="foreground-cloud-1" scroll-amount="40"></div>
|
||||
</div>
|
||||
<div purpose="static-cloud-city" v-else>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
|
|
@ -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;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue