Ever wondered how do those JavaScript transpilers actually work? Let’s take the most popular one: Babel. The transpiler is a big deal, especially now with recently introduced support for transformation plugins. This means everyone can create their own transformations...
Read my article about JavaScript AST. You'll learn how to understand abstract syntax tree, about tools to transform your code, like Babel does, and I'll demonstrate how to make a very basic transformation for constant folding.
I've been working on a bunch of small projects, mainly React-related, with purpose of learning the framework. And so I want to share a word about recent updates.
github-issues (DEMO) is a small React app to view repo info and issues, built with Flux, Webpack and Babel. I believe the source code can help anyone to understand how React and Flux works together. Make sure to check README for some insights.
react-a11y-video is accessible HTML5 video player React component. This one is flavored with ARIA and semantic markup, both plays nicely with assistive technologies.
react-webrtc (DEMO) is an attempt to make WebRTC integration easier into existing React applications. Component provides a simple API for communication, using PeerJS lib behind the scenes.
impact-node is a command line interface written in Node.js for developing HTML5 games with Impact game engine. You can bootstrap a new project, run development server and build production-ready bundle.
f-react-kit is starter kit for developing React apps with a power of functional programming style and data immutability. The main concept comes from Om (ClojureScript interface to React) and it works so great, I'm using it for all new projects and so decided to create a starting point project.
anybar-webpack if you are Webpack user, you'll definitely love it. I've found myself constantly looking at the console for a build status, if it fails I need fix something. But toggling between console and editor gets annoying quickly. I've found a great simple OS X app called AnyBar, which does display an icon ot OS menubar and changes it based on command you send via UDP port. So I did a small Webpack plugin with nice crisp status icons.
Recently I've joined a small game project as the only front-end developer, with responsibilities of actual client-side game implementation. So I started with Phaser, because it's powerful and free. Phaser is built on top of Pixi.js, which is 2D WebGL renderer, so it's really fast, and it has a straightforward API. A good process requires a good code base organization: proper directories structure and modularity.
RequireJS is a common solution. Here's my project structure:
bower_components
modules
utils
extensions
factories
states
units
PhaserGame.js
runtime.js
bower_components directory includes Bower front-end deps, at least Phaser. modules is where all the stuff is and runtime.js is the main file with RequireJS config. states includes game states, units includes game characters, objects, etc., factories produces units, such as a bunch of trees; extensions are game objects which extends Phaser API, and utils is for game utils. Finally PhaserGame.js loads all the modules and returns a game object.
runtime.js defines config and initializes game by loading all the states and then running the first one. There are three types of states: boot, preload and game state. Boot state includes initial settings, like number of pointers that can be used to control a game. Preload state loads all the assets of the game. And game states runs different parts of game: main menu, stages. Runtime starts Boot state, which starts Preload and so on. Here's how runtime.js is looks like:
requirejs.config({ paths: { Phaser: 'bower_components/phaser/build/phaser' } }); require([ 'modules/PhaserGame', 'modules/states/Boot', 'modules/states/Preload', 'modules/states/StageOne' ], function (PhaserGame, BootState, PreloadState, StageOneState) { var game = new PhaserGame(640, 480); game.state.add('Boot', BootState); game.state.add('Preload', PreloadState); game.state.add('StageOne', StageOneState); game.state.start('Boot'); });
'PhaserGame.js'
define([ 'Phaser', 'modules/extensions/MyExtension', 'modules/units/MyUnit', 'modules/factories/MyFactory' ], function (Phaser) { var PhaserGame = function (w, h) { return new Phaser.Game(w, h, Phaser.AUTO); }; return PhaserGame; });
Once PhaserGame was instantiated and the game's object reference is assigned to PhaserGame variable, we can require this module whenever we need a use of it. Notice, that modules being loaded but not used, because I'm assigning them, within itself, to Phaser namespace. For factories I have Phaser.factories namespace and Phaser.utils for utils.
Typical state module:
define([ 'Phaser' ], function (Phaser) { var BootState = function (game) {}; BootState.prototype = { constructor: BootState, preload: function() {}, create: function() {}, update: function() {} }; return BootState; });
Also extension module might be interesting to look at:
define([ 'Phaser' ], function (Phaser) { var Extension = function (arguments) { Phaser.Extends.apply(this, [arguments]); }; Extension.prototype = Object.create(Phaser.Extends.prototype); Extension.prototype.constructor = Extension; Extension.prototype.method = function() {}; Phaser.Extension = Extension; });
First call built-in Phaser object which we want to extend with the context of extension's constructor function, this will assign all properties of the target object to extension's instance (this). Then setup a prototype object of the extension from the target's prototype. Assign constructor and define custom interface.
Right now this way of organization works fine for me and I'm happy with it.
Polymer is doing great, since I last wrote about it, it got from pre-alpha to developer preview. On our project we decided to move gradually to components-based front-end. Because of its success, Polymer was the first option to try. We've developed and integrated a couple of components. It was great to work with Polymer, its API is pretty close to Web Components spec, so if you did native components, it's going to be easy to get started with Polymer. Unfortunately Polymer has poor support for IE9, which is still the case for us. We have managed to fix IE9 specific issues, but still there were conflicts with other third-party libs on which our front-end is based. Figuring out all the stuff would take a while for us and unfortunately we have no time for this. From this point we have moved to pure JS solution. I'm really sad about how big companies doesn't care much about the web.
I was looking at Polymer’s source and found out it tries to polyfill not only Shadow DOM, HTML Imports and other Web Components parts, but some ES6 stuff as well: WeakMaps, Object.observe and other great JS APIs. I was like: “Huh, do I really need all of it?” No, I don’t. What I need is support for Custom Elements, Shadow DOM and, probably, HTML Imports. It should be enough for building decent components. Also I wanted IE9 support and to be able to develop compatible components according to standards. This means no Polymer's wrapper for registering element and stuff like that, just plain JS code which runs great with or without polyfills, depends on the amount of implemented parts of Web Components spec in particular browser.
The major parts are Custom Elements and Shadow DOM. document.registerElement allows to define new HTML elements with its own, unique functionality.
/* Create a new object prototype from HTMLElement prototype */ var MyNameElement = Object.create(HTMLElement.prototype); /* Do something when element gets created */ MyNameElement.createdCallback = function() { this.textContent = this.getAttribute('is'); }; /* Register element within document */ document.registerElement('my-name', { prototype: MyNameElement });
Element has a set of so called lifecycle callbacks. For example createdCallback is being called when an instance of the element is created, there you should perform element initialization.
Shadow DOM is used to hide DOM representation of element's functionality. Take <video> element: it has some controls but it's still a single HTML element. Calling Element.createShadowRoot() on HTML element will create a sub-tree scope within the element, which represents extended document fragment interface. Taking a code snippet from previous example to show how it works:
There's already a great polyfill for document.registerElement which is intended to be a lightweight alternative to Polymer's polyfill, only 2KB minified and gzipped. And shadow root is just a document fragment, it's operable as a usual DOM node. The simplest possible emulation is to append stuff to custom element itself, so the polyfill is just a few lines of code or 110 bytes minified and gzipped. This of course doesn't cover Shadow DOM specific CSS selectors. Inspired by Polymer's way I did a basic CSS polyfill, which is again just 435 bytes minified and gzipped. There are some limitations as well. For example: you should put a comment with custom element's tag name in your CSS; and there are only two Shadow DOM CSS selectors which will be processed, they are :host and :host(). In fact, I think, they are the most common, maybe will add support for others later. Both selectors allows to target the host element, the custom element itself, but the second one brings specificity by matching the exact host with provided selector. So this code:
The next part of Web Components spec is Templates. This introduces <template> element to the DOM. Both HTML and JS can live inside the template, but it won't be processed by browser until you take it outside and put somewhere in the DOM. Combining with Custom Elements it takes us to more common process of creating components.
The last one is HTML Imports, which is literally import for the web. It's intended to simplify usage of web components in a way where all you need to do to enable a component is to include <link rel="import" href="path/to/component.html"> in your HTML. Browser will fetch and run the document. Document tree can be accessed import property of the link[rel="import"] element. The polyfill is 580 bytes minified and gzipped.
There's still one thing should be polyfilled, it is document.currentScript.ownerDocument property, which returns a document which is the owner of the <script> element whose code is currently being processed. It is necessary to use within import document when accessing its DOM, because JS code of the import document actually runs within master (parent) document scope. document.currentScript polyfill is 850 bytes minified and gzipped.
As a result the set of polyfills is only 3.5KB minified and gzipped. And it's ready for IE9+ and mobile.
When you need both stable and unstable Node.js releases or trying to check your application on multiple Node versions or when someone reports an issue caused using another Node version. In these and other cases or without any it's a good practice to have a tool for managing multiple versions of whatever.
Similar to RVM for Ruby, there's NVM for Node.js. Node Version Manager helps me a lot, at first I started using it because of a simple Node.js runtime installation process, now I'm using all of its benefits.
Running this command will fetch and execute shell script which will install NVM for you:
curl https://raw.githubusercontent.com/creationix/nvm/v0.8.0/install.sh | sh
Install Node.js version you need: nvm install 0.10.29
Single command front-end dev environment deployment with Vagrant
I'm currently working on a side project where everyone has different dev environments. Someone works on Win, someone on Mac OS, I prefer Ubuntu. Different OS means different environment deployment processes. Let's say you need Node.js, MongoDB, Grunt, Bower and Compass (Ruby). As a front-end dev I already know how to install all the stuff without pain on all platforms, but other guys may doesn't know about these components and still they need to install them all, just to be able to build and run project on local machine for further development. I don't want to waste my time for explaining how to install everything to each person.
I'm not Vagrant pro, but I still like it and trying to use it when it's appropriate. It's simple to get up and run. Then you need to install all the tools used in your project. For this particular part and for every project with Node, Grunt, Bower and Sass I've wrote a shell script boilerplate which can be expanded, so you can install specific stuff for your project.
For now it's only for Ubuntu, so contributing to Windows and Mac OS flow is welcoming. The whole process is separated into two scripts, you need to run only one: vagrant-getup. This will ask you for your project repo Git url, clone path on your host machine and actually clone the project. Also the script will try install both Vagrant and VirtualBox, add Ubuntu 12.04 32-bit box to Vagrant, init the box and sync cloned project directory to the box. After that, another install script will be executed within vm: install_front-end_tools. You'll get installed cURL, Git, NVM with Node.js v0.10.28, Grunt and Bower, RVM with Ruby 1.9.3, Sass and Compass. The final part is to reload the box, and run npm install && bower install from project directory to install all deps.
As you can see there's nothing too specific, all tools are widely used in every front-end project. Installation process takes 10-15 minutes and then you are ready for making web awesome.
There are still lots of things to do besides cross-platform installation. I think I need to add Vagrant port forwarding, so you can access your project running inside of the vm from your host OS. Also any contributions are welcoming!
If you ever did performance and optimization testing of your website or web application, you probably have used a service like WebPagetest, which allows to test things in real browsers, at real consumer connection speeds and capture resource loading waterfall charts etc. Among all these useful features, the whole loading process can be captured into a video to show how your application looks like while loading assets into user's browser.
There's an interesting topic about rendering performance were discussed, which is directly relates to loading perf. In short: your external CSS is blocking page rendering, huge amount of CSS being loaded on slow connection will probably make a blank screen for a while. That's why it's critical to make sure that everything runs fast and experiences acceptable on whatever connection user can be.
Since Grunt has already strongly entered my everyday workflow, I've made an attempt to move a part of a such service features to local dev environment.
The above GIF is an output of grunt-load-perf Grunt task, it represents an entire loading process from page initialization to fully loaded. Here most of the time the page is blank and only last two frames shows rendered layout. This Grunt task is not used to be a replacement of services which provides this functionality, but an addition for a constant testing of a base loading performance, while using such services testing can be done with real things.
The task can capture screenshots and batch them into GIF animation. Network emulation done by throttling connection streams, you can define values for downstream and upstream connections as well as setup latency. Also there's a list of predefined common internet connection types, like 3G, 4G, etc. There are more options to set viewport size, capturing frame rate, network setup and target url.
PhantomJS is responsible for loading target url and taking screenshots. If you have installed imagemagick you'll get a GIF. Connection throttling is done by node-stream-throttle and some code for proxying.
This task is a part of front-end DevOps repository where I'm storing all the stuff for efficient full-stack JavaScript development.
So I'm doing some slides for my upcoming kind of a talk about Node.js, here in Lviv, at the office. We had one already about Node's internal stuff and event loop on a really low level, like CPU caches and I/O. I'm going to talk about actual code, API, use cases and popular libs.
Besides just a bunch of words I wanted to show some code and the best way is a working demo application. Even better would be to have an app with all major Node's use cases presented. They are: fast prototyping, streaming data, JSON API development, real-time and single page applications. It seems for me like video sharing service app would be great for that.
There are AngularJS and Bootstrap on front-end, and Express, MongoDB, Passport and Socket.io on server-side. Every single part is relatively easy to start playing with, if you are ok with JavaScript and back-end stuff. Express is serving a single page where Angular is running the app, the main use of it is REST API. It's pretty simple and self-descriptive as it should be. Passport manages users auth using API token strategy. And all data is stored in MongoDB, which is much more great with Mongoose, it brings you a set of useful features like schemas and virtual methods.
I've been looking across the web for more info about real-time video encoding from Node perspective, but haven't found much. Before we dive into server-side code, there's a thing worth to know about Angular and XHR file uploading: content-type header should be multipart/form-data including boundary parameter which is presented in FormData object as well. This is handled by browser, I've seen some code for that, not sure if it works. So you can't just set a header, the best way is to let the browser to figure out the content of the data and so it can set appropriate header. This can be done by setting content-type to false in Angular, at least this is what I've seen people are doing on the web. As it turned out, it no longer works (Angular 1.2.10), use undefined instead.
Node.js streams are great, if you haven't used them before or just can't figure out how to use it, read an article about “Node.js Stream Playground” and play with streams online. In Node.js, streams is what you need when talking about real-time, it allows you to consume and process data on the fly, which means you don't need to wait until the end of the upload process. With streams you are working with chunks of incoming data. Node's streams can be both writable and readable as well as piped one into another, which is a UNIX way of doing things. Both stdin and stdout are streams too, so Node can pipe some data into a separate process and get it back.
For video encoding I'm using avconv, which is a new ffmpeg on Ubuntu and multiparty form data parser instead of Express built-in formidable, it kind of same, but simpler and I like its API. Btw, if you are going to use separate form parser, you should not use express.bodyParser(), just replace it with express.json() and express.urlencoded(), both are included into bodyParser along with formidable. Data parsing, encoding and writing to file system is simple as it sounds thanks to streams:
var multiparty = require('multiparty'), spawn = require('child_process').spawn, fs = require('fs'); var form = new multiparty.Form(), // Init form data parser args = ['-i', 'pipe:0', '-f', 'webm', 'pipe:1'], // Set args, define i/o streams avconv = spawn('avconv', args), // Spawn avconv process output = fs.createWriteStream('./output.webm'); // Write to file system form.on('part', function (part) { // Listen on parsed `part` if (part.filename) { // Only file has a 'filename' property part.pipe(avconv.stdin); // Write chunks of file into avconv standard input } }); avconv.stdout.pipe(output); // Write encoded chucks to the file system form.parse(req, function (err, fields) { // Run parsing process if (err) return console.log(err); });
That's it, it works, unless you are not on really high-end machine, which is able to process data faster than it can be uploaded. That's what I ran into when did some testing. Video encoding is pretty heavy task you know, and when incoming data can't be handled fast enough — all real-time things will not work at all. Both readable and writable streams in Node.js has a Buffer object which holds a chunk of incoming data and will pass it further on a pipeline when the next stream is ready to consume it (when its buffer becomes empty). So when you run this code on a low-end hardware, and especially on localhost, every single video file you pass will be fully uploaded as fast as avconv will done with encoding. This makes no sense. To make things run truly in real-time you need a really fast machine or to limit bandwidth, which is applicable for testing purpose only. Otherwise: upload first, then encode. Not real-time, but still doing its job.
The application I'm working on is available on GitHub, maybe you can contribute some optimizations?
JavaScript is great, math is awesome, if you know both — you have a power. Everything can be represented as a vector: direction, speed and any force. Thanks to Daniel Shiffman's “The Nature of Code” I learnt a lot about vectors and natural programming. I'm going to post a couple of articles based on my recently aquaried knowledge, where at the end I'll tell how to make something like this. This is a JS implementation of path-following algorithm, documented code is available on GitHub. Maybe you can try to figure out how it works by yourself.
On paper vector can be represented as an arrow pointing somewhere. It has magnitude (length) and direction. Vectors can be added, subtracted, multiplied, divided, scaled and lots of other stuff we can do with them to achieve interesting results. I'd like to use a lib for this, glMatrix can handle all the math, but it's still recommended to dive in how things happens when you doing particular operation.
Let's write down the scene setup and create an object constructor to represent a vehicle.
There are canvas size setup and rendering function with scene cleaner code. Everything in this function will be executed 60 times per second with requestAnimationFrame function.
The Vehicle object describes a simple object on the scene with a single force applied — velocity. Add current location to velocity and the object will move, the bigger the velocity value, the bigger object's speed will be. Also there's rendering function and the one to do not let the object go out of the scene for ever.
Here we find a vector pointing from current location to the mouse cursor, convert it to unit vector and scale by some scalar. The bigger the scalar, the faster the vehicle will turn in cursor direction. Then we add acceleration to velocity and limit velocity with the maximum speed value, this will avoid constant speed increasing, and finnaly update location with velocity. Play with max speed and acceleration scale values to see how it works.
One addition to glMatrix lib is the limit function.
vec2.limit = function (out, v, high) { var x = v[0], y = v[1]; var len = x*x + y*y; if (len > high*high && len > 0) { out[0] = x; out[1] = y; vec2.normalize(out, out); vec2.scale(out, out, high); } return out; };
You may notice how the circle wiggling around when reaches the mouse cursor, it's trying to go exact position, but can't. There should be implemeted arrival steering behavior. Will try to explain about it in the next article.
(9.09.2014) Update: I've updated the repo with latest Polymer release and pushed native implementation of the component to 'native' branch. Chrome is already fully compatible with Web Components standard.
I've been working recently on a forked pages2pdf client-side converter, which is actually did what it stands for, but I wanted to add an ability to preview output PDF. So I made my own PDF reader with PDF.js. It's already integrated into converter, so you can give it a try.
I thought it would be nice to try to implement the reader as a web component, since I've never tried Polymer before. Polymer is a polyfill lib with lots of great things inside. It allows to provide support for Web Components in unsupported browsers and use native APIs in compatible.
Polymer is in pre-alpha developer preview at the moment, but if everything is ok, you'll be able to see the reader below.
The component is available on GitHub. Clone the repo and run the following command to get a minified component:
npm install && bower install && grunt
This will install required deps and run build script. Make sure you have npm, bower and gunt installed.
After build process is done, you'll find the output under dist folder. It consists of sample PDF and index.html files, minified component HTML file with styles and JS code, PDF processing worker script and Polymer runtime platform.min.js script.
To use PDF Reader component, you need to include polymer platform.min.js script in your HTML, as well as import the component itself. Put this tag in head:
width and height attributes specifies component size and url provided in data-url src attribute should points to PDF document which will be loaded into the reader.
In some browsers, where Web Components spec is not implemented yet, Polymer renders components as a part of DOM, not Shadow DOM, and it kind of tries to create some CSS scope but it's not ideal, thus some global styles can affect component. To avoid it I've added class prefix for each element, but it still can be broken in some ways.
Anyway, Web Components is here, and we can already use them, where it makes sense of course.
How to forget about jQuery and start using native JavaScript APIs
JavaScript is here and it's ready for you, but probably you are not ready for it yet. Why not using jQuery? Because it's slow and your website doesn't really need extra weight.
I'm not going to argue about libs vs native. Sometimes it's really hard to live without all that magic stuff. But I'm going to argue about loading kilos of code just only for a one-character-selector-function aka $ or things like that.
Assuming that shorthands is not the case, everyone use jSomething because of it's support for IE, animation handling and the only selector function.
Native equivalents
Select elements
// jQuery var els = $('.el'); // Native var els = document.querySelectorAll('.el'); // Shorthand var $ = function (el) { return document.querySelectorAll(el); } var els = $('.el'); // Or use regex-based micro-selector lib // http://jsperf.com/micro-selector-libraries
Create elements
// jQuery var newEl = $('<div/>'); // Native var newEl = document.createElement('div');
// jQuery $.get('url', function (data) { }); $.post('url', {data: data}, function (data) { }); // Native // get var xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.onreadystatechange = function (data) { } xhr.send(); // post var xhr = new XMLHttpRequest() xhr.open('POST', url); xhr.onreadystatechange = function (data) { } xhr.send({data: data});
So this is just a few, you can find more native stuff using the console in your browser or read MDN's JS API reference or WPD's DOM docs.
You still can use libs, check here for some ultra-lightweight and find the one you need for particular task, but first make sure you can't achieve the goal without it, otherwise use native JS.
Well, it's something about two weeks or more I'm trying to figure out how to make things really nice with shaders. It's lots of math and low level stuff, but it's still fun to try and maybe even create something to cheer yourself.
GLSL Shaders is so cool, yeah. But I feel lack of info for beginners, so it's a bit hard to start doing things right in the moment.
In WebGL, which is basically OpenGL ES 2.0 for the web, we have Vertex and Fragment (pixel) shader. One transforms things, and the other one makes them to look nice. I personally haven't created any shader from scratch yet. It's always a good start to read the code that is already works. The gif above is actually a two diff demos I've found on the web and have played with. The first one is explosion demo and the other is heightmap texture demo. Both shaders are somewhat simple enough to understand what they are doing.
But first here's how a shader can be used with Three.js material. Usually shaders are included in the HTML like a script <script id="vertexShader" type="x-shader/x-vertex"></script>, I believe it can be loaded via XHR as well.
var material = new THREE.ShaderMaterial({ uniforms: { tDiffuse: {type: "t", value: THREE.ImageUtils.loadTexture('texture.jpg')}, time: { type: "f", value: 0 }, weight: {type: "f", value: 10} }, vertexShader: document.querySelector('#vertexShader').textContent, fragmentShader: document.querySelector('#fragmentShader').textContent });
uniforms are parameters which can be passed to shader program from the outside, so the shader can be configured and controlled from JS. In this example there are three uniforms: tDiffuse of type t which stands for texture, time and weight both of type f which is for float, there's a list of all available uniforms types. Usually the time uniform is used to animate shader. It can be done from render function.
var time = clock.getElapsedTime(); material.uniforms['time'].value = time;
Now lets try to get into OpenGL stuff, I may make mistakes somewhere, but the whole concept is straight forward for me.
This is a vertex shader from the heightmap texture demo. What it does is using image data of the heightmap to transform each vertex along its normal. Texture lookup function texture2D accessing the image data, then the value of the r property (red channel of rgba) is used to transform the vertex. The heightmap is a grayscale image, so it's no matter which color channel to use.
The fragment shader is used to calculate areas which will be covered with appropriate texture. The height range is set in the first smoothstep function, the second is used to set the fade range, I believe. Also there's a fog based on depth, which is mixed with textures set to create dark spots in deep areas of the mesh.
I'm working on some fun stuff with shaders and Leap Motion, hopefully will finish it somewhere to post a demo here. More about OpenGL is on its wiki pages, also here is some tutorials.
There was an article about Shadow DOM on the old blog of mine, and folks on the internet still refers to it, so I thought it would be nice to get it back and tell more about Web Components
Everything in this article is true for the moment when it was published. Things may change in the future.
Web Components is a new thing to the web, they are intended to bring more sense and reusability to modern front-end web development.
As for the spec Web Components consists of five pieces:
Shadow DOM
Templates
Custom Elements
Decorators
Imports
Shadow DOM
Shadow DOM talks for itself, if you think about it for a while. But before we start exploring it, make sure you have browser that supports all the stuff, you'll need Chrome Canary or Firefox Nightly. I'm using Chrome, where it's required to activate the experimental features. For Shadow DOM, open DevTools, go to settings panel and enable "Show Shadow DOM" option.
Once you've done, try to inspect this audio player, which is simply <audio> element.
You'll notice that there are a whole bunch of DOM elements hidden inside. That's right, all these controls elements are not some kind of magick, it's DOM, but hidden in the shadow. So the Shadow DOM provides an ability to create stuff out of the document global scope.
To apply Shadow DOM to the element use createShadowRoot method which returns a ShadowRoot node and the element itself becomes a shadow host. Then a number of elements can be appended to the shadow host.
Try to inspect this form, you'll wonder what's the shadow host of it.
var form = document.querySelector('.weird-form').createShadowRoot(); form.innerHTML = '<form><input type="text"><button type="submit">Submit';
We can also style stuff in the Shadow DOM.
var form = document.querySelector('.weird-form-style').createShadowRoot(); form.innerHTML = '<style>input{border:1px solid #000;border-radius:4px;padding:6px 8px;}button{background:#ccc;border-radius:2px;box-shadow:0 1px 2px #666;border:none;height:28px;}'; form.innerHTML += '<form><input type="text"><button type="submit">Submit';
The Shadow DOM has a scope, so the global styles will not affect shadow host children and vice versa.
What about applying styles from global scope? You probably know about ::placeholder pseudo-element and others. In this example the input field has a placeholder. Find the element which acts like a placeholder.
The element has an attribute called part with a value of -webkit-input-placeholder. An element of Shadow DOM can be styled from global scope using ::part(value) CSS function, where value is the value of the part attribute of the element. In order to get this work, you need to enable experimental Web Platform features, follow this link. Check this <h1> for one more time, it has same CSS applied, but not from the shadow scope.
var form = document.querySelector('.weird-form-style-global').createShadowRoot(); document.head.innerHTML += '<style>h1::part(weird-input){border:1px solid #000;border-radius:4px;padding:6px 8px;}h1::part(weird-button){background:#ccc;border-radius:2px;box-shadow:0 1px 2px #666;border:none;height:28px;}'; form.innerHTML = '<form><input type="text" part="weird-input"><button type="submit" part="weird-button">Submit';
Until then all operations with Shadow DOM was performed via JavaScript. But there's another way of doing this.
Templates
You are already familiar with templates if you using Backbone, Ember or any other template-dependent framework. In Web Components template is a piece of markup defined in <template> element. The beauty of it in a way of how it behaves on the page. The content of the template is parsed by the parser, but nothing will be loaded or rendered until you use the template. The template element itself is not rendered as well.
The template node has content property which holds the content of the template. To use/enable the template .cloneNode(true) method should be used on the content property, this will return the copy of the template content which can be appended elsewhere in the document.
And that's how the template can be used with Shadow DOM.
var form = document.querySelector('.some-element').createShadowRoot(); form.appendChild(document.querySelector('#weird-form').content.cloneNode(true));
Templates can be used not just with Shadow DOM, but with document DOM.
There are two ways of applying CSS to the template, both using <style> element.
In case of inserting templates to the document, we need to use <style scoped> where scoped attribute stands for the local style scope. CSS will be applied to the subtree of the root element where the <style> element is a direct child.
With Shadow DOM things are usual, shadow subtree has its own scope, so there's no need to define it.
When it comes to CSS and Templates, the scoped styles can intersect with the global scope and be overwritten using !important directive. Here are three examples below: Shadow DOM element built out of the template, usual DOM fragment built out of the template with scoped styles and the original markup. Also there's CSS in the global scope.
var form = document.querySelector('.weird-form-shadow-scope').createShadowRoot(); document.head.innerHTML += '<style>.weird-input{border:1px solid #ff0000;border-radius:0;background:#eee;box-shadow:inset 1px 1px 4px #666;}.weird-button{border-radius:0;box-shadow:2px 3px #000;}'; form.appendChild(document.querySelector('#weird-form').content.cloneNode(true)); document.querySelector('.form-local-scope').appendChild(document.querySelector('#weird-form').content.cloneNode(true));
The first example is fine, shadow subtree has its own scope and outer styles can break it, unless there are styles with ::part() function.
The last example is fine too, it relies on the styles from the global scope.
But the second one is broken, it has both scoped and global styles applied.
To avoid this we can use diff class names or even better is to add a prefix, which is how most frameworks work. There's another option exists.
Custom elements
Custom elements is a type of DOM elements which can be defined by authors. It means you don't need to use fancy class names for you application, you can create elements with a custom name.
document.register('my-element');
Now you can use element <my-element> as a usual DOM element. There's a limitation regarding naming conventions, the name of the custom element should include hyphen character and should not match some reserved names.
There's also a section that describes defining of a custom element right from the DOM and use of lifecycle callbacks, which is really interesting thing, but none of them are supported at the moment.
Decorators
Decorators can be used to enhance or override the presentation of the element. Notice that Decorators do not have a spec yet and it is currently unsupported by browser vendors.
The decorator element contains a template where the content element will be replaced with the content of the decorated element. Using select attribute you can specify the exact position of the particular element in the produced markup.
As long as decorators can be applied using CSS together with Media Queries, it's a powerful tool when it comes to Responsive Web Design.
Imports
The last but not least important part of Web Components is Imports. When you have lots of custom elements, templates and decorators it's not efficient to keep them all in the same document where you application runs. For example when developing with Backbone and Require.js templates can be included from external files right in the app.
Imports represents a way of doing same stuff using <link> element.
<link rel="import" href="templates/main.html">
And here's a simple demo using Imports, Templates, Shadow DOM and Custom Elements. The element is <x-widget>, and the import file is here x-widget, check its source code.
Enable "Enable HTML Imports" flag under chrome://flags
Manipulating rigged hand with Leap Motion in Three.js
I've got my Leap Motion controller recently and already have played with it for a while. There are lots of apps and games at Airspace Store. There's also the leap.js client lib which provides a nice API to use in the browser, you can try some examples here.
In this article I'm going to talk about rigged geometry in Three.js and how to apply Leap Motion data to run things, also about the API.
Try the demo if you have the controller device or watch the video below. The code is available on GitHub.
The demo showed in this video is actually the initial version, which is slightly diff from the current one.
API
In this section I'll describe the valuable data for current case only, for the full API, check official docs.
Leap Motion works in snapshot manner, which means it sends a block (frame) of data with all info about current state of the scene in particular moment of time. Here's the data required for a hand model with an armature.
There is, of course, a lot more output data, but it's fairly enough to implement rigged manipulation.
The hands array includes objects with data which describes each detected hand. The direction array describes direction unit vector which points from the palm position toward the fingers. The palmPosition array is the center position of the palm in x, y, z format. The pointables array is the list of the Pointable objects (fingers & tools), the tool is something other than a finger (pen, for example, it's longer and thinner). The direction array of the Pointable object describes the direction (as unit vector) in which finger or tool is pointing.
Hand rigging
That's how the armature of the hand should looks like.
And here are vertex groups assigned to appropriate bones.
If you wonder how to rig mesh in Blender, checkout previous article about rigging and skeletal animation.
Going live
Preparing the scene
Export the model , make sure required export options are checked: skinning, bones and skeletal animation. Setup a basic Three.js scene with model loader code from rigging article. Grab the latest leap.js client lib from its repo and include it in your html.
Setting up and passing Leap Motion data
You might know about Leap Motion Controller object which used to manually connect to device, but this is not necessary when using the frame loop, it will setup controller and connect by itself.
var leap = new Leap.Controller({host: 'localhost', port: 6437}); leap.connect();
Run frame loop. Leap.loop(); function passes a frame of data to the callback function 60 times per second using requestAnimationFrame();. Add this function call to the very ending of the model load function.
Leap.loop(function (frame) { animate(frame, hand); // pass frame and hand model });
The core function called in the callback includes extracted and structured data that describes position of the hand and fingers in 3D space, as well as position updating functions.
function animate (frame, handMesh) { if (frame.hands.length > 0) { // do stuff if at least one hand is detected var leapHand = frame.hands[0], // grab the first hand leapFingers = frame.pointables, // grab fingers handObj, fingersObj; // grab, structure and apply hand position data handObj = { position: { z: -leapHand.palmPosition[0]/4, y: leapHand.palmPosition[1]/6-30, x: -leapHand.palmPosition[2]/4+10 }, rotation: { z: leapHand.palmNormal[2], y: leapHand.palmNormal[0], x: -Math.atan2(leapHand.palmNormal[0], leapHand.palmNormal[1]) + Math.PI }, update: function() { var VectorDir = new THREE.Vector3(leapHand.direction[0], -leapHand.direction[1]+.6, leapHand.direction[2]); // define direction vector handMesh.lookAt(VectorDir.add(handMesh.position)); // setup view handMesh.position = this.position; // apply position handMesh.bones[1].rotation.set(this.rotation.x, this.rotation.y, this.rotation.z); // apply rotation } }; // grab, structure and apply fingers position data fingersObj = { update: function (boneNum, fingerNum, isThumb) { var bone = handMesh.bones[boneNum], // define main bone phalanges = [handMesh.bones[boneNum+1], handMesh.bones[boneNum+2]], // define phalanges bones finger = leapFingers[fingerNum], // grab finger dir = finger.direction; // grab direction // if current finger is thumb, use only one additional phalange if (!!isThumb) { phalanges = [handMesh.bones[boneNum+1]]; } // make sure fingers won't go into weird position for (var i = 0, length = dir.length; i < length; i++) { if (dir[i] >= .1) { dir[i] = .1; } } bone.rotation.set(0, -dir[0], -dir[1]); // apply rotation to the main bone // apply rotation to additional phalanges for (var i = 0, length = phalanges.length; i < length; i++) { var phalange = phalanges[i]; phalange.rotation.set(0, 0, -dir[1]); } }, // define each finger and update its position // passing main bone number and finger number fingers: { pinky: function() { fingersObj.update(3, 3); }, ring: function() { fingersObj.update(7, 1); }, mid: function() { fingersObj.update(11, 0); }, index: function() { fingersObj.update(15, 2); }, thumb: function() { fingersObj.update(19, 4, true); } }, // update all fingers function updateAll: function() { var fingers = this.fingers; for (var finger in fingers) { fingers[finger](); } } }; handObj.update(); // update hand postion // update fingers position if there are all five fingers is detected if (leapFingers.length == 5) { fingersObj.updateAll(); } } }
Basically it's easy to setup and run something with Leap Motion, but when the goal is to achieve the best possible results, all the pitfalls immediately goes up, for example I've used magical Math.atan2 for one of the hand rotation axis instead of palmNormal value. As it turned out, there's no nicely represented pitch, roll and yaw values, you need to calculate some manually, check this Leap demo to see what's wrong with rotation data. Also I've tweaked almost all data to make the model behave nicely on the screen.
One of the most important things to remember when building the armature for the model in Blender (or other software) for Three.js: do not move/rotate the armature, always align it using bone's head/tail position (this is true for Three.js r60, seems like in r56 it wasn't required).
Update: Do not move/rotate the armature, this will cause geometry stretching. Align the first bone using its tail/head position
Three.js supports some basic rigging and skeletal animation. The simplest way to achieve a nice character animation is to make it in appropriate software first. This article is going to be about Blender rigging workflow and how to do the best for a clean Three.js export.
Here's a very nice and detailed article about rigging and animating in Blender with further exporting and scene setup in Three.js. Actually I got this thing after reading the article. Here's my, actually same, workflow, but with some tips and additions.
That's what we'll get when all thing will be done.
Lets create a simple shape. You'll need Blender itself and exporter script, check the article about external models to find out.
Create a mesh
Remove camera and light from the scene and extrude the box. Select it with RMB, go into Edit mode with Tab, hit A to unselect the object, hit Ctrl+Tab and select Face select mode. Select one face with RMB, hit E, hold Ctrl and drag the mouse to extrude the face. Drag it until you get another cube in same size. Click LMB and release Ctrl key. Do the process for one more time to make another cube.
Create an armature
Now, add an armature. Hit Z to go to Wireframe view mode. Hit Shift+A, select Armature -> Single Bone, this will add a bone. Rotate and position the bone as shown below. Position the bone using its tail/head position. Select armature, go to Edit mode, select bone, hit E, hold Ctrl and drag to create a new bone. Create another two bones, so you'll get four bones armature.
Create and assign vertex groups
So here we have a mesh and an armature, the next step is to assign the geometry to the appropriate bones. It can be done in two ways: automatic assignment and manual. I prefer manual, you can choose assignment polygons more precisely.
Each set of polys is called a vertex group, and the name of the group should be exact same like the name of the bone we want assign to. Usually, in Blender, the first bone name is Bone, the second one is Bone.001, the next is Bone.002 and so on.
Select the mesh and go to Edit mode. Go to Object Data tab in the Properties panel. Hit + under the Vertex Groups list to add a new group, call it Bone.001, this is the second bone of the armature. Choose Face select mode and select all five faces of the first cube with Shift+RMB. Once you've done make sure that the right vertex group is chosen, hit Assign button under Vertex Groups. Now the first cube geometry will be transformed with its bone. You can check the assignment by selecting a vertex group and hit Select button, if there are extra faces, select only them and hit Remove button. Repeat the process for the rest of the mesh.
Tip: For more complex geometry to create nearby vertex groups and make sure that there are no same faces assigned to both, create the first vertex group, select it in the list, hit select to select its faces, select all faces of the second vertex group which are the nearest to first group faces, make sure the first vertex group is selected, hit Deselect and you'll get edge selection which is a boundary between two groups. Now you create the second vertex group based on that line.
Tip: When the armature is about to have a static geometry, read further about it, it also must be assigned to its bone, otherwise it won't be visible. So, we need to select all the faces that are not belong to any other vertex groups. The trick here is to select all groups (choose group in the list and hit Select) and then hit Ctrl+I which states for Inverse Selection, selected faces can be assigned to static vertex group.
There's one more action required to finish with assignment. We need to parent the mesh to the armature. Go to Object mode, select mesh, go to Object tab in the Properties panel and hit Parent box under relations section, select Armature object. Hit the box below and select Armature from the list. If the mesh has changed its position, align it back to the bones.
Animate the armature
The armature is ready for animation. Make sure the mark is in the first frame position on the timeline. Select the armature and go to Pose mode (Ctrl+Tab), select the bone, hit I to add a keyframe and choose LocRotScale. Move the mark to frame 10 and move the bone, add another keyframe. Do it with all bones and play with position and time, except the first one. The first bone is the support bone and it should be static. Do not change its position, just add a keyframe in the first frame. This is useful when you are doing a hand animation, for example, where only fingers are moving and the hand itself should be static. Select the last keyframe of the animation and hit E to set the end of the anim. Three.js will play the animation from the first to the last defined keyframes. That means if you have a gap before the first keyframe, it won't be played. Just add another keyframe in the first frame position to make that gap playable in Three.js.
In the article I've mentioned at the beginning there's a section about removing unused actions of the animation, to make sure Three.js will play the right one. This is a good point, but is not required if you are making all stuff carefully. But still, read about it.
Export the model
If everything is ok and you are happy with your skeleton and anim, it's time to export things. Make sure you have installed exporter script. Select both mesh and armature. Go to File -> Export -> Three.js. Enable the next options in the Export Three.js section: skinning, bones and skeletal animation. Export the model.
Setup Three.js scene
Use a scene setup from one of the previous articles, adjust light and camera position.
We need a loader to load the model, create SkinnedMesh instance using model geometry and materials and enable skinning on its materials.
var loader = new THREE.JSONLoader(); var animation; // load the model and create everything loader.load('model.js', function (geometry, materials) { var mesh, material; // create a mesh mesh = new THREE.SkinnedMesh( geometry, new THREE.MeshFaceMaterial(materials) ); // define materials collection material = mesh.material.materials; // enable skinning for (var i = 0; i < materials.length; i++) { var mat = materials[i]; mat.skinning = true; } scene.add(mesh); // add animation data to the animation handler THREE.AnimationHandler.add(mesh.geometry.animation); // create animation animation = new THREE.Animation( mesh, 'ArmatureAction', THREE.AnimationHandler.CATMULLROM ); // play the anim animation.play(); render(); }); function render() { animation.update(.01); renderer.render(scene, camera); requestAnimationFrame(render); }
Babylon.js: a complete JavaScript framework for building 3D games with HTML 5 and WebGL
Babylon.js is yet another Three.js, that's what I've noticed when started with the lib. The framework is on its early stage, a lot features hasn't been released yet, but there are a bunch presented already. So, let's start with a simple scene.
Apply some styles to make the scene fullscreen. I hope this will be deprecated.
html, body, .scene { height: 100%; width: 100%; }
The HTML will be a canvas element with scene class.
Grab the lib from its repo. As long as Babylon.js uses pointer events, you'll need a polyfill lib. Include both in your HTML, and let's go to scene code itself.
var canvas = document.querySelector('.scene'); // init Babylon engine var engine = new BABYLON.Engine(canvas, true); // init scene with the engine var scene = new BABYLON.Scene(engine); // init camera, name, position (x, y, z), camera vector, scene var camera = new BABYLON.ArcRotateCamera('Camera', 1, .8, 10, new BABYLON.Vector3(0, 0, 0), scene); // init light, name, light vector, scene var light = new BABYLON.DirectionalLight('dirlight', new BABYLON.Vector3(0, 0, 10), scene); // position the light light.direction.y = -100; light.direction.z = -40; //init shadow generator, resolution, light var shadowGenerator = new BABYLON.ShadowGenerator(1024, light); // create a sphere mesh, name, num of segments, size, scene var sphere = BABYLON.Mesh.CreateSphere('sphere', 10, 1, scene); // position the mesh sphere.position.x = .5; sphere.position.y = 1; // push the mesh to shadow generator to allow its shadow rendering shadowGenerator.getShadowMap().renderList.push(sphere); // create a material, name, scene var SphereMat = new BABYLON.StandardMaterial('lava', scene); // apply texture, path, scene SphereMat.diffuseTexture = new BABYLON.Texture('lava.jpg', scene); // apply material to the mesh sphere.material = SphereMat; // disable specular sphere.material.specularColor = new BABYLON.Color3(0, 0, 0); // create plane mesh, name, size, scene var plane = BABYLON.Mesh.CreatePlane('plane', 10, scene); // position the mesh plane.rotation.x = Math.PI/2; var PlaneMat = new BABYLON.StandardMaterial('ground', scene); PlaneMat.diffuseTexture = new BABYLON.Texture('ground.jpg', scene); plane.material = PlaneMat; plane.material.specularColor = new BABYLON.Color3(0, 0, 0); // allow mesh to receive shadows plane.receiveShadows = true; // init controls, so you can rotate and zoom camera scene.activeCamera.attachControl(canvas); // run rendering loop engine.runRenderLoop(function() { scene.render(); });
Generally, Babylon.js seems a very nice lib, I'm really looking forward to its future. For the moment it's weird to have something like ShadowGenerator and add objects in such a low level way.
I'll post some more articles about Babylon.js, stay tuned.
I've been granted access to Clara.io beta recently. This is what the Web world needs, more visual, not pure coding tools. Clara.io is a WebGL, Three.js based, 3D editor application in the cloud, and you can already try it, just apply for an invitation.
This thing is ready to use, but still requires improvements. You can model, animate and even render stuff with V-Ray render (not released yet, watch preview).
That's how the app looks from the inside, just like a typical 3D editor software. The UI is familiar for those, who have worked in any other editor.
On top there are four tabs with basic objects, lights, camera and null object to put on the scene.
On left side there is a list of objects, materials and assets under the Explorer tab, and the Sub-Object tab includes a bunch of editing modes on sub-object level (flip/edit normals, triangulate, mesh smooth, etc.).
To the right is the History tab in case you want to check what you've done before. And the Properties tab, where you can change object settings, apply modifiers, transforms, materials and other properties.
On the bottom is the Timeline, the Log window and the Script window where you can run external script.
Clara.io can import .jpg, .jpeg, .png, .gif, .fbx, .jsfbx, .obj, .mtl, .stl, .json files, and export to Collada, OBJ, STL, Three.js, jsFBX and FBX.
According to the docs, the app will release plugins support soon, you'll be able to create and use your own. Also they have a REST API, which provides a few operations you can make with your scenes (the Scene here is like a separate project file), such as list all scenes, create, delete, update, clone and share a scene.
In conclusion I'll say that there nothing new has been done, except that this is for the Web and it works on the Web, and that's a big deal.