<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nikola Geneshki</title>
    <description>The latest articles on DEV Community by Nikola Geneshki (@kokozaurus).</description>
    <link>https://dev.to/kokozaurus</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F928278%2F6c8fb0e2-3610-4ba9-b6f0-13ef3a36c735.jpe</url>
      <title>DEV Community: Nikola Geneshki</title>
      <link>https://dev.to/kokozaurus</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kokozaurus"/>
    <language>en</language>
    <item>
      <title>Building REST API for data visualization with Flask</title>
      <dc:creator>Nikola Geneshki</dc:creator>
      <pubDate>Sat, 11 Feb 2023 17:15:16 +0000</pubDate>
      <link>https://dev.to/kokozaurus/building-rest-api-for-data-visualization-with-flask-56i</link>
      <guid>https://dev.to/kokozaurus/building-rest-api-for-data-visualization-with-flask-56i</guid>
      <description>&lt;p&gt;Lately I’m working on a project that involves gathering data in one way or another and then visualizing it using a chart. Up to now I have created a web page with a chart on it, and it wasn’t easy. First I decided &lt;a href="https://geneshki.com/learning-html5-canvas-for-a-stupid-reason/"&gt;I’ll build the chart myself&lt;/a&gt;, then I snapped back to reality and built a web component with Chart.js to replace my excuse for a chart. Now I want to make this page dynamic and actually provide some data to this chart. So here I’ll build a REST API for it using Flask.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requesting data from a REST API
&lt;/h2&gt;

&lt;p&gt;Let’s start this task with a slight deviation. Since I already have some kind of UI for this API I would like to use it to see if I’m doing well or not. This means I need the UI to request the data from the API and then visualize it.&lt;/p&gt;

&lt;p&gt;I could do this last of course, but I prefer doing this first, to shorten the feedback loop. So what do I need to do?&lt;/p&gt;

&lt;h3&gt;
  
  
  Fetch the data
&lt;/h3&gt;

&lt;p&gt;Naturally, I need some data to load. The only way I know how to do this is to ask the back-end to give me some. This back end is going to be our API, so in effect I’m going to make a call to it.&lt;/p&gt;

&lt;p&gt;There are many npm packages which are allowing you to do an http call from a web client, and of course there’s the Ajax calls that are a standard. However I prefer the newer standard Fetch API which is available in every modern browser.&lt;/p&gt;

&lt;p&gt;Fetching the data is pretty simple really. It all starts with calling the fetch function. Let’s try doing it like that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const dataPromise = fetch('/data');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;fetch()&lt;/code&gt; method takes at least the URI to the resource we need. You can see that I have only written the last part of a valid URI. The reason is, that &lt;code&gt;fetch()&lt;/code&gt; considers this resource to be on the same server from where this web page is served. So if your URL is ‘https: //example.com’, &lt;code&gt;fetch()&lt;/code&gt; would append this URL in front of the resource path you give it here.&lt;/p&gt;

&lt;p&gt;This call would return us a promise. &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"&gt;Promises&lt;/a&gt; are a complex beast, but the easiest way to deal with them is to use an &lt;code&gt;async&lt;/code&gt; function and to &lt;code&gt;await&lt;/code&gt; the result. Then we can set it to our chart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function fetchData() {
  return await fetch('/data');
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem with this function is that since itself is asynchronous, it will return a promise, not whatever is behind the &lt;code&gt;return&lt;/code&gt; keyword. The &lt;code&gt;return&lt;/code&gt; actually designates the value this promise resolves to. So to break this cycle of promises, we need to set the data to the chart in this function itself. Perhaps it’s better to rename the function to &lt;code&gt;loadData()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pass the data to the chart
&lt;/h3&gt;

