Banner

Before and After Image Comparison

Responsive before/after image comparison slider.

Display estimated delivery date
PHP
/**
 * Snippet Name:     Before and After Image Comparison
 * Snippet Author:   coding-bunny.com
 * Description:      Responsive before/after image comparison slider.
 * Version:          1.0.0
 * 
 * USAGE EXAMPLES:
 * [image_comparison_slider before="URL1" after="URL2" title_before="BEFORE" title_after="AFTER" show_titles="1"]
 */

add_action('wp_enqueue_scripts', function() {
    $css = <<<CSS
.ics-image-comparison-slider {
    position: relative;
    display: inline-block;
    max-width: 100%;
    user-select: none;
}
.ics-slider-wrapper {
    position: relative;
    width: 100%;
}
.ics-before-img,
.ics-after-img {
    display: block;
    max-width: 100%;
    width: 100%;
    height: auto;
    user-select: none;
    pointer-events: none;
    position: absolute;
    top: 0; left: 0;
}

.ics-slider-handle-line {
    position: absolute;
    top: 0; bottom: 0;
    left: 50%;
    width: 2px;
    background: #fff;
    transform: translateX(-50%);
    z-index: 40;
    pointer-events: none;
}
.ics-slider-handle {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 20px;
    height: 20px;
    background: #3857e9;
    border-radius: 50%;
    transform: translate(-50%, -50%);
    z-index: 50;
    cursor: ew-resize;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 0 0 2px #fff, 0 0 4px #3857e9;
    transition: background 0.15s;
    outline: none;
    border: none;
}
.ics-slider-handle:after { display: none; }
.ics-slider-range {
    position: absolute;
    top: 0; left: 0;
    width: 100%; height: 100%;
    opacity: 0;
    z-index: 100;
    cursor: ew-resize;
}

.ics-img-titles-row {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    justify-content: space-between;
    align-items: flex-end;
    width: 100%;
    z-index: 60;
    pointer-events: none;
    box-sizing: border-box;
    padding: 0 8px 8px 8px;
}
.ics-img-title {
    color: #fff;
    background: rgba(0,0,0,0.70);
    padding: 3px 10px;
    font-size: 12px;
    border-radius: 0;
    max-width: 48%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-shadow: 0 1px 4px #000;
    box-sizing: border-box;
    transition: opacity 0.18s;
}
.ics-img-title.ics-title-before {
    text-align: left;
    justify-self: flex-start;
}
.ics-img-title.ics-title-after {
    text-align: right;
    justify-self: flex-end;
}
CSS;
    wp_register_style('ics-inline-style', false);
    wp_enqueue_style('ics-inline-style');
    wp_add_inline_style('ics-inline-style', $css);

    $js = <<<JS
jQuery(document).ready(function($){
    function setupSlider(\$container){
        var \$wrapper = \$container.find('.ics-slider-wrapper'),
            \$beforeImg = \$wrapper.find('.ics-before-img'),
            \$afterImg = \$wrapper.find('.ics-after-img'),
            \$slider = \$wrapper.find('.ics-slider-range'),
            \$handle = \$wrapper.find('.ics-slider-handle'),
            \$line = \$wrapper.find('.ics-slider-handle-line'),
            \$titleBefore = \$wrapper.find('.ics-img-title.ics-title-before'),
            \$titleAfter = \$wrapper.find('.ics-img-title.ics-title-after');

        function setWrapperHeight(){
            var h = \$beforeImg.height();
            \$wrapper.height(h);
            \$afterImg.height(h);
        }

        function setSlider(val){
            var percent = val + '%';
            \$afterImg.css('clip-path', 'inset(0 0 0 ' + percent + ')');
            \$line.css('left', percent);
            \$handle.css('left', percent);

            var value = parseFloat(val);

            if(\$titleBefore.length){
                var beforeOpacity = (100 - value)/100;
                \$titleBefore.css('opacity', beforeOpacity < 0.03 ? 0 : beforeOpacity);
            }

            if(\$titleAfter.length){
                var afterOpacity = value/100;
                \$titleAfter.css('opacity', afterOpacity < 0.03 ? 0 : afterOpacity);
            }
        }

        var imagesLoaded = 0;
        function tryInit(){
            imagesLoaded++;
            if(imagesLoaded>=2) {
                setWrapperHeight();
                setSlider(\$slider.val());
            }
        }
        \$beforeImg.on('load', tryInit);
        \$afterImg.on('load', tryInit);
        if(\$beforeImg[0].complete) tryInit();
        if(\$afterImg[0].complete) tryInit();

        $(window).on('resize', setWrapperHeight);

        \$slider.on('input change', function(){ setSlider(this.value); });

        var dragging = false;
        \$handle.on('mousedown touchstart', function(e){
            dragging = true; e.preventDefault();
        });
        $(document).on('mouseup touchend', function(){ dragging = false; });
        $(document).on('mousemove touchmove', function(e){
            if(!dragging) return;
            var pageX = e.pageX || (e.originalEvent.touches && e.originalEvent.touches[0].pageX);
            var offset = \$wrapper.offset().left;
            var width = \$wrapper.width();
            var percent = ((pageX - offset) / width) * 100;
            percent = Math.max(0, Math.min(100, percent));
            \$slider.val(percent);
            setSlider(percent);
        });

        \$handle.on('keydown', function(e){
            var val = parseInt(\$slider.val(), 10);
            if(e.key === 'ArrowLeft' || e.key === 'ArrowDown') {
                \$slider.val(Math.max(val - 1, 0)).trigger('input');
            } else if(e.key === 'ArrowRight' || e.key === 'ArrowUp') {
                \$slider.val(Math.min(val + 1, 100)).trigger('input');
            }
        });
    }
    $('.ics-image-comparison-slider').each(function(){ setupSlider($(this)); });
});
JS;
    wp_register_script('ics-inline-script', false);
    wp_enqueue_script('ics-inline-script');
    wp_add_inline_script('ics-inline-script', $js);
});

