JSFiddle - React, Tailwind, and code Playground

HTML

<script src="//cdn.jsdelivr.net/prefixfree/1.0.7/prefixfree.dynamic-dom.js"></script>
<figure data-desaturated></figure>
<figure data-original></figure>

CSS

figure{ width: 550px; height: 360px;
        position: absolute; top: 0; left: 0;
        margin: 0; padding: 0; 
        background-position: 50% 50%;
        background-repeat: no-repeat;
        background-image: url('https://static.pexels.com/photos/106399/pexels-photo-106399.jpeg');

}

figure[data-desaturated]{
    -webkit-filter: grayscale(0.9);
}

figure[data-original]{
    width: 360px;
    left: -180px;
    top: -180px;
    border-radius: 180px;
    background-position: 180px 180px;
    opacity: 0;
    transition: opacity 0.4s;
}

figure[data-desaturated]:hover + figure[data-original],
figure[data-original]:hover{
    opacity: 1;
}

JavaScript

var data_original = $('figure[data-original]');
    
$('figure[data-desaturated],figure[data-original]').mousemove(function(event){
    var translate_x = event.pageX,
        translate_y = event.pageY,
        position_x = 180 - translate_x,
        position_y = 180 - translate_y;
    
    data_original.css({
        '-ms-transform': 'translate('+translate_x+'px,'+translate_y+'px)',
        '-webkit-transform': 'translate('+translate_x+'px,'+translate_y+'px)',
        '-moz-transform': 'translate('+translate_x+'px,'+translate_y+'px)',
        'transform': 'translate('+translate_x+'px,'+translate_y+'px)',
        'background-position': ''+position_x+'px '+position_y+'px'
    })
});