JSFiddle - React, Tailwind, and code Playground

by conKORD

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.12/Rx.js"></script>
<!DOCTYPE html>
<html style="height:100%">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Test</title>
</head>

<body>
    <div>
        <canvas height='360' width='480' id='canvas'></canvas>
    </div>
    <div id="label"></div>
    <div id="log"></div>
</body>
</html>

CSS

body {
            height: 100%;
            margin: 0;
        }

        body>div {
            margin: 0 auto;
            width: 500px;
        }

        div#label,
        div#log {
            text-align: center;
            font-size: 30px;
        }

        canvas {
            border: 1px dotted gray;
        }

JavaScript

(function () {
        const canvas = document.getElementById("canvas"),
              ctx = this.canvas.getContext('2d'),
              label = document.getElementById("label"),
              debug = document.getElementById("log");

        const canvasHeight = canvas.offsetHeight,
              canvasWidth = canvas.offsetWidth;

        const initialY = canvasHeight / 2,
              initialX = canvasWidth / 2;

        const treshold = 2;//move along one axis N time bigger that move along another axis
        const minMove = 10;

        const mouseMoves = Rx.Observable.fromEvent(canvas, 'mousemove'),
              mouseDowns = Rx.Observable.fromEvent(canvas, 'mousedown'),
                mouseUps = Rx.Observable.fromEvent(canvas, 'mouseup');

        function debugWrite(text) {
            if (text && !Array.isArray(text) || (Array.isArray(text) && text.length)) {
                debug.innerHTML = JSON.stringify(text);
            }
        }

        // Calcualte offset either layerX/Y or offsetX/Y
        function getOffset(event) {
            return {
                x: event.offsetX === undefined ? event.layerX : event.offsetX,
                y: event.offsetY === undefined ? event.layerY : event.offsetY
            };
        }

        //clear ctx on click
        mouseDowns.subscribe(x => ctx.clearRect(0, 0, canvasWidth, canvasHeight));
        //draw mouse path on move
        const lines = mouseDowns.mergeMap(x =>
            mouseMoves.takeUntil(mouseUps).map(getOffset).pairwise());
        lines.subscribe(arr => {
            ctx.strokeStyle = "#000000"
            ctx.beginPath();
            ctx.moveTo(arr[0].x, arr[0].y);
            ctx.lineTo(arr[1].x, arr[1].y)
            ctx.stroke();
        });

        function normalizeAngle(angle) {//fit angle +/- PI
            while (angle <= -Math.PI) angle += Math.PI * 2;
            while (angle > Math.PI) angle -= Math.PI * 2;
            return angle;
        }
        const maxAngleDiff =...