I finally managed to resolve the performance issues and now the product images update almost immediately even in developer mode, so hope the solution below will help others who found themselves in a similar situation. Example after fix:
https://youtu.be/MDpAy_iA3gw
To fix this, first I overrode vendor/magento/module-swatches/view/frontend/templates/product/view/renderer.phtml
by copying the file to app/design/frontend/<Vendor>/<theme>/Magento_Swatches/templates/product/view/renderer.phtml
.
Then replace the code in this file with this:
<?php /** @var $block \Magento\Swatches\Block\Product\Renderer\Configurable */ ?>
<div class="swatch-opt"></div>
<script>
require(['jquery', 'jquery/ui', 'Magento_Swatches/js/swatch-renderer'], function($) {
var oJsonConfig = <?php echo $block->getJsonConfig(); ?>;
$('.swatch-opt').SwatchRenderer({
jsonConfig: oJsonConfig,
jsonSwatchConfig: <?php /* @escapeNotVerified */ echo $swatchOptions = $block->getJsonSwatchConfig(); ?>,
mediaCallback: '<?php /* @escapeNotVerified */ //echo $block->getMediaCallback() ?>'
});
// Need to wait for gallery to be loaded before querying $('.fotorama')
$('[data-gallery-role=gallery-placeholder]').on('gallery:loaded', function() {
// NOTE: Using .one() instead of .on() to prevent duplicate bindings.
$('.fotorama').one('fotorama:ready', function(e, fotorama, extra) {
// Update product images faster when clicking on color swatch
$('.swatch-option.color').click(function() {
var sOptionId = this.getAttribute('option-id');
for (var attributeId in oJsonConfig.attributes) {
var oAttribute = oJsonConfig.attributes[attributeId];
if (oAttribute.code==='color') {
for (var i=0; i<oAttribute.options.length; ++i) {
var oOption = oAttribute.options[i];
if (oOption.id===sOptionId) {
// Gather images
var aImages = oJsonConfig.images[oOption.products[0]],
aFotoramaImages = [];
for (var j=0; j<aImages.length; ++j) {
if (aImages[j].isMain) {
// Main image (first position)
aFotoramaImages[0] = {
full: aImages[j].full,
img: aImages[j].img,
thumb: aImages[j].thumb
};
} else {
// Subsequent images
aFotoramaImages[aImages[j].position] = {
full: aImages[j].full,
img: aImages[j].img,
thumb: aImages[j].thumb
};
}
}
// Load images into Fotorama
// API reference: http://fotorama.io/customize/api/
fotorama.load(aFotoramaImages);
break;
}
}
break;
}
}
});
});
});
});
</script>
Hopefully my comments are sufficient to understand what's going on, but here are some notes that might not be obvious at first:
- I moved
jsonConfig
one scope up to the variable oJsonConfig
in order to access it outside .SwatchRenderer()
. This object contains all the image data we need to update the Fotorama plugin when the user selects a color swatch.
- The color swatch click handler needs to be attached using
.one()
instead of .on()
to prevent adding the same event handler again whenever Fotorama is loaded with new images. I'm pretty sure you can also choose to move the click handler declaration outside of fotorama:ready
and use .on()
instead.
- The statement
echo $block->getMediaCallback()
was commented out since the default callback interferes with our new click handler.