How To Add 3d Image Gallery in Shopify [Free – Without APP]

Learn how to add a 3D Image Gallery in Shopify – for FREE & without any apps! 🚀 In this step-by-step tutorial, I’ll show you how to create a stunning interactive 3D gallery to showcase your products or images effortlessly. No coding skills needed! 🎨✨

🔹 100% Free – No paid apps required
🔹 Easy setup & fully customizable
🔹 Works on any Shopify store

💡 Watch now and make your Shopify store stand out with a beautiful 3D gallery! 🎥👇

Section Code:

Create a new section with the following code:

{% schema %}
{
  "name": "3D Gallery",
  "settings": [
    {
      "type": "header",
      "content": "Gallery Settings"
    },
    {
      "type": "range",
      "id": "scroll_per_item",
      "min": 300,
      "max": 1000,
      "step": 50,
      "unit": "px",
      "label": "Scroll Per Item",
      "default": 500
    },
    {
      "type": "range",
      "id": "distance",
      "min": 500,
      "max": 2500,
      "step": 100,
      "unit": "px",
      "label": "3D Distance",
      "default": 1500
    }
  ],
  "blocks": [
    {
      "type": "gallery_image",
      "name": "Gallery Image",
      "limit": 10,
      "settings": [
        {
          "type": "image_picker",
          "id": "image",
          "label": "Image"
        }
      ]
    }
  ],
  "presets": [
    {
      "name": "3D Gallery",
      "blocks": [
        {
          "type": "gallery_image"
        },
        {
          "type": "gallery_image"
        },
        {
          "type": "gallery_image"
        }
      ]
    }
  ]
}
{% endschema %}
{%- style -%}
  .gallery-wrapper {
    --smooth-scroll: true;
    position: relative;
  }
  .gsap-gallery {
    height: 100vh;
    background: #000000;
    width: 100%;
    position: relative;
    overflow: hidden;
  }
  .gallery-container {
    position: relative;
    height: 100vh;
    width: 100%;
  }
  .mdw-image-gallery-bg {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    overflow: hidden;
    opacity: 0.6;
  }
  .mdw-image-gallery-bg img {
    position: absolute;
    left: 0;
    top: 0;
    filter: blur(50px);
    transform: scale(1.125);
    max-width: unset;
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0;
    transition: opacity 0.5s ease;
  }
  .gsap-gallery-image {
    height: 100vh;
    width: 100%;
    position: relative;
    transform-style: preserve-3d;
    perspective: 1000px;
  }
  .gsap-gallery .e-con {
    position: absolute;
    top: 50%;
    left: 70%;
    transform: translate(-50%, -50%) translateZ(0);
    opacity: 0;
    width: 460px;
    height: 460px;
    transition: transform 0.5s ease, opacity 0.5s ease;
  }
  .gsap-gallery .e-con:nth-child(even) {
    left: 30%;
  }
  .gallery-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
  .gallery-container.is-fixed {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
  }
  .gallery-container.is-bottom {
    position: absolute;
    bottom: 0;
    top: auto;
  }
  @media (max-width: 1024px) {
    .gsap-gallery .e-con {
      left: 80%;
    }

    .gsap-gallery .e-con:nth-child(even) {
      left: 20%;
    }
  }
  @media (max-width: 768px) {
    .gsap-gallery,
    .gallery-container,
    .gsap-gallery-image {
      height: auto;
    }

    .gsap-gallery {
      padding: 20px;
    }

    .gsap-gallery .e-con {
      position: relative;
      transform: none !important;
      opacity: 1 !important;
      margin: 0 auto 20px;
      width: 100% !important;
      max-width: 460px;
      left: 0 !important;
      top: 0;
    }

    .gsap-gallery .e-con:nth-child(even) {
      left: 0 !important;
    }

    .gallery-container.is-fixed {
      position: relative;
    }
  }
{%- endstyle -%}
<div class="gallery-wrapper">
  <div
    class="gsap-gallery"
    data-scroll-per-item="{{ section.settings.scroll_per_item }}"
    data-distance="{{ section.settings.distance }}"
  >
    <div class="gallery-container">
      <div class="mdw-image-gallery-bg">
        {%- for block in section.blocks -%}
          {%- if block.settings.image -%}
            {{ block.settings.image | image_url: width: 1920 | image_tag: loading: 'lazy', class: 'bg-image' }}
          {%- endif -%}
        {%- endfor -%}
      </div>
      <div class="gsap-gallery-image">
        {%- for block in section.blocks -%}
          {%- if block.settings.image -%}
            <div class="e-con">
              {{
                block.settings.image
                | image_url: width: 460
                | image_tag: loading: 'lazy', width: 460, height: 460, class: 'gallery-image'
              }}
            </div>
          {%- endif -%}
        {%- endfor -%}
      </div>
    </div>
  </div>
