Categories
Uncategorized

Exploring Canvas

In its simplest form <canvas> can be used to draw graphics via scripting (usually JavaScript). This can, for instance, be used to draw graphs, combine photos, or create simple (and not so simple) animations.

This post will only discuss the 2D drawing canvas mode. You can also use the canvas element to host 3D WebGL models, but that’s a whole other set of articles, so we’ll skip it for now.

Getting started

In our HTML document, we need to add a canvas element where we will draw our content. The id attribute is the name that we’ll use in the script to reference the canvas element.

Height and width are not required but it’s always better to write them out

<canvas id="tutorial" width="600" height="600">
  <p>Your browser doesn't support Canvas</p>
</canvas>

We also need a script that will generate whatever we want to do on the canvas. In this example, we’ll generate a square and an arrow. How we generate them is not as important, we’ll discuss it later.

// Procedural Canvas generation
const canvas =
  document.getElementById('tutorial');
if (canvas.getContext) {
  const ctx = canvas.getContext('2d');

// This draws a rectangle
  ctx.strokeRect(100, 100, 125, 125);

  ctx.beginPath();
  ctx.moveTo(200, 50);
  ctx.lineTo(100, 75);
  ctx.lineTo(100, 25);
  ctx.fill();
}

Understanding the Canvas grid

Graphic showing the canvas coordinate system
Canvas coordinates space. Taken from MDN

To better understand the material that we’ll cover next, we need to talk about the canvas coordinate space.

Normally 1 unit in the grid corresponds to 1 pixel on the canvas. The origin of this grid is positioned in the top left corner at coordinate (0, 0) with all elements placed relative to this origin.

So the position of the top left corner of the blue square becomes x pixels from the left and y pixels from the top, at coordinate (x,y).

Canvas provides tools to translate the origin to a different position, rotate the grid and even scale it, but for now, we’ll stick to the default.

Drawing primitives

Canvas supports two primitives for drawing: rectangle and path. All other forms are derived from these two primitives.

Rectangles

Rectangles draw either rectangles or squares,

fillRect(x, y, width, height)
Draws a filled rectangle.
strokeRect(x, y, width, height)
Draws a rectangular outline.
clearRect(x, y, width, height)
Clears the specified rectangular area, making it fully transparent.

Paths

Now let’s look at paths. A path is a list of points, connected by segments of lines that can be of different shapes, curved or not, of different width and of a different color. A path, or even a subpath, can be closed. To make shapes using paths takes some extra steps:

  • First, you create the path
  • Then you use drawing commands to draw into the path
  • Once the path has been created, you can stroke or fill the path to render it.

Here are the functions used to perform these steps:

beginPath()
Creates a new path. Once created, future drawing commands are directed into the path and used to build the path up.
closePath()
Adds a straight line to the path, going to the start of the current sub-path.
stroke()
Draws the shape by stroking its outline.
fill()
Draws a solid shape by filling the path’s content area.

The first step to create a path is to call the beginPath(). Internally, paths are stored as a list of sub-paths (lines, arcs, etc) which together form a shape. Every time this method is called, the list is reset and we can start drawing new shapes.

The second step is calling the methods that actually specify the paths to be drawn. We’ll see these shortly.

The third, and an optional step, is to call closePath(). This method tries to close the shape by drawing a straight line from the current point to the start. If the shape has already been closed or there’s only one point in the list, this function does nothing.

Notes

When the current path is empty, such as immediately after calling beginPath(), or on a newly created canvas, the first path construction command is always treated as a moveTo(), regardless of what it actually is. For that reason, you will almost always want to specifically set your starting position after resetting a path.
Note: When you call fill(), any open shapes are closed automatically, so you don’t have to call closePath(). This is not the case when you call stroke().

Styles and colors

fillStyle = color
Sets the style used when filling shapes
fillStroke = color
Sets the style for shapes’ outlines
Note: When you set the strokeStyle and/or fillStyle property, the new value becomes the default for all shapes being drawn from then on. If you want to use a different color, you must reassign fillStyle or strokeStyle.

