JSFiddle - React, Tailwind, and code Playground

by Pangur Ban

HTML

<div class="viewport">

  <div class="viewport-inner">
    
    <div class="wrapper">
      <div class="canvas">canvas</div>
          
      <div class="box1"></div>
      <div class="box2"></div>
      <div class="box3"></div>
      <div class="box4"></div>
      <div class="box5"></div>
    </div>
  </div>
</div>

CSS

.viewport {
  width: 300px;
  height: 300px;
  border: 2px solid red;
  overflow: auto;
  position: relative;
}

.viewport-inner {
  margin: 20px;
  display: inline-block;
}

.wrapper {
  position: relative;
}

.canvas {
  position: absolute;
  top: 0;
  left: 0;
  background: rgba(0, 0, 0, 0.1);
  font: 30px sans-serif;
  line-height: 300px;
  text-align: center;
}



[class^=box] {
  position: absolute;
  width: 100px;
  height: 40px;
  border: 2px solid #999;
}

.box1 {
  top: 0;
  left: 0;
}

.box2 {
  top: 0;
  right: 0;
}

.box3 {
  bottom: 0;
  left: 0;
}

.box4 {
  bottom: 0;
  right: 0;
}

.box5 {
  transform: translate(-50%, -50%);
  left: 50%;
  top: 50%;
}

Babel + JSX

const viewportEl = document.querySelector('.viewport');
const wrapperEl = document.querySelector('.wrapper');
const canvasEl = document.querySelector('.canvas');

const MARGIN = 20;
const wrapperWidth = 600;
const wrapperHeight = 600;

wrapperEl.style.width = `${wrapperWidth}px`;
wrapperEl.style.height = `${wrapperHeight}px`;

const viewportWidth = viewportEl.clientWidth;
const viewportHeight = viewportEl.clientHeight;

let left = 0;
if (wrapperWidth < viewportWidth) {
  left = (viewportWidth - wrapperWidth) / 2;
  left -= 20;
}

const canvasWidth = Math.min(wrapperWidth, viewportWidth);
const canvasHeight = Math.min(wrapperHeight, viewportHeight);
const leftLimit = wrapperWidth - canvasWidth + MARGIN;
const topLimit = wrapperHeight - canvasHeight + MARGIN;
canvasEl.style.width = `${canvasWidth}px`;
canvasEl.style.height = `${canvasHeight}px`;
wrapperEl.style.left = `${left}px`;

viewportEl.addEventListener('scroll', onScroll);
onScroll();

function onScroll() {
	let scrollLeft = viewportEl.scrollLeft;
  let scrollTop = viewportEl.scrollTop;
 
	scrollLeft = (scrollLeft > leftLimit) ? leftLimit : scrollLeft;
  scrollTop = (scrollTop > topLimit) ? topLimit : scrollTop;
  
  if (scrollLeft < MARGIN) {
  	scrollLeft = 0;
  } else {
  	scrollLeft -= MARGIN;
  }
  
   if (scrollTop < MARGIN) {
  	scrollTop = 0;
  } else {
  	scrollTop -= MARGIN;
  }

	canvasEl.style.left = `${scrollLeft}px`;
  canvasEl.style.top = `${scrollTop}px`;
}