</div>
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
<script src="https://unpkg.com/lenis@1.1.11/dist/lenis.min.js"></script>
<script>
if (!window.MDWNonce113) {
  window.MDWNonce113 = true;
  
  (function($) {
    class GalleryController {
      constructor() {
        this.galleries = [];
        this.init();
      }
      
      init() {
        $('.gsap-gallery').each((_, element) => {
          this.galleries.push(new Gallery($(element)));
        });
        
        if (this.shouldEnableSmoothScroll()) {
          this.initSmoothScroll();
        }
        
        this.bindEvents();
      }
      
      shouldEnableSmoothScroll() {
        return getComputedStyle(document.body).getPropertyValue('--smooth-scroll') === 'true'
          && !this.isMobile()
          && !this.isSafari();
      }
      
      isMobile() {
        return $(window).width() < 768;
      }
      
      isSafari() {
        return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
      }
      
      initSmoothScroll() {
        const lenis = new Lenis({
          duration: 1.2,
          easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
          orientation: 'vertical',
          smoothWheel: true
        });
        
        const raf = (time) => {
          lenis.raf(time);
          requestAnimationFrame(raf);
        };
        
        requestAnimationFrame(raf);
      }
      
      bindEvents() {
        $(window).on('scroll resize', () => {
          this.galleries.forEach(gallery => gallery.update());
        });
      }
    }
    
    class Gallery {
      constructor($element) {
        this.$element = $element;
        this.$container = $element.find('.gallery-container');
        this.$images = $element.find('.e-con');
        this.$backgrounds = $element.find('.mdw-image-gallery-bg img');
        
        this.options = {
          scrollPerItem: parseInt($element.data('scroll-per-item')) || 500,
          distance: parseInt($element.data('distance')) || 1500
        };
        
        this.totalHeight = this.$images.length * this.options.scrollPerItem;
        this.$element.height(this.totalHeight + window.innerHeight);
        
        this.initializePositions();
      }
      
      initializePositions() {
        this.$images.each((index, element) => {
          const $image = $(element);
          const initialZ = -index * this.options.distance;
          
          $image.css({
            transform: `translate(-50%, -50%) translateZ(${initialZ}px)`,
            zIndex: this.$images.length - index,
            opacity: index === 0 ? 1 : 0
          });
          // Set initial background
          if(index === 0) {
            this.$backgrounds.eq(0).css('opacity', 1);
          }
        });
      }
      
      update() {
        if(this.isMobile()) return;
        
        const rect = this.$element[0].getBoundingClientRect();
        const scrollProgress = (window.pageYOffset - this.$element.offset().top) / (this.$element.height() - window.innerHeight);
        
        this.updateFixedState(rect);
        
        if (scrollProgress >= 0 && scrollProgress <= 1) {
          this.updateImagePositions(scrollProgress);
          this.updateBackgrounds(scrollProgress);
        }
      }
      
      isMobile() {
        return $(window).width() < 768;
      }
      
      updateFixedState(rect) {
        const shouldBeFixed = rect.top <= 0 && rect.bottom > window.innerHeight;
        const shouldBeBottom = rect.bottom <= window.innerHeight;
        
        this.$container.toggleClass('is-fixed', shouldBeFixed && !shouldBeBottom);
        this.$container.toggleClass('is-bottom', shouldBeBottom);
      }
      
      updateImagePositions(progress) {
        const totalMovement = this.options.distance * (this.$images.length - 1);
        const currentMove = progress * totalMovement;
        
        this.$images.each((index, element) => {
          const $image = $(element);
          const initialZ = -index * this.options.distance;
          const currentZ = initialZ + currentMove;
          const opacity = this.calculateOpacity(currentZ);
          
          $image.css({
            transform: `translate(-50%, -50%) translateZ(${currentZ}px)`,
            opacity: opacity
          });
        });
      }
      
      updateBackgrounds(progress) {
        const totalImages = this.$images.length;
        const activeIndex = Math.floor(progress * (totalImages - 1));
        
        this.$backgrounds.each((index, element) => {
          const $bg = $(element);
          const opacity = index === activeIndex ? 1 : 0;
          
          if($bg.css('opacity') !== opacity) {
            $bg.css('opacity', opacity);
          }
        });
      }
      
      calculateOpacity(z) {
        const visibleRange = 1000;
        const opacity = 1 - Math.abs(z) / visibleRange;
        return Math.max(0, Math.min(1, opacity));
      }
    }
    
    // Initialize galleries when document is ready
    $(document).ready(() => {
      new GalleryController();
    });
    
  })(jQuery);
}
</script>
4.8/5 - (5 votes)

About

Leave a Comment

Your email address will not be published. Required fields are marked *