add_shortcode('image_comparison_slider', function($atts) {
    $atts = shortcode_atts([
        'before' => '',
        'after'  => '',
        'title_before' => '',
        'title_after'  => '',
        'width' => '100%',
        'show_titles' => '0',
    ], $atts, 'image_comparison_slider');
    $before = esc_url($atts['before']);
    $after  = esc_url($atts['after']);
    $title_before = esc_html($atts['title_before']);
    $title_after  = esc_html($atts['title_after']);
    $width  = esc_attr($atts['width']);
    $show_titles = $atts['show_titles'] === '1' || strtolower($atts['show_titles']) === 'true';

    if (empty($before) || empty($after)) {
        return '<p><em>Both "before" and "after" URLs are required.</em></p>';
    }
    ob_start(); ?>
    <div class="ics-image-comparison-slider" style="width:<?php echo $width; ?>;">
        <div class="ics-slider-wrapper">
            <img src="<?php echo $before; ?>" alt="" class="ics-before-img" draggable="false" />
            <img src="<?php echo $after; ?>" alt="" class="ics-after-img" draggable="false" />
            <?php if($show_titles && ($title_before || $title_after)): ?>
                <div class="ics-img-titles-row">
                    <span class="ics-img-title ics-title-before"><?php echo $title_before; ?></span>
                    <span class="ics-img-title ics-title-after"><?php echo $title_after; ?></span>
                </div>
            <?php endif; ?>
            <div class="ics-slider-handle-line"></div>
            <input type="range" min="0" max="100" value="50" class="ics-slider-range" aria-label="Image comparison slider" />
            <span class="ics-slider-handle" tabindex="0" aria-label="Drag to compare images"></span>
        </div>
    </div>
    <?php
    return ob_get_clean();
});

How To Implement This Solution?

Leave a Reply

My Agile Privacy
This site uses technical and profiling cookies. You can accept, decline or customize cookies by pressing the desired buttons. By closing this policy you will continue without accepting.

Need help?

Choose one of the following options:

Powered by CodingBunny