You can also control transparency either globally, using globalAlpha which takes a value between 0 and 1 and will affect all shapes drawn after it’s set up, or on per-element basis using strokeStyle and fillStyle with RGBA or another color format that supports transparency.

// Assings default transparency value
ctx.globalAlpha = 0.2;

// Assigning transparent colors
// to stroke and fill style

ctx.strokeStyle = 'rgba(255, 0, 0, 0.5)';
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)';

Images

Importing images into a canvas is a two-step process:

  1. Get a reference to a supported image type. It is also possible to use images by providing a URL.
  2. Draw the image on the canvas using the drawImage() function.

Supported image types

HTMLImageElement
<img> elements or images made using the Image() constructor, as well as any element
HTMLImageElement
These are images embedded using the <image> SVG element
HTMLVideoElement
Using an HTML <video> element as your image source grabs the current frame from the video and uses it as an image
HTMLCanvasElement
You can use another <canvas> element as your image source

Using an existing image in an external URL, we can use the following HTML

<canvas id='demo'>
  <h2>Your browser doesn't support Canvas</h2>
</canvas>

And the following JavaScript to insert an image into the canvas element.

// Capture the canvas element
const canvas = document.getElementById('demo');
// Sets up a 2d context for the canvas
const ctx = canvas.getContext('2d');

// Image constrouctor and src assignment
const img = new Image();
img.src =
'https://s3-us-west-2.amazonaws.com/0168.JPG';

// Draw the image at the given coordinates
ctx.drawImage(img, 0,0);

We can then do further drawing on top of the image we just added using the primitives discussed earlier.

Idea

Using an image in a canvas element lends itself pretty well to having an outline and then draw on top of the outlines.

Where to next?

There are many other things you can do with canvas. Some ideas are:

  • Image blending and transformations
  • Mouse and pointer interaction
  • Animations
  • Image compositing and clipping

Canvass gives you a huge playground to play in. Play responsibly 🙂

Links, tutorials and resources

Categories
Uncategorized

Figures and Counters

Playing with figures and counters can produce some very interesting bits of presentation for our content.

The post will talk about the figure and figcaption elements, what they do and how to use them.

We will then discuss CSS counters, what they do and how they work when inserted into HTML elements.

Finally, we’ll look at what to do with figures and counters together. I hope you can find your creative uses.

Figure and figcaption

The HTML figure represents self-contained content with an optional caption represented by the figcaption child element. The figure, its caption, and its contents are referenced as a single unit.

I’ve always thought of figures as images with captions but the HTML spec says something different about the element:

The figure element represents some flow content, optionally with a caption, that is self-contained (like a complete sentence) and is typically referenced as a single unit from the main flow of the document.

“Self-contained” in this context does not necessarily mean independent. For example, each sentence in a paragraph is self-contained; an image that is part of a sentence would be inappropriate for figure, but an entire sentence made of images would be fitting.

The element can thus be used to annotate illustrations, diagrams, photos, code listings, etc.

From: https://html.spec.whatwg.org/#the-figure-element

One thing that the specification cautions authors is how to reference figures (any kind) from within the document:

When a figure is referred to from the main content of the document by identifying it by its caption (e.g., by figure number), it enables such content to be easily moved away from that primary content, e.g., to the side of the page, to dedicated pages, or to an appendix, without affecting the flow of the document.

If a figure element is referenced by its relative position, e.g., “in the photograph above” or “as the next figure shows”, then moving the figure would disrupt the page’s meaning. Authors are encouraged to consider using labels to refer to figures, rather than using such relative references so that the page can easily be restyled without affecting the page’s meaning.

From https://html.spec.whatwg.org/#the-figure-element

So, according to the HTML specification, we can do something like this:

<figure>
  <ul>
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
  </ul>
  <figcaption>List 1: Example list</figcaption>
