Basic Drawing App 
Tags: webdev, canvas, streams, flyd
Published: 2020-12-23 14:15:10

Here is a basic drawing app using streamsflyd.

I have choosen Flyd, but you can use whatever library you like.

You can play with it here : [DrawApp](https://flems.io/#0=N4IgtglgJlA2CmIBcA2ALAOgJxYDQgDMIEBnZAbVADsBDMRJEDACwBcxYR8BjAeytbwByEAB5uNKgDcaJAAQlWATwQBeADogARjW4BrAOYAnXgFcqUJK0maA3HOgaQE6bM1yA7tFbMnAZgAGAIAHAA93ZngIAzZ-ILDNAD5RAHoXGRJErhASeARuVgh+MkYApD8AJhAAX1xqOgYQKCMaD1gILQwAKzIefkFhRhq6kFp6EW7e536hVhEUlLkCWCUodSoAQgJzAqKqAAoqXFYASmBNXi0u+ALNVVVlYPheAjl4UODeI1YSADJfzTmKDwIhUeBrEAbB5KJ4vORgXhQUwIAD8CKRCAw70+3xID32JyQmm2VF2-Du0Nhr2BoPg-xpEDBGDoUBRDLB+1OSCoGGWq3xJ2qnOYEBIuBJZIOZ0BuQUrCMEFuIFsMiMcioqglhX4h2OZxIXlY3GYhzOEllZSM8FYpiMVCWO21UuAVptdrkrGZwWCK2FotwNCMBlM9AEJEFtnN8DkAEYkK7bfatXtTS7rYmPV6fUo-WLA8HQz8I1G5BV4+n3cmdUd4GcE+7PTRvb6fP78yHZuHqpHZNG-OW3UnHSma7gjHWK-bG82c6280GO2Hi725GgBxmqwdR0ZcKYJ4PM03s7mAwvC12e7KAKzryvD6u4eBj3cB-cZ6fHuengud5eylC3kOpJOrqT47qYAa4BAb4NlmLYivOP5Lt2JYAOyAQ6wEjo+z4QTQUG4OGaYHh+8Ftmev4oSuAAcGGbqBuGQRAhG4LwMFTnBs4Id+i5FlRspYHR95bjh4FMSxvC4FA7GHjOJ7tuef7RjGlqTphkoMWJ+HMWKklQOKMmkVx5FIXxtg0jQyKsFYzAmB46rwPZACiRgmEY+yaAAYhARiKHICmzB6vByAA+oGEDKPCph+Vo0Y0Oq-AALRggYNCFFI0aMoIBjwGqVDBcY8BpblHrMJIHpCJogrdqqHqasJqb1vaVDIrAUJUP8FxXDcrAUo8zyvB1vwbAE9yqFQ5CaAAAlNm40LAKQ+ro8DMLwsDAkYmgALo1YGbz1Vh1YyZubz7OOxEZqN9wBWGGAIFQBg+AAPk9rBnScKLwNynHyRRyG7WqRgHZKp1acdDVnAQXz7LVECqOQW2EaoASsaoO5QMj5miKYd1CI9zAvSQog3T8uMPT4thnLVBBYzj934-8+wbG9pjkFAW0nITiSqCTJBk-jH0EKorPs0g+xC7z5AkIjJAANSqDGJxQWzW2asc4uc09+y8IlCtK1A8sxtUTVyLwojIyiNA-V+0Hctrj5nQRNAnNVu7AyBVDg4d9ruXh+qGsa+y8-zPhmiuqkHkYl7KRhLMfUYSDwOLDWnBd7oHBBpzVCc5kgpZsDWSbcf-G9zsogncefcn3ucjJBysK+2dWEHH1J-RqcmxnepN13zvVP31caQ3e5p-aMZjawVd7nbDfuQ3CO7i72e2CdNAEqPI3VLDSML7w8OIxjiUxuKqgbMf3CnzGKp7cEqjAN2J2wLqqe1WBL46aoHj7Avzsr9Dpj7ygoA+AmN4CiE9PTCmstZa1ikLwaAcgAhQgeOQeAW1GY42CNFE0rBUEc1wHAhBSD7i4LQViCw-wIAYCwSQHBeDyHSRzk1M6n99gQU9icDAwJggkAAMJlQeuCQBRheRUAAIJBjxCI7hfM+CkjSt-Hc0j4A8P4ZIHK7MlYiKEBjMUfN2iKCELlPmNCTTjlwAAZX2DpJWfMZGqGYmI96Y5qgnRNEcDuakn7t0fGcJO9cMAyFgEvXA5BWAc1ccJOQUhGpqXcccBCgoToGFiQeeuiTInezkDmT2o94TPwySdMAz8qZ7RAcEDAigrR0H2IrMcqgKk6noqUvKqgk4nCPrYfxSsqDmwCP8eADCmYBEFEwzkDCc61QARUqpRVilKxoA0jAfAwBaEZG3FOr5gA4x0cMyZe04b1wJDnJO7TZZ1PclQnRSsAEmm0oKUJ4SmFqQqTokgAB5A4gydGvgDJkkGvCSl5O8Q1DxvjgD+MCfNcZQSQlhNwFQCJJ0ACqqSMyAo8YUqJAA1NF7pAVfk9v8p0cgACy68mqaFmTUzQss5xQtgLLKqmhiV7DkFodeJ164ySujzP6pNIHMBRDyIJYsuWcnBYMoJDwuksFkNi+ap8AgqngVARBY0IAon2BfEa4pQUtI9KJN2PIDEDGMX-OemNWDYxDswWw0DTgsNZk8hhY0qAogAHLvTFoQtVxC0ZcJUXwgRGj-jKNUSG8E1DsGmjHJU1ayIoDIuCFAYqSrcDvPejnKG7lzLc2VYlRK0kRpjX2CA3gKtOG0LMOtZNqbBD-CcbWR8GAACOph4AdoxmfIUzb9F4x8IkfpvxQgElwDqxWSA4b3HgCieiHiDVgQtWW0B4DbX2pgScFhpCOYMOQe6mGqrEHIPDcG9R4Iw2BojeeqA0baGxpEdWxNda00jUJF68cvaW2mqMb5QkrbB4gVrMAeuvjai1iFA3T2CLBSdzlSQBVsBL4IoZaoX1iCUOmBTcVXygCTWijNbhhePJ22dqEWfFDOi0NHpRjyJsqhkUIsmoQSQ1gSBKESrASQUAUhgCbNtcaGA+O3ykEx4krHZAca4xYFI-GQCq2xWJljAhJOce4ykF4AmeQvCWdSsASmCASfY2pmTxoaCMi08ssqjJVBKBQ8ECATxVAGBQ3IqppgChfF0-KOZKHWC8AAFIWPeR692KY64MtqDyfzFj5SMgMKoUlCLWX8E8EC1+qgOXPIPIMggGoG6DPsTWK9JBSXWmQ4VoNajBEYxNQO5gQ6UQIyQOhlGgyn21uw4IZDViaxK3gCl+0Tjcm1VYLYCABB9hM05ONThOiXojWQZ6HRDKTj-HnexEt9weQyLK6wF6G28k7aDXtwTMisQZSMDmQ7cGyoIfmtnFDu3rRCiJaaE4E3D1EI1WcQ5T2quRqgIzHkeWJEGBIOQEH4jJG2qPqrY716av7LVCAqHP0ocodB5IyZNHkF0l+F8pWcNWsENx9tkr1XQ2-EOBTwH+8enxprUmrrICKNn1LQQF63A1sjrHTdtS+HDFgl8pU3g9BAMjgix15n9aBuClNIzbgwq7oEd-XzHNTldAmkOzyaXL6G2-AA2ImDPcaewsFHkWURuTcnUzSNvaBXBM-uFyQcbk32fk9I12yG0MSMdq7em1gVrRCDMFRu04mb4BhI5rYCt0D2bjX7idADC7gAmJjacY4GAsOy5F6Y2Nno9cs6VYNuQo6ffuSFiNWw-byYNeVQajUfNaETbekrIPPIc84ebyKAgbfbCsH+LrhNnXZcl1NK4y+pfet6mADmmGZTV11brxu2sE0yHO+MXenBSSoldCBbgz0jJgShHeZNz2qtD+w5jIjCB9XC2l6cqmefo3g8I9r-jcPJx99HHf9HlXQuxiOc7+tqyMpeXqI2ZOZ2Qa-wT+0GrW+6DCsBNO1ypeAACuvMbC8pUj5nQGFkdMAK-l-D-P1plgSLYCbN8hYA8C2ljuDoAhAqri7tvmWgsvyh-oOsOt0o+LUDMrgWAMxoZipsZtJjxppvJt5tUmABgDpnwVIbgBUismsmCMLPsBULgMEgoRgKKLFlIfgVuJthsFNh3jzp6G5vKB5v5kYCXMsvNLADYUeCsDzpoHdohpoIyOqNfGqKwLwdoWAPQFABAGmvzoOB7tATwntsDiVqdrqsNi7MlloW8p8voUCibE-sthYAzpvnnhnhMlnitudvntBj4VocJqoeocwErIodZhqKYGobgGAFUV6OUbgLwk0fwC0fOrPibCCjXKnAErCrUNHqMloSQC4KoX4HqjXGCsBrVEDL0RpGBHWDNqBlKvNHCmgtlhmCIq4fNC9HPFoo9hUvQEGCAnUeoV0S-IvhUhAP4eCEEYIPsAsR7McKJHWOQAEFtFXEYB8VtMct9LsbAFXCKusVYPBohgMjCusUMR4hzFse6K8hYB8gcM8eFhvAEDCQwvkRYPgs2nLlofKJICQEiNwGcfUZcXXONIcI5HIGgUrKiTqAVgakDBNNNFNISVQMSR5rlCkIYsENtF9n6lnrChQWpNYcNLyj8WyRyVyaSUYCkFaCSeCNtFqlQVAHsmOMxjNDKUqfKUEh2ttISEYEMU8kccsraFdqFhBBUgQCYGAGgXaaKCAodq-nIXMuQXBj4EIBLgQW9MUUMm+o9iUTaVxqwAMA6WLk6Skbkj0T6SJP0QSBgF6dyjCRErgGgdQiYP5v1FqeyS0JybqSkIyBFAJs0vfOmZmbwNmTCFHtKfmbKTyVaCQFZKWRDOWRmcEFmVWTWbmTqdyfKXya2dMd0WpMGcvOsOsG5mtIMtwAgIGOQROVQAgKwHIAACItAeBiLehyCqByBxl7lQC8Dc5yDnD2hyDrByByDLlyDpCyC2AXnnlnkLA3kzChArkVBrBnnXkFChD3lnkPnPlWgFDqIICeAtDejxY3mSAZAPnXlAUrm7n3x-mXkAWLB6YkCwXWjwhmC5Ckq8AZQAAkO5SwKwt6emC5X5WFCI0U8AvCvAXwUAJARFu51FuF+F8ABFD5l5QmTY+wYAO5iQe5wAoQcgSA8IGAIliUcg8FEluA2Sol4lSgcgUlMlSg2cOcF5F5CwkQsATwvkF515NAMATkGUAgxFZaUgDcN5WgJwAlUFrgfMRlUAJlswAAMkwblBZVZdwDZchdeXkKwAAEJKAACSaqu51i0kdlh53AvEGAOUrATkCAhYwVYVkVGl9o15sAvABg5lYAJABgtlqgglU5mI2VKS+VhVyFqF-kzYioaUbKigxUmFK5TVggxFp5l5l5VSvAegtFa0XwClmgWgXG+gmguAXFcgzQrQkFYlhmsAuQD53YD5D5J0TV3w65rQBIJ5k115wlclalxFrF-VDFTFFFXVN5rAoQGAsUBgjIaBaUJoGVF1P5Qm7FAAKrwPsKEAdc9V1W1YVhuZBbuRYfAMhXINUFxatVEtNV-LZZ1V1RNnuQDVwkDQ9PDZNVeVhftdktUEdThSdUYIxQRedS9VdSrmCJ9d9b9eDV1a9T1X1aTZeZDWeSzShWeWtf5sEJtXDSeezRdUjfsCjbDfFhjWeRdcLWjblbufNbkLTRDUtStRzVEsWYlaZT8HuWLRdU5S5QIB5OAATQiBlONdhTRXhYRX9ZeTrerfrcdUbfACbbDZbf5MZTbZoMdYeR4FQCbetawDzc7dbbMLbQTVhj7Vzf7eDXjUrZeSdKrVucEJrTteLfZRkMRQFaleqZoLeRhSAM7T+cRdnfFdaHRQIO8G9JoB+VVPYJNfBQXdBbIEXUFWYBYPFrwu0LMAAEo9RM2XXXUM39XZVqi7nDWjV6B2CTWq260-Ck1s2PmXkmzABx3eiz3rB41bonCLk83x3aFUARTx0EjZArIOYIBGAiA6CxScD4C5D5BOglAgBoAAQVBXjDD1DjCMDLIkBTByIDBzBDDVCIyjANAiBb3ehoA1BAA)


```js
let DrawApp = (function (doc) {
  
  let canvas;
  
  // context 2d
  let ctx;
  
  // rectangle wrapping canvas
  let rect = {};
  
  // streams
  let mouseMove$ = flyd.stream();
  let mouseCoords$ = mouseMove$
    .map(m => ({x : m.x - rect.x, y : m.y - rect.y}));
 
 //helpers
 let addEvent = (evt, cb) => canvas.addEventListener(evt, cb);
 let eltById = (id) => document.getElementById(id);
 let log = (msg) => console.log(msg);
 
  // application state
  let state = {
    strokeColor : "black",
    drawing : false
  };
  
  function startDraw() {
    let {x, y} = mouseCoords$();
    ctx.beginPath();
    ctx.moveTo(x, y);
    state.drawing = true;
  }
   
  function draw() {
    if (state.drawing) {
     let {x, y} = mouseCoords$();
     ctx.lineTo(x, y);
     ctx.stroke();
    }
  }
  
  function stopDraw() { 
    if (state.drawing) {
      state.drawing = false;
    }
  }
  
  function initEvents () {
    addEvent("mousemove", mouseMove$);
    addEvent("mousemove", draw);
    addEvent("mousedown", startDraw);
    addEvent("mouseup", stopDraw);
  } 
  
  function initApp () {
    canvas = eltById("canvas");
    ctx = canvas.getContext("2d"); 
    rect = canvas.getBoundingClientRect();
    ctx.strokeColor = "black";
    initEvents();
  }
  
  return {initApp}
  
} )()

DrawApp.initApp()
```