&lt;p&gt;Let’s try to amend the function above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function loadData(chart) {
  const dataResponse = await fetch('/data');
  chart.setAttribute(dataResponse.text());
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s imagine the parameter &lt;code&gt;chart&lt;/code&gt; is the web component I built before. It requires the data to be set to its &lt;code&gt;data&lt;/code&gt; attribute and this attribute is in the form of a string.&lt;/p&gt;

&lt;p&gt;The data itself we can get from the object which the promise from &lt;code&gt;fetch&lt;/code&gt; resolves to. It has a bunch of handful methods, but the one we need is &lt;code&gt;text()&lt;/code&gt;, which gives us the response from the back-end as string. Which reminds me it is time to create this back-end.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Flask Application
&lt;/h2&gt;

&lt;p&gt;Following the &lt;a href="https://flask.palletsprojects.com/en/2.2.x/quickstart/"&gt;Flask Quickstart page&lt;/a&gt; I can quickly make the root page show something:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
    return "&amp;lt;p&amp;gt;Hello, World!&amp;lt;/p&amp;gt;"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And I can run the server with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flask --app hello run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So I’ll save my new Flask application under app.py and run the command above. What I get is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Usage: flask run [OPTIONS]
Try 'flask run --help' for help.

Error: Could not import 'hello'.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, I guess the file needs to be called &lt;em&gt;hello.py&lt;/em&gt;. And then I’m greeted with this wonderful message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; * Serving Flask app 'hello'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! This means we have a server running and we can access it at &lt;a href="http://127.0.0.1:5000"&gt;http://127.0.0.1:5000&lt;/a&gt;! I’ll spin up a browser and check what’s there.&lt;/p&gt;

&lt;p&gt;It’s not a lot, but we have the “Hello World” that was expected! Hurray! We have a Flask application! Let’s do something useful with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining the REST API
&lt;/h2&gt;

&lt;p&gt;One of the most important things that has to be done when you’re devising a REST API is to decide what the resources would look like. This is basically the names of your functions, so they have to be pretty, easy to understand, somewhat short and very, very stable. If you ever change one part of the URI for a resource, someone’s code is going to break.&lt;/p&gt;

&lt;p&gt;Fortunately, we already defined our resource in the front-end part above, so there’s no need to worry about things like this. We just have to implement the &lt;code&gt;/data&lt;/code&gt; resource. So let’s build the function that would process this call.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build the request handler
&lt;/h3&gt;

&lt;p&gt;First, let’s write some Python code and return a string to the caller. The string I’ll take from the web component article and it’ll be the JSON passed to the Chart constructor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def data():
    return '''{
        "type": "line",
        "data": {
          "labels": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ],
          "datasets": [
            {
              "label": "dataset 1",
              "data": [{ "x": 0, "y1":2, "y2":1 },{ "x": 1,"y1":2}, { "x": 1.4,"y1":6 }, { "x":2,"y1":3 }, { "x":3,"y1":6 }, {"x":5, "y2": 2}, {"x":6, "y2":1}, {"x":8, "y2":6}, {"x":9,"y2":6}, {"x":10,"y2":0}],
              "parsing": {
                "yAxisKey": "y1"
              },
              "borderColor": "rgb(75,192,192)"
            },
            {
              "label": "dataset 2",
              "data": [{ "x": 0, "y1":20, "y2":1 },{ "x": 1,"y1":2}, { "x": 1.4,"y1":6 }, { "x":2,"y1":3 }, { "x":3,"y1":6 }, {"x":5, "y2": 2}, {"x":6, "y2":1}, {"x":8, "y2":6}, {"x":9,"y2":6}, {"x":10,"y2":0}],
              "parsing": {
                "yAxisKey": "y2"
              },
              "borderColor": "rgb(192,192, 75)"
            }
          ]
        }
      }'''
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Route the requests to the handler
&lt;/h3&gt;

&lt;p&gt;Now we have our data ready to be served, we only need to call this method when there is a request for the &lt;code&gt;/data&lt;/code&gt; resource. Just slap an &lt;code&gt;@app.route&lt;/code&gt; decorator on top of the method like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@app.route("/data")
def data():
  ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this is enough to receive the data once we navigate to &lt;code&gt;http://localhost:5000/data&lt;/code&gt;. So our REST API is up and running. But when I open the web page with the chart I created for testing I see only a form, no chart. There should be a chart here, but it doesn’t show:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cBtiBeta--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xubychytwhiuzf1ktgcr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cBtiBeta--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xubychytwhiuzf1ktgcr.png" alt="No data is visualized yet, but there is an error in the console." width="880" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Allowing Fetch to use REST APIs with different origin
&lt;/h2&gt;

&lt;p&gt;This is a bit of a side topic but it completes the picture. Since I’m building the UI above with React I’m running a server on &lt;code&gt;localhost:3000&lt;/code&gt;. For the REST API I run a development Flask server on &lt;code&gt;localhost:5000&lt;/code&gt;. While they are both running on my local machine, the fact that they’re two separate servers is enough to confuse &lt;code&gt;fetch&lt;/code&gt; and send the REST API call to the UI server.&lt;/p&gt;

&lt;p&gt;To fix this I can simply add the full address of the &lt;code&gt;/data&lt;/code&gt; resource:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function fetchData() {
  return await fetch('http://localhost:5000/data');
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would, however, fail since I’m calling a different server from origin. In fact, it fails with this particular error in the web browser console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://127.0.0.1:5000/data. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 200.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CORS setup for our Frontend
&lt;/h3&gt;

&lt;p&gt;CORS stands for &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS"&gt;Cross-Origin Resource Sharing&lt;/a&gt;. It is by default disabled, because Cross-Origin requests can be exploited for a cross-site scripting attacks. But sometimes people do need to access multiple servers for a single web page, so there is a solution to this too.&lt;/p&gt;

&lt;p&gt;First’ let’s make our &lt;code&gt;fetch&lt;/code&gt; send requests in ‘cors’ mode by passing an option to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const response = await fetch('http://127.0.0.1:5000/data', {method: 'GET', mode: 'cors'});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s see what happens in the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://127.0.0.1:5000/data. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 200. [Learn More]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Enabling CORS on the Backend
&lt;/h3&gt;

&lt;p&gt;The error that I got in the console is there to tell the developer that something has to be done on the backend. It doesn’t explain it well. But that’s what I found out when I googled it.&lt;/p&gt;

&lt;p&gt;Therefore I needed some code that puts the ‘Access-Control-Allow-Origin’ header to the response of the Flask App. Something like &lt;a href="https://flask-cors.readthedocs.io/en/latest/"&gt;Flask-CORS&lt;/a&gt;. Let’s install it and try it out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install flask-cors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed, I imported it in the &lt;em&gt;hello.py&lt;/em&gt; module and initialized it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the simplest way to use Flask-CORS and it allows CORS on all routes. Once this is done, the error message in the console disappears and a beautiful chart appears on the page:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gn79Xdyt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q8jpzfhbxmt2v80ownt5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gn79Xdyt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q8jpzfhbxmt2v80ownt5.png" alt="Once CORS is setup the client can access the REST API and the chart appears on the page." width="880" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Going Forward
&lt;/h2&gt;

&lt;p&gt;Now that I have a connection between my frontend and the backend, I can focus on writing “business logic” and exposing it through the REST API. This would of course make the Flask app look very ugly if all the end-points are in the single file we created for this server.&lt;/p&gt;

&lt;p&gt;To deal with this, you can use &lt;a href="https://flask-restful.readthedocs.io/en/latest/"&gt;Flask-Restful&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If, on the other hand you are trying to present data real-time, perhaps the &lt;a href="https://geneshki.com/server-sent-events-a-story-of-notifications-python-and-generators/"&gt;Server-sent Events&lt;/a&gt; approach I described earlier would be more appropriate.&lt;/p&gt;

&lt;p&gt;If you wonder how the chart got so big, have a look at &lt;a href="https://geneshki.com/making-canvas-greedy-using-css-flexbox/"&gt;Making Canvas greedy using CSS flexbox&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>flask</category>
      <category>rest</category>
      <category>api</category>
    </item>
    <item>
      <title>How I made Chart.js sane by wrapping it in a web component</title>
      <dc:creator>Nikola Geneshki</dc:creator>
      <pubDate>Sat, 21 Jan 2023 14:01:56 +0000</pubDate>
      <link>https://dev.to/kokozaurus/how-i-made-chartjs-sane-by-wrapping-it-in-a-web-component-pao</link>
      <guid>https://dev.to/kokozaurus/how-i-made-chartjs-sane-by-wrapping-it-in-a-web-component-pao</guid>
      <description>&lt;p&gt;I have this tiny web-based project which isn’t fun, doesn’t go well and I still keep working on once every 6 months or so (perhaps this is its main problem). One of its features is related to presenting loads of data, so a chart would be great to have. At first I looked at libraries for charts like Chart.js and Highcharts, and I decided on Chart.js because of its license. Unfortunately it has this unintuitive API that requires to pass a canvas to it instead of directly declare a line chart.&lt;/p&gt;

&lt;p&gt;That’s why I decided to &lt;a href="https://geneshki.com/learning-html5-canvas-for-a-stupid-reason/" rel="noopener noreferrer"&gt;make it myself&lt;/a&gt;. Not a good idea, but that’s how I roll. Six to eight months of trying to make it work, I had a horrible spaghetti code that yielded this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flqwc9fxjte8zbgmvtcsq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flqwc9fxjte8zbgmvtcsq.png" alt="The not so beautiful mess that my chart is." width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then I compared my code to the &lt;a href="https://github.com/chartjs/Chart.js" rel="noopener noreferrer"&gt;Chart.js GitHub repository&lt;/a&gt;, and I decided it isn’t worth it to start from scratch. They’re pretty good at coding! So I gave it a try another time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Salvaging useless code
&lt;/h2&gt;

&lt;p&gt;I decided I’m going to replace the old chart I made directly with the new one. This way it was easier to debug and test. This allowed me to reuse the web component setup I worked so hard to incorporate into my failed project. At least there’s some time recovered from that fiasco.&lt;br&gt;
How to make a web component&lt;/p&gt;

&lt;p&gt;Even though I salvaged it from a previous development, I feel it’s right to explain how to make a web component. This way it’s pretty clear what you need to make all of this work.&lt;/p&gt;

&lt;p&gt;Following &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components#tutorials" rel="noopener noreferrer"&gt;MDN’s tutorials&lt;/a&gt;, I created a class that extends HTMLElement and in its constructor I did this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class LineChart extends HTMLElement{
  constructor() {
    super();
    this.attachShadow({mode:'open'});
    this.shadowRootDiv = document.createElement('div');
    this.canvas = document.createElement('canvas');
    this.shadowRootDiv.append(this.canvas);
    this.props = {};

    this.shadowRoot.appendChild(this.shadowRootDiv);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s very important to call the &lt;code&gt;super()&lt;/code&gt; constructor to initialize the web component.&lt;/p&gt;

&lt;p&gt;Then we need to attach the shadow DOM and afterwards we have all the necessary elements of the component created and structured. This includes the &lt;code&gt;canvas&lt;/code&gt; we’ll be using for Chart.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    this.attachShadow({mode:'open'});
    this.shadowRootDiv = document.createElement('div');
    this.canvas = document.createElement('canvas');
    this.shadowRootDiv.append(this.canvas);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, of course, we need to append the component elements to the &lt;code&gt;shadowRoot&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;this.shadowRoot.appendChild(this.shadowRootDiv);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Interacting with the canvas in the web component
&lt;/h2&gt;

&lt;p&gt;What my attempt to create a chart has thought me is that if you create a web component, you can’t use it’s elements directly from its constructor. It’s simply too early and they’re not attached to the DOM yet.&lt;/p&gt;

&lt;p&gt;This means you need to wait for the canvas to be loaded before you can access it. Therefore I didn’t create a new Chart object directly in the controller, but put it into a separate function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;render() {
  const data = JSON.parse(this.getAttribute('data'));
  const context = this.canvas.getContext('2d');
  const chart = new Chart(context, data);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also see that I extract the data for the chart from the web component’s attributes. It needs to be parsed before it is passed, since it is in the form of string.&lt;/p&gt;

&lt;p&gt;This function is still not executed though. I needed to find an appropriate way to call it, when all the elements of the web component are loaded.&lt;/p&gt;

&lt;p&gt;Somewhere in the MDN docs I found this lifecycle function that the web components have. It’s called &lt;code&gt;connectedCallback&lt;/code&gt; and it is executed right after the shadowDOM is connected to the page DOM. After this I can happily edit a canvas and see the results on the screen. So I used it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  connectedCallback() {
    this.render();
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Importing of Chart.js modules and dependencies
&lt;/h2&gt;

&lt;p&gt;Of course, even if you assemble the code snippets above, the code wouldn’t work. After all, we haven’t imported the Chart object yet. But that’s not too hard to do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Chart from 'chart.js/auto'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would import all the different modules that Chart.js uses.&lt;/p&gt;

&lt;p&gt;But why is there an entire heading in this blog post just for one obvious line of code?&lt;/p&gt;

&lt;p&gt;Well, the Docs say you can also import parts of the modules instead. This seems a good practice in a larger project, where you don’t want huge chunks of useless code to be delivered to your user despite you want to just use a Line Chart.&lt;/p&gt;

&lt;p&gt;To import only the Line Chart relevant modules you need to import Chart first, then the modules, and then register them like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Chart, LineController, LineElement, PointElement, CategoryScale, LinearScale } from 'chart.js'

Chart.register(LineController, LineElement, PointElement, CategoryScale, LinearScale)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this, it should be possible to draw a chart on your canvas.&lt;/p&gt;

&lt;p&gt;One thing I haven’t tried yet is what happens when two web components import the chart like this and then they are used on the same page. I hope that the web component works as a module and there is no problem doing this, but I need to test it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Passing the data to the Chart.js object
&lt;/h2&gt;

&lt;p&gt;At this point I have a setup that would work only if there were data to show.&lt;/p&gt;

&lt;p&gt;To pass the data to the chart I’d put it in the &lt;code&gt;&amp;lt;line-chart&amp;gt;&lt;/code&gt; tag’s &lt;code&gt;data&lt;/code&gt; attribute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;line-chart id='line-chart-id' data="{type: 'line', ...}" className="chart"&amp;gt;&amp;lt;/line-chart&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way our web component can read it and pass it to Chart.js.&lt;/p&gt;

&lt;p&gt;You might notice that the data attribute is a string. This is totally ok, but it’s much easier for me to write down the configuration in a json instead. So I prefer having an event handler which sets the data dynamically. This also allows me to load the data asynchronously later (like from a server or something).&lt;/p&gt;

&lt;h3&gt;
  
  
  Set the data to the web component dynamically
&lt;/h3&gt;

&lt;p&gt;To do this instead of having a data attribute in the definition of the tag, we can set up an event handler, which loads the data and sets it to the line chart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;document.addEventListener('DOMContentLoaded', async (event) =&amp;gt; {
  const lineChart = document.getElementById('line-chart-id');
  lineChart.setAttribute('data', await fetchData());
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see that the data is loaded by calling the asynchronous fetchData() and awaiting its result. There we can write down the json which is representing our data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function fetchData() {
  return JSON.stringify({
    type: 'line',
    data: {
      labels: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ],
      datasets: [
        {
          label: 'dataset 1',
          data: [{ x: 0, y1:2, y2:1 },{ x: 1,y1:2}, { x: 1.4,y1:6 }, { x:2,y1:3 }, { x:3,y1:6 }, {x:5, y2: 2}, {x:6, y2:1}, {x:8, y2:6}, {x:9,y2:6}, {x:10,y2:0}],
          parsing: {
            yAxisKey: 'y1'
          }
        },
        {
          label: 'dataset 2',
          data: [{ x: 0, y1:2, y2:1 },{ x: 1,y1:2}, { x: 1.4,y1:6 }, { x:2,y1:3 }, { x:3,y1:6 }, {x:5, y2: 2}, {x:6, y2:1}, {x:8, y2:6}, {x:9,y2:6}, {x:10,y2:0}],
          parsing: {
            yAxisKey: 'y2'
          }
        },
      ],
    }
  });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What’s important here is that the config of the chart is written in JSON, but before it is returned it is turned into a string. This way we can set it to the attribute directly.&lt;/p&gt;

&lt;p&gt;The rest you can see in Chart.js’s own documentation.&lt;/p&gt;

&lt;p&gt;Unfortunately, the result looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F405w7oma1652q3ejkay5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F405w7oma1652q3ejkay5.png" alt="No chart can be seen here, although the Chart.js web component is loaded" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why is the chart not loading?
&lt;/h3&gt;

&lt;p&gt;Because we’re setting the data dynamically now, our web component is too fast in creating the Chart object. This means it passes empty data object to it.&lt;/p&gt;

&lt;p&gt;To fix this we need to render a bit later. For example when the &lt;code&gt;data&lt;/code&gt; attribute changes. This is why I’m adding the following methods to the web component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  static get observedAttributes() {
    return ['data'];
  }

  attributeChangedCallback(name, oldValue, newValue) {
    this.render()
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This alone unfortunately does not change the page. What it changes is in the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Uncaught Error: Canvas is already in use. Chart with ID '0' must be destroyed before the canvas with ID '' can be reused.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How to make Chart.js redraw?
&lt;/h3&gt;

&lt;p&gt;Well, the problem is now clear – we can’t paint over a chart, so what’s the solution? We need to destroy the chart first. The canvas is reusable, but the Chart object from Chart.js insists on being destroyed before the canvas can be repainted.&lt;/p&gt;

&lt;p&gt;Therefore I added this code to the &lt;code&gt;attributeChangedCallback&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    if (this.chart) {
      this.chart.destroy();
      this.chart = null;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I decided also it is a good idea to force the garbage collector to remove the old object by setting the reference to &lt;code&gt;null&lt;/code&gt;. Oh, and also, notice that the chart is now a property of the web component object. This means our &lt;code&gt;render()&lt;/code&gt; function needs to populate it. Now it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;render() {
  const data = JSON.parse(this.getAttribute('data'));
  const context = this.canvas.getContext('2d');
  this.chart = new Chart(context, data);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The chart is alive
&lt;/h2&gt;

&lt;p&gt;And finally we have a working chart:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F600dqfdecf68k36o2r2b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F600dqfdecf68k36o2r2b.png" alt="The same data I had in my home-grown chart is now looking much prettier in Chart.js" width="800" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While it is not easy to see the gray lines and there are breaks in the lines of one of the data series, the Chart.js version is much prettier than my own.&lt;/p&gt;

&lt;p&gt;It would take another few hours to brush up the rough edges, but it definitely sped up my project with a few months if not years. And this makes me really happy. I’ve learned, that creating a charting library isn’t a two hours work, but rather a years-long moonlighting project if you have a team of one.&lt;/p&gt;

&lt;p&gt;I’ve also learned, that painting things on the browser isn’t impossible. And if you choose your tools correctly you can make some really beautiful projects.&lt;/p&gt;

&lt;p&gt;If this is your kind of fun, then go for it! I definitely don’t regret it.&lt;/p&gt;

&lt;p&gt;Cheers,&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>career</category>
      <category>tooling</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Making Canvas greedy using CSS flexbox</title>
      <dc:creator>Nikola Geneshki</dc:creator>
      <pubDate>Sat, 14 Jan 2023 18:01:40 +0000</pubDate>
      <link>https://dev.to/kokozaurus/making-canvas-greedy-using-css-flexbox-g1g</link>
      <guid>https://dev.to/kokozaurus/making-canvas-greedy-using-css-flexbox-g1g</guid>
      <description>&lt;p&gt;A while ago I started a &lt;a href="https://geneshki.com/learning-html5-canvas-for-a-stupid-reason/" rel="noopener noreferrer"&gt;completely unnecessary project&lt;/a&gt;. There are many problems with it. Nothing is easy. The one that really frustrates me is how the canvas is fixed-size and no matter how big the browser window is, it will always stay the same. So I went on a tangent to fix this and spent 10 hours doing this. I consider this a great loss of valuable time I could have used for gaming, so I think it’s a good topic for a post. Today I’ll tell you how to make canvas fill up the empty space of a page using CSS and the &lt;code&gt;flexbox&lt;/code&gt; layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is flexbox the right way to do it?
&lt;/h2&gt;

&lt;p&gt;No idea. I don’t really do things professionally at home. This is is called work and I have a job where I have to be responsible. At home it’s complete mayhem. Let’s have some fun.&lt;/p&gt;

&lt;h2&gt;
  
  
  The mythical goal of filling up the screen
&lt;/h2&gt;

&lt;p&gt;You know when you have a small web page with maybe an element or two and a huge amount of white space underneath?&lt;/p&gt;

&lt;p&gt;And when you have a footer on the bottom with a different color from white? Then you have a colored line in the middle of the screen instead of the pretty design you worked on so hard. Well, if you have a canvas on the screen and you don’t know how big it needs to be something similar to this happens:&lt;br&gt;
Initially the chart is not stretched to fit the empty space at all.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9mz9bav733xbylx5r07h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9mz9bav733xbylx5r07h.png" alt="Look at all the empty space around the chart! This is a canvas with hardcoded size" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You see, to resize a canvas, you definitely need to set its size in pixels. And using CSS for this job also doesn’t work, since it will only stretch the image, it won’t make the canvas bigger. The canvas has its own reality in which it lives. If it says there are only 100 pixels, you can only paint 100 dots. The CSS would only make these dots bigger and uglier. But that’s a problem for later.&lt;/p&gt;

&lt;p&gt;You could of course grab the parent of the canvas and use its width and height and set them to the canvas. And this would make your drawing space as big as the parent. But if the parent is just a div with nothing else, it will have a height of 0 – just enough to fit all the content in.&lt;/p&gt;

&lt;p&gt;So basically you end up with a canvas which has 0 pixels. These two problems are basically the same case. This is great, because it means I can look at what others did and then apply it to my problem, right?&lt;/p&gt;
&lt;h2&gt;
  
  
  Height of the content
&lt;/h2&gt;

&lt;p&gt;To make the canvas take all the space which is available, first we need to solve the 0 pixel height problem.&lt;/p&gt;

&lt;p&gt;A bit of CSS would help with this. If we set the body height to 100%, it should do the trick. Except it doesn’t.&lt;/p&gt;

&lt;p&gt;Let’s say we have the following HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        &amp;lt;div id="canvas-wrapper"&amp;gt;
            &amp;lt;canvas id='canvas-id' class="chart"&amp;gt;&amp;lt;/canvas&amp;gt;
        &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see there are a few CSS classes there. Let’s ignore them first and put some attributes in the body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;body {
  height: 100%;
}
.chart {}
.canvas-wrapper {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only the body has a height attribute of 100% nothing else. And here’s the result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9vgvfdlu5h6vpjkq2p4m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9vgvfdlu5h6vpjkq2p4m.png" alt="Setting 100% height on the body of a canvas isn’t exactly working as expected." width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When only the height on the body of a canvas is set to 100% the chart takes only a small part of the available space&lt;/p&gt;

&lt;p&gt;Of course the canvas height is set using javascript like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    this.canvas.width = this.parentElement.clientWidth;
    this.canvas.height = this.parentElement.clientHeight;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here the canvas is edited in a dedicated javascript object, which also has a link to the parent of the canvas.&lt;/p&gt;

&lt;p&gt;As you can see from the picture above, something went wrong. A way too big part of the screen is unused, when the goal is to have ginormous chart with no blank space. I guess the percent unit of the height is not the one we need to use. Let’s use the &lt;code&gt;vh&lt;/code&gt; unit. The difference between it and the percent is that the &lt;code&gt;vh&lt;/code&gt; reference is the viewport height, instead of the parent height.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;body {
  height: 100vh;
}
.chart {}
.canvas-wrapper {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Additional structure
&lt;/h2&gt;

&lt;p&gt;This alone, unfortunately, does not change the result. So let’s introduce a bit more markup. I’ll wrap the &lt;code&gt;canvas&lt;/code&gt; into a &lt;code&gt;div&lt;/code&gt; and just for fun, I’ll add a &lt;code&gt;form&lt;/code&gt; into another &lt;code&gt;div&lt;/code&gt; next to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;div class="App"&amp;gt;
      &amp;lt;div class="item"&amp;gt;
        &amp;lt;form&amp;gt;
            &amp;lt;input /&amp;gt;
        &amp;lt;/form&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div class="item-main"&amp;gt;
          &amp;lt;canvas id='id' class="chart"&amp;gt;&amp;lt;/canvas&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div class="item"&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can play around with the different sections and their CSS classes. First, Let’s add some attributes to the &lt;code&gt;App&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.App {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes the outer &lt;code&gt;div&lt;/code&gt; work as a stack. It’s display mode is &lt;code&gt;flex&lt;/code&gt; which means the &lt;code&gt;div&lt;/code&gt; is a flexbox. Now each element within it will act fluidly, filling up space as described with the &lt;code&gt;flex&lt;/code&gt; attribute of their CSS class.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flexbox items
&lt;/h2&gt;

&lt;p&gt;Now let’s have a look at the items in this container. In my case they are simple &lt;code&gt;div&lt;/code&gt;s which have the following CSS classes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.item {}
.item-main {
    flex: 2;
    overflow: auto;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see that one of them is empty while the other one has a few attributes. This is actually crucial in this case. The &lt;code&gt;item-main&lt;/code&gt; class defines the type of flex and the overflow behaviors, while the normal &lt;code&gt;item&lt;/code&gt; class has no such fields. This way we specify the proportions of the different items.&lt;/p&gt;

&lt;p&gt;What this means is, that flex items with class &lt;code&gt;.item-main&lt;/code&gt; would take up twice as much space as ones with a class that specifies &lt;code&gt;flex: 1&lt;/code&gt; and basically all the empty space in our case, since our other class doesn’t have such attribute at all.&lt;/p&gt;

&lt;p&gt;With all this in place, the page with the chart looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe0evizlhgm7bchg8viqk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe0evizlhgm7bchg8viqk.png" alt="The chart takes all the available space when the necessary  raw `flexbox` endraw  attributes are in place." width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After all the necessary flexbox attributes are in place, the chart takes all available space on the page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flexbox is tricky
&lt;/h2&gt;

&lt;p&gt;Like all the CSS, &lt;code&gt;flexbox&lt;/code&gt; is tricky. You can’t simply slap in on a &lt;code&gt;div&lt;/code&gt; and expect it to work instantly and as you want it. However, the concept isn’t that hard to grasp and after a few tries, you can easily apply it to your page.&lt;/p&gt;

&lt;p&gt;On the other hand it makes it pretty simple to align a bunch of elements in a row or a column when compared to the old way of floating &lt;code&gt;div&lt;/code&gt;s. So I believe it’s worth it to learn how to use it and never look back.&lt;/p&gt;

&lt;p&gt;Cheers,&lt;/p&gt;

</description>
      <category>webcomponents</category>
      <category>angular</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Learning HTML5 canvas for a stupid reason.</title>
      <dc:creator>Nikola Geneshki</dc:creator>
      <pubDate>Sat, 15 Oct 2022 11:25:07 +0000</pubDate>
      <link>https://dev.to/kokozaurus/learning-html5-canvas-for-a-stupid-reason-33g</link>
      <guid>https://dev.to/kokozaurus/learning-html5-canvas-for-a-stupid-reason-33g</guid>
      <description>&lt;p&gt;Several times now I have tried to make a simple web application that tracks some personal data. Each time I halt on different place, as is usual, but this time it was when I tried to display the data in a form that allows deriving insights – a chart.&lt;/p&gt;

&lt;p&gt;Plenty of libraries allowing for drawing charts on a web site have been published on the Internet, but when I tried to quickly use one or two, I ended up stuck because of lack of easy to understand documentation. So I decided in a whim I’m going to go with the hardest approach you could choose – writing another one from scratch using the most user unfriendly technology available for the task: an HTML5 Canvas. After all that’s the engineer’s way, right? Reinvent the square wheel again and again… Sigh.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learning the Canvas Basics
&lt;/h2&gt;

&lt;p&gt;As my first goal was to create a simple line chart, I had to start with very simple things: drawing a coordinate system and naming the axes. So I went on with the first line.&lt;/p&gt;

&lt;h3&gt;
  
  
  Drawing a line on a Canvas
&lt;/h3&gt;

&lt;p&gt;Going through the MDN Canvas API documentation, I found out that I need to get the 2D context of the canvas and execute these functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const context = canvas.getContext('2d');
context.beginPath();
context.moveTo(10, 10);
context.lineTo(10,400);
context.stroke();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And I had a vertical line! Marvelous, I felt like I’m going to be finished with this chart within an hour. Let me explain what’s happening here.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We get the canvas and its 2D context.&lt;/li&gt;
&lt;li&gt;We start a path using the &lt;code&gt;beginPath&lt;/code&gt; method. This is used for drawing a set of lines each starting where the other one ends. However, we will only draw one line.&lt;/li&gt;
&lt;li&gt;We move to the point where the line starts with the &lt;code&gt;moveTo&lt;/code&gt; method&lt;/li&gt;
&lt;li&gt;We instruct the canvas to draw a line to the point where it should end.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;stroke&lt;/code&gt; flushes our commands and draws the line on the canvas.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Dealing with the Coordinate System of the Canvas
&lt;/h3&gt;

&lt;p&gt;My hopes of advancing rapidly, however, proved to be an illusion. Once I did the same for the horizontal line which would be the X axis, I found this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2gZeQCAp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3syxkpvfc9rnx3aira09.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2gZeQCAp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3syxkpvfc9rnx3aira09.png" alt="The fourth quadrant of coordinate system, instead of the first, is used in computer graphics and by extension in the Canvas." width="430" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I forgot how computer graphics work… Of course the coordinates are in the Fourth quadrant. This basically means that if I just use the numbers I get from my data, the chart would be completely upside down.&lt;/p&gt;

&lt;p&gt;Because at that point (and even now) I am a complete novice in computer graphics, I kept on trying to brute-force my way through the development of a line chart. Instead of reading a bit about coordinate systems and how to deal with them, I decided I’ll just introduce compensation constants to the coordinates which I get from my data.&lt;/p&gt;

&lt;p&gt;This quickly devolved into a mess which is not worthy of presenting even on the Internet, but was the reason to waste an entire day of my weekend into it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Playing Around with Transforms
&lt;/h3&gt;

&lt;p&gt;Once I was fed up with coordinate silliness, I decided to have a look around on the internet. While I didn’t find a panacea to my problems, I found a keyword – “transform”. Something immediately clicked in my monkey brain and I knew, this is the solution to the upside-down chart.&lt;/p&gt;

&lt;p&gt;Fortunately, these transforms are done with simple methods on the context. Simple, if you know what you want. And read beforehand. Instead, I decided to YOLO it with the hardest to understand of the available methods and some StackOverflow answers.&lt;/p&gt;

&lt;h4&gt;
  
  
  setTransform on a canvas
&lt;/h4&gt;

&lt;p&gt;I found out the canvas context has a function called &lt;code&gt;setTransform&lt;/code&gt; that takes a bunch of arguments. Like 6 of them. This isn’t really great, because 6 is a big number and I’m usually struggling even with 3. But if I look at the documentation often enough that isn’t really a problem. So I decided to use it.&lt;/p&gt;

&lt;p&gt;I read there that the 6 parameters are the upper two rows of a 3×3 matrix, which describes how exactly would the viewport of the canvas change. Each of the parameters are responsible for a certain operation and using all of them somehow allows you to do whatever you want.&lt;/p&gt;

&lt;p&gt;Since I wanted the picture to flip horizontally, I needed to use this matrix:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2PVoCgR9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iwoipou4gg4wob57hnk9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2PVoCgR9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iwoipou4gg4wob57hnk9.jpg" alt="The transform needed to flip to Quadrant I in matrix form." width="122" height="82"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It basically tells the canvas to scale the vetical coordinates with -1 and to move the viewport up by 400 pixels.&lt;/p&gt;

&lt;p&gt;Once I applied this matrix I had the axes drawn in the right places! Perfect.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing Text
&lt;/h3&gt;

&lt;p&gt;At this point I felt like labeling the axes. This meant I had to write down their names, preferably next to each axis and centered either vertically or horizontally, depending on the axis itself.&lt;/p&gt;

&lt;p&gt;Using the &lt;code&gt;fillText(text, x, y)&lt;/code&gt; function of the context, I wrote the name of the x-axis. And this was the result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n3UQcTGF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uq9p2p31mcgc2prrpo41.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n3UQcTGF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uq9p2p31mcgc2prrpo41.png" alt="The text also flips, when you apply a transform to the canvas context" width="428" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s just ignore the fact that the Y-axis label is not well placed. I think the bigger problem here is that it isn’t that easy to read the labels when they’re upside down. It doesn’t really look like English, does it?&lt;/p&gt;

&lt;h2&gt;
  
  
  Transform madness
&lt;/h2&gt;

&lt;p&gt;This is when I felt like I had to read a bit more in the documentation to find out what exactly has happened and how to fix it. There I found out about the transform stack and how to put stuff on it and then take it out. The natural progression was, of course, to use it whether it makes things easier or not. I mean abuse it. That was the word, yes.&lt;/p&gt;

&lt;p&gt;I found the methods &lt;code&gt;save()&lt;/code&gt; and &lt;code&gt;restore()&lt;/code&gt; of the 2d context. They manipulate the transform stack, namely &lt;code&gt;save()&lt;/code&gt; puts the current transform on the stack, and &lt;code&gt;restore()&lt;/code&gt; takes the last transform on the stack and reapplies it. This way you can reset the canvas to its original viewport and then return back to the previous point you were drawing easily.&lt;/p&gt;

&lt;p&gt;It gets even better, when you find out you can store multiple transforms and then layer them back on using &lt;code&gt;restore()&lt;/code&gt; multiple times. So I used all of this at once. And here’s how it went.&lt;/p&gt;

&lt;p&gt;For each feature of the chart – axis, label, tick on an axis and whatever else was there I had to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Save the current transform using &lt;code&gt;save()&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Clear the transform using &lt;code&gt;resetTransform()&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Set the new transform using &lt;code&gt;translate()&lt;/code&gt;, &lt;code&gt;rotate()&lt;/code&gt;, &lt;code&gt;scale()&lt;/code&gt; or &lt;code&gt;setTransform()&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Draw something;&lt;/li&gt;
&lt;li&gt;Restore the previous transform using &lt;code&gt;restore()&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
Further it required from me to remember what the previous state of the transform was, because I needed to decide at stage 5, whether I really want to restore it or not.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While this proved to look neat in the documentation, when I applied it, it ended up being completely custom and unfit for reuse. Further it relied on constant debugging to check what’s happening, since the state of the transform stack was very important. And the result wasn’t really stunning:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1GqhQG85--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rqy6vm2k6emh4tntuzxw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1GqhQG85--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rqy6vm2k6emh4tntuzxw.png" alt="Drawing just the axes of a chart isn’t easy. Using the transform technique I managed this." width="832" height="831"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Look at the mess this chart is. It has the X-axis labels span from way-too-far left to way-too-far right, the Y-axis labels are shifted up for no reason and the labels of the Y-axes is so far away, no one even knows what this text does there.&lt;/p&gt;

&lt;p&gt;What happened!?&lt;/p&gt;

&lt;h2&gt;
  
  
  On State
&lt;/h2&gt;

&lt;p&gt;State management is the main problem which needs to be solved in a front-end application. Sooner or later there’s enough moving parts on a web-site, to make your life a javascript hell: buttons, images, columns of text that need to be placed precisely. Colors have to change depending on some data – there is a reason we call it hell.&lt;/p&gt;

&lt;p&gt;A chart shows a lot of data by design, but if you have to keep track not only of it but also of some meta data that is only used because of choice of technology, then the hell is imminent and it inflicts at least three times more mental pain.&lt;/p&gt;

&lt;p&gt;When encountering such a thing, usually a developer chooses one of two paths: change the technology in hopes the meta data there is much less, or keep working with the same technology, but introduce a library that deals with the bloat.&lt;/p&gt;

&lt;p&gt;And I looked into libraries that introduce a DOM on top of a Canvas element. But because I’m so stubborn, I decided I’ll keep on going without one. If I weren’t that stubborn, I could have used &lt;code&gt;fabric.js&lt;/code&gt; or &lt;code&gt;paper.js&lt;/code&gt;, that offer a much simpler, high-level API to Canvas. But let’s keep doing stuff the most painful way.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTML5 Canvas isn’t easy
&lt;/h2&gt;

&lt;p&gt;As a conclusion of this part I can say that using HTML5 Canvas isn’t easy. It takes time to learn the API and then it takes much more time to apply it in a way that paints what you want. It doesn’t support simple things like clicking on an element that you have drawn out of the box and I still haven’t found a way to test it.&lt;/p&gt;

&lt;p&gt;This said, it does provide enough, to create insanely complex things like games and animation. So I think it’s worth playing around with it, learning how to use it. Therefore I’ll continue this project and create at least an adequate line chart, that I can use for other projects. And I’ll do this by starting with rewriting what I have done up to now to get my first data set charted.&lt;/p&gt;

&lt;p&gt;But I think I need a strategy first.&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>html</category>
      <category>canvas</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
