JSFiddle - React, Tailwind, and code Playground

by Pangur Ban

HTML

<div class="viewport">
  <div class="canvas">canvas</div>
  
  <div class="scroll">
    <div class="scroll-inner">

      <div class="padding">
        <div class="box1"></div>
        <div class="box2"></div>
        <div class="box3"></div>
        <div class="box4"></div>
        <div class="box5"></div>
      </div>
    </div>
  </div>
</div>

CSS

* {
  box-sizing: border-box;
}

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

.scroll {
  position: relative;
  z-index: 100;
  height: 100%;
  overflow: auto;
}

.scroll-inner {
  margin: 20px;
}

.padding {
  position: relative;
  margin: auto;
}

.canvas {
  background: rgba(0, 0, 0, 0.1);
  position: absolute;
  top: 0;
  left: 0;
  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 scrollEl = document.querySelector('.scroll');
const canvasEl = document.querySelector('.canvas');
const paddingEl = document.querySelector('.padding');
const OFFSET = 20;

const width = 600;
const height = 600;

paddingEl.style.width = `${width}px`;
paddingEl.style.height = `${height}px`;

let left = 0;
if (width < scrollEl.clientWidth) {
	left = (scrollEl.clientWidth - width) / 2;
  left -= 20;
}

canvasEl.style.width = `${Math.min(scrollEl.clientWidth, width)}px`;
canvasEl.style.height = `${Math.min(scrollEl.clientHeight, height)}px`;
canvasEl.style.left = `${left}px`;

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

function onScroll() {
  const top = scrollEl.scrollTop;
  const bottom = scrollEl.scrollHeight - scrollEl.scrollTop - scrollEl.clientHeight;
  const left = scrollEl.scrollLeft;
  
  let y = 0;
  if (top <= OFFSET) {
  	y = OFFSET - top
  } else if (bottom <= OFFSET) {
  	y = -(OFFSET - bottom)
  }
  
  let x = 0;
  if (left <= OFFSET) {
  	x = OFFSET - left;
  }
  
  canvasEl.style.transform = `translate3d(${x}px, ${y}px, 0)`;
  /* do render */
}