Edit in JSFiddle

/**
 * @classdesc
 * Make map cursor act as a magnify lense
 *
 * @constructor
 * @extends {ol.control.Control}
 * @param {olx.control.Otpions=} opt_options MagnifierControl options.
 */
ol.control.MagnifierControl = function(opt_options) {
/**
 * get and set config options
 * @scaleOffSet {integer} (optional) default is 2. 
 * @radius {integer} (optional) default is 100.
 * @lineWidth {integer} (optional) default is 5.
 * @strokeStyle {stirng) (optional) red+green+blue+alpha chanel
 * @layers {ol.layer[]} (optional) an Array of layers. Default is parent's map exisitng layers
 */
var options = opt_options || {};
//console.log("this.getMap()",this.getMap())
options.scaleOffSet  = typeof(options.scaleOffSet) !== 'undefined' ? options.scaleOffSet  : 2;
options.radius       = typeof(options.radius)      !== 'undefined' ? options.radius       : 100;
options.lineWidth    = typeof(options.lineWidth)   !== 'undefined' ? options.lineWidth    : 5;
options.strokeStyle  = typeof(options.strokeStyle) !== 'undefined' ? options.strokeStyle  : 'rgba(255,0,0,1)';
options.layers       = typeof(options.layers)      !== 'undefined' ? options.layers       : [];
options.synchMaps    = typeof(options.synchMaps)   !== 'undefined' ? options.synchMaps    : false;

//attach options to the control
this.options = options;
//vars with global scope
this.initialised = false;
this.magmap;//ol.map
this.mousePosition = null;
this.targetMapDivId; 

//control toggle button
var button = document.createElement('button');
button.innerHTML = 'M';
var this_ = this;
/**
 * toggling the magnifing lense
 * intialise if first time
 * else show/hide the lense 
 */
this.toggleMagCntrl = function() {
   if (this_.isVisble===true ){//need to hide lense and detach all listners
   document.getElementById('magmap').style.display = 'none';
   //remove all the listeners 
   ol.Observable.unByKey(this_.precomposeListener);
   ol.Observable.unByKey(this_.postcomposeListener);
   ol.Observable.unByKey(this_.mousemoveListener);
   //this_.getMap().unByKey(this_.moveendListener);
   document.getElementById(this_.targetMapDivId).removeEventListener("mouseout", this_.mouseOutFn);
   window.removeEventListener('resize', this_.resizeFn);
   this_.isVisble = false;
   }
   else {//initialise if not visible and show. Attach  the listeners
     if (this_.initialised === false){ 
       this_.initMagCntrl();
     }
   //asign all the listeners
    this_.precomposeListener = this_.magmap.on('precompose', 
    	function(event) {
    this_.precomposeFn(event);
    });
    this_.postcomposeListener = this_.magmap.on('postcompose', 
    	function(event) {
    this_.postcomposeFn(event);
    });
    this_.mousemoveListener = this_.getMap().on('pointermove', 
    	function(event) {
    this_.mouseMoveFn(event);
    });
    document.getElementById(this_.targetMapDivId).addEventListener("mouseout", this_.mouseOutFn);
    window.addEventListener ("resize", this_.resizeFn, false);
    document.getElementById('magmap').style.display = 'block';
    this_.resizeFn();
    this_.isVisble = true;
   }
  };
  
button.addEventListener('click', this_.toggleMagCntrl, false);
button.addEventListener('touchstart', this_.toggleMagCntrl, false);
var element = document.createElement('div');
  element.className    = 'ol-unselectable ol-control';
  element.style.zIndex = '9999';//set one level above the magnifier so been visible 
  element.style.top    = '65px';
  element.style.left   = '.5em';
  element.appendChild(button);
  
  ol.control.Control.call(this, {
    element : element,
    target  : options.target
  });    
};
ol.inherits(ol.control.MagnifierControl, ol.control.Control);
/**
* initialising the magnifying lense
*
* @returns {undefined} 
*/
ol.control.MagnifierControl.prototype.initMagCntrl = function(){
  if (this.initialised===false){
  this.targetMapDivId = this.getMap().getTarget();
  var magelement = document.createElement('div');
  magelement.id                      = 'magmap';
  magelement.style.zIndex            = "9998";    //set one level below the magnify button so button is visible 
  magelement.style.display           = 'inline-block';
  magelement.style.position          = 'absolute';
  magelement.style['pointer-events'] = 'none';   //this allowing interaction above magmap element. Love it!!!!! 
  document.getElementById(this.targetMapDivId).appendChild(magelement);
  //disable the deafult controls
  var magnControls = ol.control.defaults({
      rotate      : true, //keep it true
      zoom        : false,
      attribution : false
  });

  if (this.options.layers.length===0){
    if (this.options.synchMaps === false){
    this.options.layers = this.getMap().getLayers().getArray(); //get as array so remove any synch
    } else { 
    //in this case (default) magnmap layers should stay in sunc with map layers. 
    //great attitude of ol.Collection. in it?
    //if not means have been passed as an array. So no sync.
    this.options.layers = this.getMap().getLayers();//returns ol.Collection     
    }  
  } else {
  this.options.synchMaps = false; //set it false as not suppling layers means no synch may exist
  }
  //create the map for the magnify
  this.magmap = new ol.Map({
  layers        : this.options.layers,
  target        : 'magmap',
  controls      : magnControls,
  interactions  :ol.interaction.defaults({
      shiftDragZoom       : false,
      dragPan             : false,
      altShiftDragRotate  : false,
      doubleClickZoom     : false,
      mouseWheelZoom	    : false
  }),
  view:new ol.View({
      center   : this.getMap().getView().getCenter(),
      zoom     : this.getMap().getView().getZoom(),
      rotation : this.getMap().getView().getRotation()
    })
});
this.magmap.setSize(this.getMap().getSize());
this.initListeners();
this.resizeFn();
this.initialised = true;
}
}; 
/**
 * setting the listeners related
 */
