{ "version": "https://jsonfeed.org/version/1", "title": "vixalien.io", "home_page_url": "http://vixalien.ga/", "feed_url": "https://vixalien.ga/feed/feed.json", "description": "Feed for my personal website!", "icon": "http://vixalien.ga/favicon/maskable.png", "author": { "name": "Angelo Verlain", "url": "https://vixalien.ga" }, "items": [ { "id": "let-it-snow", "content_html": "Let it snow! - vixalien
Sun Jan 31 2021

Let it snow!

Building an optimized snowing weather with the Web Animations API and Promises.



\n

Easter egg: Run this page with #snow at the end

\n
\n

πŸŒ¨β›„ Do you like snow? Does it snow in your region? Are we in December yet?

\n

We are going to create virtual snow using the chilly Web Animations API.

\n

A snowflake!

\n

First and foremost, let's create a snowflake! Our snowflake will be loaded as an .svg file provided by the beautiful Ionicons.

\n

Loading the snowflake

\n

You can store it as a local file then load it as SVG, or use it from Ionicon's library, but we will be storing it as a string.

\n
let svg_str = `<!-- snowflake svg text here -->`;\n
\n

Parsing the string into a DOM element

\n

Then we'll use DOMParser to parse the string into an actual DOM element.

\n
let snow = new DOMParser().parseFromString(svg_str, \"text/xml\").children[0];\n
\n
\n

Note: Because parseFromString returns a #document, we used .children[0] to get the <svg> element instead. (<svg> is equivalent to <html>.)

\n
\n

Setting the snowflake to float

\n

Our snowflake is fixed (it doesn't scroll like other elements) and initially, it is placed just above the screen.

\n
snow.style.position = \"fixed\";\nsnow.style.top = \"-24px\";\n
\n

Creating a new snowflake

\n

Because our page will have many snowflakes, we'll clone the snowflake we just created.

\n
let newSnow = () => {\n    let clonedSnow = snow.cloneNode(true);\n    // we pass true to clone the node deeply (that is, with all it's children).\n};\n
\n
\n

Note: from now on, our code will be in the newSnow function.

\n
\n

Next, we'll generate a random left position for that snowflake

\n
let left = Math.floor(document.body.offsetWidth * Math.random());\n// we use Math.floor to ensure left is an integer\nclonedSnow.style.left = left + \"px\";\n
\n

Then we'll just add it to the DOM

\n
document.body.append(clonedSnow);\n
\n

Animating the snowflake

\n

Here we'll just use Web Animations API to animate an element. To use the API, we run element.animate(keyframes, options). You can read more in the MDN Page.

\n

To make real snow effect, we will also generate a random speed (think the animation's duration)

\n
let time = Math.max(10 * Math.random(), 5) * 1000;\n// Math.max choose the largest argument it was given. By using it here, we restrict time to be larger than 5.\n
\n

We will animate the snow to change it's top CSS property gradually. At the end, the element will be placed just below the viewport, where you can't see it.

\n
let anim = clonedSnow.animate(\n    {\n        top: window.innerHeight + 24 + \"px\",\n    },\n    { duration: time, fill: \"forwards\" }\n);\n
\n

One last thing, we'll do Garbage Collection. When the animation ends, delete that snowflake as it is no longer useful.

\n
// garbage collection\nanim.onfinish = el => el.target.effect.target.remove()\n
\n

Now go ahead, in your console, run newSnow(). You'll see a snowflake falling slowly.

\n

Snowing!!!

\n

So far, we can only create snowflakes on demand by running newSnow() everytime we need it. What about we create a loop that create as many snowflakes as possible?

\n

The problem with native JS loops

\n

If you use for loops or while or whatever, it won't work. Why? It will create many snowflakes at a time. Your browser will be filled with snowflakes and unless you are on a supercomputer, your browser will crash, badly. This creates a need for a custom loop!

\n

Looping asynchronously

\n

Async Iterate

\n

Here's an implementation of an async loop.

\n
let asyncIterate = async (start, iterations, fn) => {\n    // initialize the iterator\n    let i = start;\n    let call = res => fn(res)\n        // waits for the function to resolves before calling the next iteration\n        .then(async result => {\n            if (i >= iterations) return result;\n            i++\n            return await call(i)\n        });\n    return await call(i);\n}\n
\n

It accepts 3 parameters. start is what the iterator is initialized as. iterations is pretty self-explanatory. it is the number of times the function will run. then fn is the function to execute.

\n

It is important to remember that this is an async loop. That means, it will run the function, then waits that it resolves. then execute the next iteration.

\n

wait

\n

Next is the wait function. This is a wrapper around setTimeout. It waits some time (in milliseconds), then execute a function. (It is available on the npm registry as async-wait-then).

\n
wait = time => new Promise(res => setTimeout(res, time))\n
\n

Here is a simple example using wait.

\n
wait(1000)\n    .then(() => console.log('This will be logged after one second!'));\n
\n

Using wait and asyncIterate to snow

\n

By combining wait and asyncIterate, we get a powerful function set that uses the Promises API.

\n

So, to create realistic snow (and prevent browser crashes) we'll have to wait before we create a snow element

\n
asyncIterate(0, 10, async () => {\n    await wait(1000)\n    newSnow()\n})\n
\n

This will make it rain 10 snowflakes, but with an interval of 1 seconds between each snowflake

\n

To make it look more realistic (and add some suspense), we will wait for a random amount of time instead of the static 1 second.

\n
asyncIterate(0, 10, async () => {\n    await wait(Math.max(3 * Math.random(), 1) * 300)\n    newSnow()\n})\n
\n

But then, this will only create 10 snowflakes. Let's make it rain forever.

\n
asyncIterate(0, Infinity, async () => {\n    await wait(Math.max(3 * Math.random(), 1) * 300)\n    newSnow()\n})\n
\n

The full code, complete with some optimizations is posted as Github Gist

\n
", "url": "https://vixalien.ga/post/let-it-snow", "title": "Let it snow!", "summary": "Building an optimized snowing weather with the Web Animations API and Promises.", "date_modified": "2021-01-31T15:49:22.776Z", "author": { "name": "Angelo Verlain", "url": "https://vixalien.ga" } }, { "id": "explosiv", "content_html": "How Explosiv Works - vixalien
Thu Jan 28 2021

How Explosiv Works

The most lightweight, yet fully featured static-site generator you'll see.



Explosiv npm β†— Github β†— is a static site generator for JSX content.

\n
\n

This article is about how Explosiv works, if you want to want to learn how to use Explosiv go to Explosiv's Github Page instead.

\n
\n

Why Explosiv was made.

\n

While I was creating this blog, I thought.

\n

About all the front-end options I had. Because, I was not going to write static HTML for a fully featured site! While I had already met stylus for all my CSS needs, I was still looking for an option to write my markup seamlessly.

\n

React

\n

TBH, I love React. It's syntax, it's community, it's everything really. Yet, React also put so much overhead on your site, like Build times, Babel, Webpack, hydrating or rendering etc. After some digging, I found out the foundation behind React was JSX, dubbed as XHTML within Javascript.

\n
// JSX syntax is coool!\nlet Site = (data) => {\n    return <div>Hello {data.name}!</div>\n}\n
\n

Well because JSX is tightly coupled with React, I kinda thought it would not work on it's own, but yet kartiknair made Dhow, which proved me otherwise.

\n

Dhow

\n

Dhow, is a static site generator, that uses JSX to render static HTML at build time, ready to be served as is. It is quick, very fast and still uses JSX, so migrating my app from React was a breeeeze. Until I encountered the severe limitations of Dhow.

\n

Dhow was very young. That means It just implemented JSX, nothing else. Many features were lacking. While creating this site, I cloned Dhow. I found myself adding many features as I wanted. I saw it was incredible. I decided to push it to My Github as Explosiv

\n

Explosiv

\n

Explosiv, being a clone of Dhow, inherits all it's current features. Here is a simple example.

\n

First, add explosiv to your site's dependencies.

\n
npm i explosiv\n
\n

And install explosiv globally, so that you can use the CLI wherever you are...

\n
npm i explosiv -g\n
\n

Or, to keep up with modern standards, although I personally like the first syntax more, use npx to always use the latest version of explosiv

\n
npx explosiv\n
\n

To make an Explosiv site, just create a folder and generate a pages/ directory. Add a simple index.js file to get started.

\n
// pages/index.js\nimport Explosiv from 'explosiv'\n\nexport default () => (\n    <main>\n        <h1>Hello there!</h1>\n        <p>\n            This is a super simple example of generating static files using Explosiv.\n            You can learn more at{' '}\n            <a href=\"https://github.com/vixalien/explosiv\">here</a>\n        </p>\n    </main>\n)\n
\n

To build, and serve, the site use:

\n
explosiv build\nexplosiv serve\n
\n

Et voìla! A static site was generated in your /out directory. Magic right!

\n

How it works

\n

You can learn how JSX works by this article from the React team.

\n

You can read a very nice article by kartiknair, the creator of Dhow about converting JSX into HTML without React.

\n

TL;DR: We use a pragma function that generate real DOM elements using a minimal DOM implementation, min-document.

\n
// A general overview of how it works.\n// !! Not real code\nconst document = require('min-document');\n\nconst createElement = (tag, props, ...children) => {\n    const element = document.createElement(tag)\n\n    children.forEach((child) => {\n        element.appendChild(child)\n    })\n\n    return element\n}\n
\n

We transpile Javascript using ESBuild, a verrry fast, yet fully featured transpiler. We transpile the code in the pages directory from JSX into pure, native Javascript, while replacing all instances of JSX with our pragma function.

\n

The transpiled file will look like this

\n
// transpiled/index.js\nlet { createElement } = require('explosiv')\n\nexport default () => (\n    createElement('main', null, \n        createElement('h1', null, 'Hello there!'),\n        createElement('p', null, \n            'This is a super simple example of generating static files using Explosiv.',\n            'You can learn more', ' ',\n            createElement('a', {\n                href: \"https://github.com/vixalien/explosiv\"\n            }, 'here'\n        ),\n    )\n)\n
\n

At the end we render our DOM into static HTML by using document.toString() and piping the output into the relevant output directory.

\n

Impovements over Dhow

\n

Explosiv, is a personal project. It is not a competitor, or even an alternative to Dhow, yet all current improvements are listed for anyone interested. Many of these can also be implemented in Dhow if worth it.

\n
    \n
  • Provide an explosiv serve command that serve a static directory on a specified port (defaults to 3000).
  • \n
  • Head elements are added on top of document.head instead of the bottom (allowing overriding existing tags)
  • \n
  • Rewritten for build code to be independent and ease debugging
  • \n
  • Does not use polka but the more minimal connect.
  • \n
  • Use middleware deemed as useful like morgan which log all requests and compression which compress resources on HTTP.
  • \n
  • Fixed bugs on edge cases like rendering <> (aka Fragment tags) as root elements and rendering empty children.
  • \n
  • Added support for className HTML attribute.
  • \n
  • Fixed bug where self-closing (like <img src="/path/to.img">) elements doesn't render correctly.
  • \n
  • Use tabs instead of 4 spaces lol!
  • \n
  • And other many but subtle changes.
  • \n
\n
", "url": "https://vixalien.ga/post/explosiv", "title": "How Explosiv Works", "summary": "The most lightweight, yet fully featured static-site generator you'll see.", "date_modified": "2021-01-28T17:23:43.290Z", "author": { "name": "Angelo Verlain", "url": "https://vixalien.ga" } } ] }