JSFiddle - React, Tailwind, and code Playground

by egon

HTML

<script src="//cdnjs.cloudflare.com/ajax/libs/rxjs/2.3.0/rx.all.min.js"></script>
<canvas id="ribbons" width="640" height="480"></canvas>

CSS

html, body { background: #fff; }
        #ribbons { border: 1px solid #000; }

JavaScript

(function () {
	'use strict';

	var Observable = Rx.Observable;

	var filterById = function(id){
		return function(p){ return p.id == id; };
	};

	var canvas = document.getElementById("ribbons");
	var context = canvas.getContext("2d");

	// a touch event stream
	var touch = function(eventname){
		return Observable.fromEvent(document, eventname).
			selectMany(function(ev){
				ev.preventDefault();
				var pointers = ev.changedpointers.map(function(t){
					return { 
						id: t.identifier,
						x: ev.pageX - canvas.offsetLeft,
						y: ev.pageY - canvas.offsetTop,
						timestamp: ev.timeStamp
					};
				});
				return Observable.fromArray(pointers);
			});
	};

	// a mouse event stream
	var mouse = function(eventname){
		return Observable.fromEvent(document, eventname).
			map(function(ev){
				ev.preventDefault();
				return {
					id: "mouse",
					x: ev.pageX - canvas.offsetLeft,
					y: ev.pageY - canvas.offsetTop,
					timestamp: ev.timeStamp
				};
		});
	};

	// stream of pointer starting
	var pointerStart = Observable.merge(
		touch("touchstart"),
		mouse("mousedown")
	);
	
	// stream of pointer moving
	var pointerMove = Observable.merge(
		touch("touchmove"),
		mouse("mousemove")
	);

	// stream of pointer ends
	var pointerEnd = Observable.merge(
		touch("touchend"), touch("touchcancel"),
		mouse("mouseup"), mouse("mouseleave")
	);

    function manhattan(a, b){
      return Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
    };

	// stream of stream of mouse locations
	var trails = pointerStart.
		map(function(start){
			return pointerMove.
				startWith(start).
				where(filterById(start.id)).
				distinctUntilChanged(null, function(prev, cur){ return manhattan(prev, cur) < 10; }).
				takeUntil(pointerEnd.where(filterById(start.id)));
		});

	// the application state
	var Drawing = {
		trails: []
	};
	trails.subscribe(function(points){
		var trail = new Trail();
		Drawing.trails.push(trail);
		
		// trail...