ol.control.MagnifierControl.prototype.initListeners = function(){
var this_ = this;
this.resizeFn = function(event){
  var magMapEl = document.getElementById('magmap');
  var oriMapEl = document.getElementById(this_.targetMapDivId);
  magMapEl.style.width  = oriMapEl.getBoundingClientRect().width + 'px';
  magMapEl.style.height = oriMapEl.getBoundingClientRect().height + 'px';
  magMapEl.style.left   = oriMapEl.getBoundingClientRect().left + 'px';
  magMapEl.style.top    = oriMapEl.getBoundingClientRect().top + 'px';
  this_.getMap().updateSize();
  this_.magmap.updateSize();
};

/**
* the precompose liistener function
* create a circle around the mouse
* using the canvas
*/
this.precomposeFn = function(event){
  var ctx = event.context;
  var pixelRatio = event.frameState.pixelRatio;
  ctx.save();
  ctx.beginPath();
  if (this_.mousePosition) {
    // only show a circle around the mouse
    ctx.arc(
        this_.mousePosition[0] * pixelRatio, this_.mousePosition[1] * pixelRatio,
        this_.options.radius * pixelRatio, 0, 2 * Math.PI
    );
    ctx.lineWidth = this_.options.lineWidth * pixelRatio;
    ctx.strokeStyle = this_.options.strokeStyle;
    ctx.stroke();
  }
  ctx.clip();
};

/**
*
*/
this.postcomposeFn = function(event){
  var ctx = event.context;
  ctx.restore();
};  


/**
*
*/
this.mouseMoveFn = function(event){
var coords = event.coordinate;
this_.mousePosition = event.pixel;
var magcenter = this_.magmap.getView().getCenter();
var mapcenter = this_.getMap().getView().getCenter();
var xdif= magcenter[0] -  mapcenter[0];
var ydif= magcenter[1] -  mapcenter[1];
//seems to need some correction
var coords1 = [coords[0]-(xdif/(this_.options.scaleOffSet+1)),coords[1]-(ydif/(this_.options.scaleOffSet+1))]; 
console.log("this_.options.scaleOffSet+1-----",this_.options.scaleOffSet+1);
if (this_.options.scaleOffSet===0){
this_.magmap.getView().setCenter(this_.getMap().getView().getCenter());
} else {
this_.magmap.getView().setCenter(coords1);
}
this_.magmap.getView().setZoom(
  this_.getMap().getView().getZoom()+this_.options.scaleOffSet
);
this_.magmap.getView().setRotation(
  this_.getMap().getView().getRotation()
);
this_.magmap.render();
};


this.mouseOutFn = function(){
  this_.mousePosition = null;
  this_.magmap.render();
};
}

/**
 * destroy the control
 * 
 */