</figure>

This example has a few drawbacks. The main one is that it hardcodes the number of the item in question, in this case, the list number. This means that if we insert a list before the one we labeled number one, we’ll have to manually change the numbers. This is error-prone and brittle if we have too many items of one kind in the document.

We can leave it as Example List but that’s confusing, particularly if you have too many similar items. It may also make it difficult to reference individual items as suggested in the specification.

CSS Counters

CSS Counters address the need for a means to automate the numbering of figure items. The idea is that we use counters and insert them, along with accompanying text, in the place we specify.

The process is as follows:

  1. We initialize the counters by using counter-reset with the name of the counters we want to reset
    1. We can repeat this for multiple counters
    2. The counter-reset function can be placed anywhere in the stylesheet. In this example, we placed the initialization in the root element selector
  2. Wherever we want to increase the value of the counter we use counter-increment
    1. We can repeat this for multiple counters
    2. The counter-increment function can be placed anywhere in the stylesheet. In this example, we placed the increment in the section h2::before selector
  3. Insert the content you want to display using a combination of text and the counter(counter-name) for the counter that you want to use inside a content property

The CSS code example below illustrates how to use counters to automatically set up a counter for chapter numbers and attach these counters to the first h2 element inside a section.

body {
  counter-reset: chapter;
}

section h2::before {
  counter-increment: chapter;
  content: "Chapter " counter(chapter) ": ";
}

The HTML shows one possible way to write HTML that uses the CSS in the previous block to auto number the sections we use as chapter separators.

<h2>Book Title</h2>

<section>
  <h2>Example Chapter</h2>

  <p>Some text for the chapter.</p>
</section>

You can see a working example in this code pen

See Automatic Numbering With Counters in the CSS Lists Module Level 3 specification for more information and additional counter functionality.

Experimenting

The following are some examples of what we can do with counters and how to best leverage counters and figures to make our content more appealing and easier to read.

Multiple counters

The first experiment is how to handle multiple counters in the same document. The example CSS code below works with four different counters: chapters, figures, tables and lists with each of these counters using different elements too trigger counter increment and displaying the content.

I picked these particular types of counters to illustrate different ways to use them.

The initialization works the same as the single-counter example. counter-reset can take more than one value separated by spaces.

:root {
  /* initializing counters */
  counter-reset: chapter figure tables lists;
}

The chapter counter works the same as our previous example.

section {
  counter-increment: chapter;
}
section h2::before {
  content: "Chapter " counter(chapter) ": ";
}

The figure counter works with our default figure setup without adding any additional information.

figure {
  counter-increment: figure;
}
figure figcaption::before {
  content: "Figure " counter(figure) ": ";
}

The table counter works with the HTML table elements and uses the caption child to display the counter in a similar way to what the default figure counter does.

table {
  counter-increment: tables;
}
table caption {
  padding: 10px;
  caption-side: bottom;
}
table caption::before {
  content: "Table " counter(tables) ": ";
}

The lists counter uses a modified version of the figure counter to account for it being a special type of figure.

figure.lists {
  counter-increment: lists;
}
figure.lists figcaption::before {
  content: "List " counter(lists) ": ";
}

Counters reset at the chapter level

The previous example initialized/rest the counters in the root element of the stylesheet. There may be times when we want to reset items further down the style tree.

This example will reset the figure counter on every section, restarting the figure numbering on every section, regardless of how many figures we have on each chapter.

:root {
  counter-reset: chapter;
}

section {
  counter-reset: figure;
  counter-increment: chapter;
}
section h2::before {
  content: "Chapter " counter(chapter) ": ";
}

figure {
  counter-increment: figure;
}
figure figcaption::before {
  content: "Figure " counter(figure) ": ";
}

Using more than one counter

So far all our examples have used a single counter to show the positioning of an item on the page.

There are times where we want to show more specific information about the image, for example, this is the first image of chapter two or the third table in chapter 5.

