From 516fc7dea6aa5177a8ef5587b3ee744d27d510ba Mon Sep 17 00:00:00 2001 From: Yurii Zusik Date: Mon, 22 Mar 2021 09:41:31 +0200 Subject: [PATCH] feat(storefront): BCTHEME-446 Improve performance of analyzing homepage carousel image --- CHANGELOG.md | 1 + .../carousel/utils/getActiveSlideInfo.js | 10 ++--- .../carousel/utils/handleImageAspectRatio.js | 35 ++++++++++------- .../common/carousel/utils/handleImageLoad.js | 39 ++++++++++++++++--- assets/js/theme/common/utils/ie-helpers.js | 2 +- .../stencil/heroCarousel/_heroCarousel.scss | 9 +++++ 6 files changed, 67 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66293e25a7..a84e724278 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## Draft +- Improve performance of analyzing homepage carousel images. [#2027](https://github.com/bigcommerce/cornerstone/pull/2027) ## 5.3.0 (03-25-2021) - Remove AddThis for social sharing, replace with provider sharing links. [#1997](https://github.com/bigcommerce/cornerstone/pull/1997) diff --git a/assets/js/theme/common/carousel/utils/getActiveSlideInfo.js b/assets/js/theme/common/carousel/utils/getActiveSlideInfo.js index c5b67c770e..3cdf46b7df 100644 --- a/assets/js/theme/common/carousel/utils/getActiveSlideInfo.js +++ b/assets/js/theme/common/carousel/utils/getActiveSlideInfo.js @@ -5,16 +5,12 @@ export default ({ $slider }, isAnalyzedDataAttr) => { if (isAnalyzedSlide) return { isAnalyzedSlide }; const $activeSlideImg = $activeSlide.find('.heroCarousel-image'); - - const attrsObj = { - src: $activeSlideImg.attr('src'), - srcset: $activeSlideImg.attr('srcset'), - sizes: $activeSlideImg.attr('sizes'), - }; + const activeSlideImgNode = $activeSlideImg[0]; return { - attrsObj, $slider, $activeSlide, + $activeSlideImg, + activeSlideImgNode, }; }; diff --git a/assets/js/theme/common/carousel/utils/handleImageAspectRatio.js b/assets/js/theme/common/carousel/utils/handleImageAspectRatio.js index ef163e433e..f10ab2cdd6 100644 --- a/assets/js/theme/common/carousel/utils/handleImageAspectRatio.js +++ b/assets/js/theme/common/carousel/utils/handleImageAspectRatio.js @@ -6,7 +6,7 @@ const IMAGE_CLASSES = { }; const IS_ANALYZED_DATA_ATTR = 'image-ratio-analyzed'; -const defineClass = (imageAspectRatio) => { +const defineAspectRatioClass = (imageAspectRatio) => { switch (true) { case imageAspectRatio > 0.8 && imageAspectRatio <= 1.2: return IMAGE_CLASSES.square; @@ -17,13 +17,21 @@ const defineClass = (imageAspectRatio) => { } }; -export default ({ delegateTarget }, carousel) => { +const setAspectRatioClass = (imageNode, $slides) => { + if (imageNode.naturalHeight <= 1) return; + + const imageAspectRatio = imageNode.naturalHeight / imageNode.naturalWidth; + $slides.each((idx, slide) => $(slide).addClass(defineAspectRatioClass(imageAspectRatio))); +}; + +export default ({ delegateTarget }, carouselObj) => { const { isAnalyzedSlide, - attrsObj, $slider, $activeSlide, - } = getActiveSlideInfo(carousel || delegateTarget.slick, IS_ANALYZED_DATA_ATTR); + $activeSlideImg, + activeSlideImgNode, + } = getActiveSlideInfo(carouselObj || delegateTarget.slick, IS_ANALYZED_DATA_ATTR); if (isAnalyzedSlide) return; @@ -32,15 +40,12 @@ export default ({ delegateTarget }, carousel) => { if ($activeSlide.find('.heroCarousel-content').length) return; - $('') - .on('load', function getImageSizes() { - const imageHeight = this.height; - const imageWidth = this.width; - - if (imageHeight < 2 || imageWidth < 2) return; - - const imageAspectRatio = imageHeight / imageWidth; - $activeSlideAndClones.each((idx, slide) => $(slide).addClass(defineClass(imageAspectRatio))); - }) - .attr(attrsObj); + if (activeSlideImgNode.complete) { + if (activeSlideImgNode.naturalHeight === 1) { + // only base64 image from srcset was loaded + $activeSlideImg.on('load', () => setAspectRatioClass(activeSlideImgNode, $activeSlideAndClones)); + } else if (activeSlideImgNode.naturalHeight > 1) { + setAspectRatioClass(activeSlideImgNode, $activeSlideAndClones); + } + } else $activeSlideImg.on('load', () => setAspectRatioClass(activeSlideImgNode, $activeSlideAndClones)); }; diff --git a/assets/js/theme/common/carousel/utils/handleImageLoad.js b/assets/js/theme/common/carousel/utils/handleImageLoad.js index 66572db108..9a9f5383a8 100644 --- a/assets/js/theme/common/carousel/utils/handleImageLoad.js +++ b/assets/js/theme/common/carousel/utils/handleImageLoad.js @@ -1,20 +1,47 @@ +import { isBrowserIE } from '../../utils/ie-helpers'; import getActiveSlideInfo from './getActiveSlideInfo'; const IMAGE_ERROR_CLASS = 'is-image-error'; const IS_ANALYZED_DATA_ATTR = 'image-load-analyzed'; -export default (e, carousel) => { +const generateImage = ($slide, $image) => { + $('') + .on('error', () => $slide.addClass(IMAGE_ERROR_CLASS)) + .attr('src', $image.attr('src')); +}; + +export default (e, carouselObj) => { const { isAnalyzedSlide, - attrsObj, $activeSlide, - } = getActiveSlideInfo(carousel, IS_ANALYZED_DATA_ATTR); + $activeSlideImg, + activeSlideImgNode, + } = getActiveSlideInfo(carouselObj, IS_ANALYZED_DATA_ATTR); if (isAnalyzedSlide) return; $activeSlide.data(IS_ANALYZED_DATA_ATTR, true); - $('') - .on('error', () => $activeSlide.addClass(IMAGE_ERROR_CLASS)) - .attr(attrsObj); + if (activeSlideImgNode.complete) { + if (activeSlideImgNode.naturalHeight === 0) { + $activeSlide.addClass(IMAGE_ERROR_CLASS); + } else if (activeSlideImgNode.naturalHeight === 1) { + // only base64 image from srcset was loaded + $activeSlideImg.on('error', () => $activeSlide.addClass(IMAGE_ERROR_CLASS)); + } + + return; + } + + if (!$activeSlideImg.attr('src')) { + $activeSlide.addClass(IMAGE_ERROR_CLASS); + return; + } + + if (isBrowserIE) { + generateImage($activeSlide, $activeSlideImg); + return; + } + + $activeSlideImg.on('error', () => $activeSlide.addClass(IMAGE_ERROR_CLASS)); }; diff --git a/assets/js/theme/common/utils/ie-helpers.js b/assets/js/theme/common/utils/ie-helpers.js index c65b0c1275..cde421dc48 100644 --- a/assets/js/theme/common/utils/ie-helpers.js +++ b/assets/js/theme/common/utils/ie-helpers.js @@ -1,3 +1,3 @@ -export const isBrowserIE = navigator.userAgent.includes('Trident'); +export const isBrowserIE = !!document.documentMode; export const convertIntoArray = collection => Array.prototype.slice.call(collection); diff --git a/assets/scss/components/stencil/heroCarousel/_heroCarousel.scss b/assets/scss/components/stencil/heroCarousel/_heroCarousel.scss index c109d870eb..10cff12547 100644 --- a/assets/scss/components/stencil/heroCarousel/_heroCarousel.scss +++ b/assets/scss/components/stencil/heroCarousel/_heroCarousel.scss @@ -33,6 +33,15 @@ } } + // for IE + @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { + opacity: 0; + + &.slick-initialized { + opacity: 1; + } + } + &:not(.slick-initialized) :not(.heroCarousel-slide--first).heroCarousel-slide { display: none; }