JSFiddle - React, Tailwind, and code Playground

by Pangur Ban

HTML

<div class="chart">

  <div class="chart_container">

    <div class="chart_wrapper">
      <div class="chart_canvas">
        <span>canvas</span>
      </div>

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

SCSS

.chart {
  overflow: auto;
  position: relative;
  // test
  //width: 300px;
  height: 300px;
  border: 2px solid red;
  /// ---
  
  &_container {
    padding: 20px;
    display: inline-block;
  }
  
  &_wrapper {
    position: relative;
  }
  
  &_canvas {
    // test
    position: relative;
    background: rgba(0, 0, 0, 0.1);
    font: 30px sans-serif;
    text-align: center;
    //---
    
    span {
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
    }
  }
}

[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

class CanvasPosition {

	constructor(wrapperWidth, wrapperHeight) {
  	this.wrapperWidth = wrapperWidth;
    this.wrapperHeight = wrapperHeight;
  
		this.init();
    this.layoutCanvas();
    this.positionCanvas();
  }
	
  init() {
    this.chartEl = document.querySelector('.chart');
    this.containerEl = this.chartEl.querySelector('.chart_container');
		this.wrapperEl = this.chartEl.querySelector('.chart_wrapper');
		this.canvasEl = this.chartEl.querySelector('.chart_canvas');
    
    this.chartEl.addEventListener('scroll', e => this.positionCanvas());
    window.addEventListener('resize', e => {
    	this.layoutCanvas();
      this.positionCanvas();
    });
  }
  
  layoutCanvas() {
    this.wrapperEl.style.width = `${this.wrapperWidth}px`;
    this.wrapperEl.style.height = `${this.wrapperHeight}px`;
    
    const chartWidth = this.chartEl.clientWidth;
    const chartHeight = this.chartEl.clientHeight;
    const canvasWidth = Math.min(this.wrapperWidth, chartWidth);
    const canvasHeight = Math.min(this.wrapperHeight, chartHeight);
    const containerWidth = this.containerEl.clientWidth;
    this.margin = (containerWidth - this.wrapperWidth) / 2;
    this.leftLimit = this.wrapperWidth - canvasWidth + this.margin;
    this.topLimit = this.wrapperHeight - canvasHeight + this.margin;

    let left = 0;
    if (containerWidth < chartWidth) {
      left = (chartWidth - containerWidth) / 2;
    }

    this.canvasEl.style.width = `${canvasWidth}px`;
    this.canvasEl.style.height = `${canvasHeight}px`;
    this.wrapperEl.style.left = `${left}px`;
  }
  
  positionCanvas() {
    const scrollTop = this.chartEl.scrollTop;
    const scrollLeft = this.chartEl.scrollLeft;

    let top = (scrollTop > this.topLimit) ? this.topLimit : scrollTop;
    if (top < this.margin) {
      top = 0;
    } else {
      top -= this.margin;
    }

    let left = (scrollLeft > this.leftLimit) ? this.leftLimit : scrollLeft;
    if (left < this.margin) {
      left = 0;
    } else {
     ...