JSFiddle - React, Tailwind, and code Playground

by Mark Benedict Santiago

HTML

<div class="img-viewer">
    <div class="img-viewer-image"></div>
    <div class="img-viewer-controls">
        <span class="img-viewer-zoom in">+</span>
        <span class="img-viewer-zoom out">-</span>
    </div>
</div>

CSS

.img-viewer{
    display: block;
    border: thin solid black;
    overflow: hidden
}
.img-viewer-image{
    margin: 5px auto;
    background-image: url("http://lorempixel.com/800/400/nature");
    background-repeat: no-repeat;
    background-position: 0px 0px;
    width: 400px;
    height: 200px;
    background-size: 400px 200px;
}
.img-viewer-controls{
    display: block;
    border-top: thin solid black;
    text-align: right;
}
.img-viewer-zoom{
    display: inline-block;
    width: 15px;
    height: 15px;
    text-align: center;
    border: thin solid black;
    background-color: white;
    bottom: 10px;
}
.img-viewer-zoom.out{
    right: 10px;
}
.img-viewer-zoom.in{
    right: 30px;
}

JavaScript

var exploreImage = false;
var lastPosition = { x: undefined, y: undefined };
$(document).on('mousedown', '.img-viewer .img-viewer-image', function(){
    exploreImage = true;
});
$(document).on('mousemove', '.img-viewer .img-viewer-image', function(event){
    if(exploreImage){
        if(lastPosition.x){
            var horizontalMovement = event.pageX - lastPosition.x;
            var verticalMovement = event.pageY - lastPosition.y;
            var target = $(this);
            var viewer = {
                width: target.width(),
                height: target.height(),
                bgWidth: parseInt(target.css('background-size').split(' ')[0]),
                bgHeight: parseInt(target.css('background-size').split(' ')[1])
            }
            var newPosition = {
                x: parseInt(target.css('background-position-x')) + horizontalMovement,
                y: parseInt(target.css('background-position-y')) + verticalMovement
            };
            if(newPosition.x < 0 && newPosition.x > viewer.width - viewer.bgWidth){
                target.css("background-position-x", newPosition.x + "px");
            }
            if(newPosition.y < 0 && newPosition.y > viewer.height - viewer.bgHeight){
                target.css("background-position-y", newPosition.y + "px");
            }
        }
        lastPosition = { x: event.pageX, y: event.pageY };
    }
});
$(document).on('mouseup mouseout', function(event){
    if(exploreImage){
        exploreImage = false;
        lastPosition = { x: undefined, y: undefined };
    }
});
// SCROLL
$(document).on('click', '.img-viewer .img-viewer-controls > .img-viewer-zoom', function(){
    zoom($(this).closest('.img-viewer').find('.img-viewer-image'), $(this).hasClass("out") ? "out" : undefined);
});
function zoom(viewer, e){
    var viewerSize = {
        width: parseInt(viewer.css('background-size').split(' ')[0]),
        height: parseInt(viewer.css('background-size').split(' ')[1])
    };
    var offsetx...