ol.control.MagnifierControl.prototype.destroy = function(){
if (this.initialised===true){
//destory all the listeners
ol.Observable.unByKey(this.precomposeListener);
ol.Observable.unByKey(this.postcomposeListener);
ol.Observable.unByKey(this.mousemoveListener);
document.getElementById(this.targetMapDivId).removeEventListener("mouseout", this.mouseOutFn);
window.removeEventListener('resize', this.resizeFn);
//remove the control
if (this.getMap()){
this.getMap().removeControl(this);
}
//remove the element holding the magn map
var element = document.getElementById(this.magmap.getTarget());
element.parentNode.removeChild(element);
//set init vars to false and magnmap to null

} else {
if (this.getMap()){
this.getMap().removeControl(this);
}
}
this.isVisble=false;
this.initialised=false;
};   

/**
 * very dirty way to reset the layers
 * need to recreate the map itself. No matter the goodness of ol.collection
 * sometimes is a pain in the a**
 */
ol.control.MagnifierControl.prototype.setLayers = function(lyrs) {
if (this.initialised===true){
  var existingLyrs = new ol.Collection(this.magmap.getLayers().getArray()).getArray();
  this.options.synchMaps = false;
  this.magmap.setTarget(null);
  this.magmap = null; 
  ol.Observable.unByKey(this.mousemoveListener);
  var magnControls = ol.control.defaults({
      rotate      : true, //keep it true
      zoom        : false,
      attribution : false
  });
  this.magmap = new ol.Map({
  layers        : lyrs,
  target        : 'magmap',
  controls      : magnControls,
  interactions  :ol.interaction.defaults({
      shiftDragZoom       : false,
      dragPan             : false,
      altShiftDragRotate  : false,
      doubleClickZoom     : false,
      mouseWheelZoom	    : false
  }),
  view:new ol.View({
      center   : this.getMap().getView().getCenter(),
      zoom     : this.getMap().getView().getZoom(),
      rotation : this.getMap().getView().getRotation()
    })
});
this.magmap.setSize(this.getMap().getSize());
var this_ = this;
console.log("this",this)
    if (this_.isVisble===true ){
    this.precomposeListener = this.magmap.on('precompose', 
    	function(event) {
    this_.precomposeFn(event);
    });
    this.postcomposeListener = this.magmap.on('postcompose', 
    	function(event) {
    this_.postcomposeFn(event);
    });
    this.mousemoveListener = this.getMap().on('pointermove', 
    	function(event) {
    this_.mouseMoveFn(event);
    });
    document.getElementById(this.targetMapDivId).addEventListener("mouseout", this_.mouseOutFn);
    window.addEventListener ("resize", this_.resizeFn, false);
    document.getElementById('magmap').style.display = 'block';
    
    } else {
   //remove all the listeners 
   ol.Observable.unByKey(this_.precomposeListener);
   ol.Observable.unByKey(this_.postcomposeListener);
   ol.Observable.unByKey(this_.mousemoveListener);
   document.getElementById(this_.targetMapDivId).removeEventListener("mouseout", this_.mouseOutFn);
   window.removeEventListener('resize', this_.resizeFn);
   this_.isVisble = false;
    }   
   this.resizeFn();
   this.initialised = true; 
   this.options.synchMaps = false;     
} else {
//this.options.layers = lyrs;
console.log("action when not initialised")
}
};  

var key = 'AuxBBMjAlE3BNG6BEaiNVni3ic3XH1uFPrS_ctXzHKohBObdCOBN_rP-n4hkAo9h';

var roads = new ol.layer.Tile({
  source: new ol.source.OSM()
});

var imagery = new ol.layer.Tile({
  source: new ol.source.BingMaps({key: key, imagerySet: 'Aerial'})
});

var magnCntrl = new ol.control.MagnifierControl({
scaleOffSet:2,//the diff between our map and magn map
radius:100//,//the radius of magn lense
//layers:[ro4ds]
});

var map = new ol.Map({
 controls: ol.control.defaults({
    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
      collapsible: false
    })
  }).extend([
    magnCntrl
  ]),
  layers: [roads],
  target: 'mymap',
  view: new ol.View({
    center: ol.proj.fromLonLat([-109, 46.5]),
    zoom: 2
  })
});
<div id="mymap" class="map">
</div>
.map {
  width: 100%;
  height: 300px;
}