To do this we have to use more than one counter when writing the counter content.

We’ll take the same code as the previous example and change the content inside figcaption::before to add the chapter counter and a string to separate the chapter counter from the figure counter. The rest of the code is the same.

:root {
  counter-reset: chapter;
}

section {
  counter-reset: figure;
  counter-increment: chapter;
}
section h2::before {
  content: "Chapter " counter(chapter) ": ";
}

figure {
  counter-increment: figure;
}
figure figcaption::before {
  content: "Figure " counter(chapter) '-' counter(figure) ": ";
}

Using counters is not exclusive to figures. It’s just a matter of how creative you are and what you’re trying to accomplish.

Categories
Uncategorized

Testing javascript in the browser without the browser

There are times that we want to test snippets of Javascript code in different browsers to make sure that it works as intended in all our target browsers. I’ve always fired the browsers and pasted the code into DevTools or Web Inspector to check if the results are the same.

Now there is a pair of applications that will automate this for you: jsvu and eshost.

To install these packages run the following command

npm i -g jsvu eshost

JSVU (JavaScript Version Updater) manages the installation and updates for different Javascript engines avoiding the compilation process. Out of the box, it supports the following engines and OS combinations.

Vendor JavaScript engine Binary name mac64 win32 win64 linux32 linux64
Microsoft Chakra chakra or ch
Facebook Hermes hermes & hermes-repl
WebKit/Apple JavaScriptCore javascriptcore or jsc * *
Fabrice Bellard QuickJS quickjs
Mozilla SpiderMonkey spidermonkey or sm
Google V8 v8
Google V8 debug v8-debug
Moddable-OpenSource XS xs (32) (32)

When you first run it it will prompt you to select the JS engines to install. After initial install running the jsvu commannd will update the engines as appropriate.

Once we have the engines that we want to work with we can configure ESHost to run the same command with these multiple js engines. In this example, we’re adding the major browser’s JS engines to work with ESHost.

# Chakra is old Edge's JS engine
eshost --add 'Chakra' ch ~/.jsvu/chakra
# JSCore is Safari's JS engine
eshost --add 'JavaScriptCore' jsc ~/.jsvu/javascriptcore
# Spider Monkey is Firefox JS engine
eshost --add 'SpiderMonkey' jsshell ~/.jsvu/spidermonkey
# V8 is Chrome's JS engine
eshost --add 'V8' d8 ~/.jsvu/v8

Once we have the engines set up, ESHost is ready to go.

We have multiple ways to run scripts in ESHost. We can run a short script in all configured browsers with the -e flag like this:

eshost -e "12*12"

You can also run complete scripts in the configured browsers with the following command:

eshost my-script.js

The only flags I will refer to are -m and -s.

The -m flag will treat the script as a module with the corresponding differences in syntax.

The -s flag will consolidate results when different engines produce the same result. For example, the following command:

eshost -se "console.log(112*12)"

will produce the following result where only JavaScriptCore produces a different result. This will help in researching browser differences that need workarounds

eshost -se "console.log(122*12)"
#### Chakra, SpiderMonkey, V8
1464
undefined

#### JavaScriptCore

TypeError: undefined is not an object (evaluating 'console.log')

For more information use the following command:

eshost --help
Categories
Uncategorized

JS Goodies: Nullish Coalescing and Optional Chaining

One of the things I like about the annual release schedule for Javascript is that the learning curve for new versions is much smaller than ES2015/ES6 and previous versions.

Two of the features that I’m most interested in are optional Chaining and Nullish Coalescing.

Optional Chaining

Optional Chaining, allows yout to check the existence of a property or short circuit and return undefined if any property in the chain that doesn’t exist.

In the example below, we define a zoo object with animal types and their names.

const zoo = {
  name: 'Alice',
  bird: {
    name: 'Hawkeye',
  },
  dog: {
    name: 'Fluffy'
  },
  cat: {
    name: 'Dinah'
  }
};

