JSFiddle - React, Tailwind, and code Playground

by georeith

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.9.25/paper-full.min.js"></script>
<div id="text-control-container">
  <svg xmlns="http://www.w3.org/2000/svg" id="svg">
    <foreignObject width="100%" height="100%">
      <div xmlns="http://www.w3.org/1999/xhtml" id="text-control" contentEditable="true">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent at libero sit amet elit ultrices venenatis. Curabitur aliquet at risus non molestie. Ut mattis eget odio nec scelerisque. Aenean id ipsum varius, malesuada orci at, sagittis enim. Ut porttitor tempor nibh id mollis.
      </div>
    </foreignObject>
  </svg>
</div>
<canvas id="canvas" resize="true"></canvas>

CSS

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  width: 100%;
}

#canvas {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

#text-control-container {
  vertical-align: top;
  position: absolute;
  width: 300px;
  line-height: 2;
  top: 100px;
  left: 100px;
}

#text-control:focus {
  outline: none;
}

JavaScript

var $textControlContainer = $("#text-control-container"),
		$svg = $("#svg"),
		$textControl = $("#text-control"),
    $canvas = $("#canvas"),
    raster;
    
$textControl[0].style.lineHeight = $textControl.css('lineHeight');
$textControl[0].style.fontSize = $textControl.css('fontSize');
$textControl[0].style.textAlign = $textControl.css('textAlign');

function setSVGSize() {
  $svg[0].setAttribute('width', $textControl.width() + 'px');
	$svg[0].setAttribute('height', $textControl.height() + 'px');
}
function drawRasterText(raster) {
	var position = $textControlContainer.position();
    
	raster = raster || new paper.Raster();
  raster.scale(1/raster.matrix.a, 1/raster.matrix.d);
  setSVGSize();
  raster.source = 'data:image/svg+xml;base64,' + btoa($textControlContainer.html());
	raster.pivot = raster.parentToLocal(raster.bounds.topLeft);
  raster.position = [position.left, position.top];
	$textControlContainer.css('opacity', 0);
  
  paper.view.draw();
  
  return raster;
}

function rasterMousedown(mouseEvent) {
	if (raster.data.resizing) return;  
  raster.selected = true;
}
function rasterMouseup(mouseEvent) {
  raster.data.resizeLocked = false;
}
function rasterMousedrag(mouseEvent) {
	console.log('raster');
	if (raster.data.resizing) return;
	raster.translate(mouseEvent.delta);
}
function rasterDoubleclick(mouseEvent) {
	raster.off('mousedrag', rasterMousedrag);
  $textControl.focus();
}
function viewMousedown(event) {
  if (raster.data.resizing) {
  	var originalWidth = raster.width,
  			originalLeft = raster.bounds.left,
        originalX = event.point.x,
        direction = raster.data.resizing;
      
  	raster.data.resizeLocked = true;
    raster.data.originalWidth = raster.width;
    raster.data.originalLeft = raster.bounds.left;
    raster.data.originalX = event.point.x;
    raster.data.resizeDirection = raster.data.resizing;
  } else {
		if (event.target !== raster) raster.selected = false;
  }
}

function viewMousedrag(event)...