Learn how to add a 3D Image Gallery in WordPress using Elementor! 🚀 In this step-by-step tutorial, I’ll show you how to create an interactive and eye-catching 3D gallery to enhance your website’s visual appeal.
First Step: Create a Container
Create a container with wsp-gallery
class make it full width(100%) and full height(100vh)
Second Step: Create a Container
Create another container with in first container with wsp-gallery-image
class make it full width(100%) and full height(100vh). Set the over-flow to “hidden”
Third Step: Container for Image and Heading
Create another container with in second container with 400px with and add heading and image(450px height). Set position to “Absolute” and offset to “50%”
Now duplicate this container to add more gallery images, change text and heading as per your requirements.
Fourth Step: Add Custom Code
Add the following custom code in Second Container we created in step 2
<style>
body{
--smooth-scroll: true;
}
[class^='wsp-gallery'],
[class*='wsp-gallery']{
--disabled-section-padding-top: 70px;
--disabled-section-padding-bottom: 50px;
}
[class^='wsp-gallery'].disabled,
[class*='wsp-gallery'].disabled{
height: auto !important;
padding-top: var(--disabled-section-padding-top, 70px);
padding-bottom: var(--disabled-section-padding-bottom, 50px);
overflow: hidden;
position: relative;
}
html:not(.elementor-html) [class^='wsp-gallery'] .elementor-widget-html,
html:not(.elementor-html) [class*='wsp-gallery'] .elementor-widget-html{
display: none;
}
[class^='wsp-gallery'] .wsp-gallery-image,
[class*='wsp-gallery'] .wsp-gallery-image{
transform-style: preserve-3d;
perspective: 750px;
backface-visibility: hidden;
}
[class^='wsp-gallery'] .wsp-image-gallery-bg,
[class*='wsp-gallery'] .wsp-image-gallery-bg{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100vh;
overflow: hidden;
opacity: 0.6;
}
[class^='wsp-gallery'].disabled .wsp-image-gallery-bg,
[class*='wsp-gallery'].disabled .wsp-image-gallery-bg{
position: absolute !important;
background-size: cover;
background-position: center;
filter: blur(50px);
transform: scale(1.15);
height: 100%;
}
[class^='wsp-gallery'] .sticky,
[class*='wsp-gallery'] .sticky {
position: fixed;
top: 0;
}
[class^='wsp-gallery'] .sticky-end,
[class*='wsp-gallery'] .sticky-end{
position: absolute;
bottom: 0;
top: auto !important;
}
[class^='wsp-gallery'] .wsp-image-gallery-bg img,
[class*='wsp-gallery'] .wsp-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;
}
[class^='wsp-gallery'].disabled .wsp-gallery-image,
[class*='wsp-gallery'].disabled .wsp-gallery-image{
flex-direction: row;
flex-wrap: wrap;
position: static !important;
justify-content: center;
gap: 12px;
}
[class^='wsp-gallery'] .wsp-gallery-image > .e-con,
[class*='wsp-gallery'] .wsp-gallery-image > .e-con,
[class^='wsp-gallery'] .wsp-gallery-image > .e-container,
[class*='wsp-gallery'] .wsp-gallery-image > .e-container{
opacity: 0;
transition: 0s;
left: 70% !important;
}
[class^='wsp-gallery'].disabled .wsp-gallery-image > .e-con,
[class*='wsp-gallery'].disabled .wsp-gallery-image > .e-con,
[class^='wsp-gallery'].disabled .wsp-gallery-image > .e-container,
[class*='wsp-gallery'].disabled .wsp-gallery-image > .e-container{
position: static;
opacity: 1 !important;
transform: none !important;
}
html.elementor-html [class^='wsp-gallery'] .wsp-gallery-image > .e-con,
html.elementor-html [class*='wsp-gallery'] .wsp-gallery-image > .e-con,
html.elementor-html [class^='wsp-gallery'] .wsp-gallery-image > .e-container,
html.elementor-html [class*='wsp-gallery'] .wsp-gallery-image > .e-container{
opacity: 1;
}
[class^='wsp-gallery'] .wsp-gallery-image > .e-con:nth-child(even),
[class*='wsp-gallery'] .wsp-gallery-image > .e-con:nth-child(even),
[class^='wsp-gallery'] .wsp-gallery-image > .e-container:nth-child(even),
[class*='wsp-gallery'] .wsp-gallery-image > .e-container:nth-child(even){
left: 30% !important;
}
[class^='wsp-gallery'].disabled .wsp-gallery-image .elementor-widget-image,
[class*='wsp-gallery'].disabled .wsp-gallery-image .elementor-widget-image{
order: -1;
margin-bottom: 15px;
}
@media (max-width: 1024px){
[class^='wsp-gallery'] .wsp-gallery-image > .e-con,
[class*='wsp-gallery'] .wsp-gallery-image > .e-con,
[class^='wsp-gallery'] .wsp-gallery-image > .e-container,
[class*='wsp-gallery'] .wsp-gallery-image > .e-container{
left: 80% !important;
}
[class^='wsp-gallery'] .wsp-gallery-image > .e-con:nth-child(even),
[class*='wsp-gallery'] .wsp-gallery-image > .e-con:nth-child(even),
[class^='wsp-gallery'] .wsp-gallery-image > .e-container:nth-child(even),
[class*='wsp-gallery'] .wsp-gallery-image > .e-container:nth-child(even){
left: 20% !important;
}
}
</style>
<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>
if(!MDWNonce113){
var MDWNonce113 = true
;(function($){
var selector = '[class^="wsp-gallery"], [class*="wsp-gallery"]',
slides = [],
options = [],
galleryBgImages = [],
visibleRange = [],
winHeight,
firstTime = true,
initialZ = [],
frontRange = []
function mapRange(value, inMin, inMax, outMin, outMax){
return ((value - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin;
}
function isDisabled(){
var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent),
isMobile = $(window).width() < 768,
d = isSafari || isMobile
d ? $(selector).addClass('disabled') : $(selector).removeClass('disabled')
return d
}
function getValues(){
$(selector).each(function(i){
var className = $(this).attr('class'),
classNameIndex = className.indexOf('wsp-gallery'),
shortClass = className.substring(classNameIndex, className.indexOf(' ',classNameIndex)),
values = shortClass.split('-')
options[i] = {
scrollPerItem: 500,
distance: 1500,
background: true
}
values.forEach(function(value, index){
if(value =='scrollperitem' && values[index+1] && !isNaN(values[index+1])){ options[i].scrollPerItem = parseFloat(values[index+1])
}
if(value =='distance' && values[index+1] && !isNaN(values[index+1])){ options[i].distance = parseFloat(values[index+1])
}
if(value =='nobackground'){ options[i].background = false }
})
})
}
function setHeight(){
if(isDisabled()) return
winHeight = $(window).height()
$(selector).each(function(i){
$(this).css('height', winHeight + (slides[i].length * options[i].scrollPerItem) + 'px')
})
}
function initImages(){
if(isDisabled()) return
$(selector).each(function(i){
initialZ[i] = []
frontRange[i] = []
visibleRange[i] = 5000
slides[i].each(function(index){
initialZ[i][index] = -index*options[i].distance
frontRange[i][index] = 1000
$(this).css('transform', `translate3d(-50%, -50%, ${initialZ[i][index]}px)`)
$(this).css('opacity', mapRange(initialZ[i][index], -visibleRange[i], 0, 0, 1))
})
})
}
function init(){
getValues()
$(selector).each(function(i){
var $this = $(this)
slides[i] = $this.find('.wsp-gallery-image > .e-con, .wsp-gallery-image > .e-con-inner > .e-con, .wsp-gallery-image > .e-container')
slides[i].each(function(si){ $(this).css('z-index', slides[i].length - si) })
if(options[i].background){
var galleryBg = $('<div>').addClass('wsp-image-gallery-bg')
if(isDisabled()){
galleryBg.css('background-image', `url('${slides[i].find('img').eq(0).attr('src')}')`)
}else{
slides[i].each(function(){
var clonedImg = $(this).find('img').clone()
galleryBg.append(clonedImg)
})
}
$this.prepend(galleryBg)
galleryBgImages[i] = galleryBg.find('img')
galleryBgImages[i].each(function (gi) {
$(this).css('z-index', galleryBgImages[i].length - gi)
})
}
})
setHeight()
initImages()
}
function setSticky(){
if(isDisabled()) return
$(selector).each(function(i){
var $this = $(this),
rect = $this[0].getBoundingClientRect(),
inner = $this.children()
rect.top <= 0 ? inner.addClass('sticky') : inner.removeClass('sticky')
rect.bottom < winHeight ? inner.addClass('sticky-end') : inner.removeClass('sticky-end')
})
}
function moveImages(){
$(selector).each(function(i){
var progress = ($(window).scrollTop() - $(this).offset().top)/($(this).height()-$(window).height())
if(progress >= 0 && progress <= 1){
zIncrement = progress*options[i].distance*(slides[i].length - 1)
slides[i].each(function(index){
var currentZ = initialZ[i][index] + zIncrement,
opacity = mapRange(currentZ, -visibleRange[i], 0, 0, 1),
opacityBg
if(opacity >= 0){
if(opacity <= 1 || firstTime) $(this).css('opacity', opacity)
if(currentZ < frontRange[i][index] || firstTime) $(this).css('transform', `translate3d(-50%, -50%, ${currentZ}px)`)
}
if(options[i].background){
opacityBg = currentZ < 100 ? 1 : 0
gsap.to($(galleryBgImages[i][index]), {
opacity: opacityBg,
ease: 'power3.out',
duration: 1.5,
})
}
})
firstTime = false
}
})
}
$(document).ready(init)
$(window).on('load resize', setHeight)
$(window).on('load resize scroll', function(){
setSticky()
moveImages()
})
})(jQuery)
}
</script>
<!-- Smooth Scroll -->
<style>
html.lenis, html.lenis body {
height: auto;
}
.lenis.lenis-smooth {
scroll-behavior: auto !important;
}
.lenis.lenis-smooth [data-lenis-prevent] {
overscroll-behavior: contain;
}
.lenis.lenis-stopped {
overflow: hidden;
}
.lenis.lenis-smooth iframe {
pointer-events: none;
}
</style>
<script src="https://unpkg.com/lenis@1.1.11/dist/lenis.min.js"></script>
<script>
$(document).ready(function(){
var smoothScroll = getComputedStyle(document.body).getPropertyValue('--smooth-scroll'),
smoothScroll = smoothScroll && smoothScroll == 'true'
if(smoothScroll){
var lenis = new Lenis()
function raf(time) {
lenis.raf(time)
requestAnimationFrame(raf)
}
requestAnimationFrame(raf)
}
})
</script>