We can then query for properties at any point down the chain. The dog object doesn’t have a breed property so, if we use zoo.dog?.breed; to query for the breed, it will return undefined because the property doesn’t exist rather than an error as we’d normally expect.

const dogBreed = zoo.dog?.breed;
console.log(dogBreed);
// Outputs undefined

const dogName = zoo.dog?.name;
console.log(dogName);
// Outputs Fluffy

const birdType = zoo.bird?.type;
console.log(birdType);
// Outputs undefined

This makes it easier to query long chains of parent/child elements and avoid fatal errors in our applications.

Nullish coalescing operator

Nullish coalescing operator addresses an interesting shortcoming with the logical or operator || when it comes to setting up default values for an application.

const mySetting = '' || 'setting 1';

If the left-hand value can be converted to true then that’s what the application will use, otherwise the value on the right-hand side will be used.

These expression evaluate to false in Javascript:

  • null
  • NaN
  • 0
  • empty string (“” or ” or “)
  • undefined

But there’s a problem with this method of setting values for preferences. There are times when an empty or otherwise false value (other than null or undefined) is acceptable for the setting that we want to work with.

That’s where the nullish coalescing operator comes into play. It will produce the right side value if the left side value is null and the left value otherwise.

In the first example, the value of foo will be default string because the left side value is null. In this case the behavior is the same as the logical or operator.

const foo = null ?? 'default string';
console.log(foo);
// expected output: default string

const foo2 = null || 'default string';
console.log(foo2);
// expected output: default string

In the second example the value of baz will be 0. The first value is not null or undefined so the constant takes the left side value.

Compare the result with the baz2 constant where, using the logical or operator, we get the value of 42. 0 is a falsy value so we use the right side value as the value of the constat.

const baz = 0 ?? 42;
console.log(baz);
// expected output: 0

const baz2 = 0 || 42;
console.log(baz2);
// expected output: 42

The differences are subtle and can lead to annoying bugs when they don’t produce the value you expect. It’s up to you which one you use as long as you’re ok with the results you get.

Categories
Uncategorized

pre-commit hooks (an update)

This is a different take on hooks from Pre Commit Hooks: Combating Laziness, written two years ago

There are times when it would be awesome if we could force ourselves (or our development team) to perform some actions before committing code to the project’s repository.

Git has a set of tools called hooks that can help with this enforcement proccess.

The idea behind hooks is that they will run at set times during the request lifecycle and run the code you specify in the hook file. You can use hooks to run linting and accessibility checks before you commit code and reject the commit if any of the check fails.

In this example we want to run gulp axe and gulp eslint before each and every commit and fail the commit if there are errors returned from either command.

We’ll leverage the pre-commit hook to accomplish this. This hook is run first, before you type a commit message. It’s used to inspect the snapshot that’s about to be committed, complete tasks before the commit happens. Exiting non-zero from this hook aborts the commit.

Move this code into the hooks/pre-commit file inside your .git directoory.

#!/bin/sh

# Stash non-commited changes
git stash -q --keep-index
# if node_modules directory doesn't exist 
# then run npm install
if [ ! -d "/node_modules/" ]
then
    echo "Directory node_modules not created"
    npm install
fi
# Run gulp axe to check accessibility
gulp axe
# Run gulp eslint to check for syntax
gulp eslint
# Assigns the exit status to a variable
RESULT=$?
# Pop the changes back to current directory
git stash pop -q
[ $RESULT -ne 0 ] && exit 1
# Otherwise exit with error code 0
exit 0

This example makes the following assumptions:

  • You’ve added node_modules to .gitignore
  • You’ve added axe and eslint as gulp tasks

The downside of hooks is that they are not copied when you clone a repository. If your intent with these scripts is to enforce a policy, you’ll want to do that on the server side. The Git Book provides examples of server side scripts to enforce Git Policies.