Saturday, December 29, 2012

Animating the DOM: A New Year's Fireworks Effect

It's the end of the year and I'm in the mood to celebrate with some fireworks. However, instead of spending the money on real fireworks, I prefer to challenge myself and simulate them in the browser. My initial idea was to construct them using CSS3 3D transforms. The effect is nice and works well, however, only in Chrome. While Firefox may support 3D transforms, it certainly can not handle several hundred DIVs being transformed and animated without severe choppiness. So, in addition to a 3D version, I faked the 3D by creating several layers - each representing part of the sphere created by the firework exploding outward. Both version are on my sandbox for comparison [CSS3 3D version | Layered Version].

In my next post, I'll delve into how the 3D version works. For now, enjoy the show!

Monday, December 17, 2012

Using Sass and Compass to Write Less CSS Better, Faster, and Smarter

One of my favorite perks of building web-based tools is not needing to compile anything. Just open a text editor, write some code, and refresh the browser - instant gratification. So, as I've learning new technologies that are designed to make it easier to build web pages, I've been reluctant to use anything that requires building or compiling code. However, as I've written more and more CSS styles, I've found myself thinking:

  1. Wouldn't it be great if I could just reuse this block of styles and swap out the color.

  2. I need this in several sizes...

  3. The vendor prefixes are driving me nuts!



It seemed time to try a CSS meta-language to reduce the amount of work required to handle those scenarios I kept running into.  Sass seemed like a good starting point - I've seen quite a few references to it and like its syntax.  It really only takes a few minutes to figure out the basics.  I figured if it helped write less CSS, it would be worth it even if it required a build step to get to the final style sheet.

The true tipping point came when I realized I'd need to write the following styles to make my CSS-only spinner work properly across all the browsers:


@-moz-keyframes clock-spinleft {
from {
-moz-transform: rotate(0deg);
transform: rotate(0deg);
}

to {
-moz-transform: rotate(-360deg);
transform: rotate(-360deg);
}
}

@-webkit-keyframes clock-spinleft {
from {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}

to {
-webkit-transform: rotate(-360deg);
transform: rotate(-360deg);
}
}

@-o-keyframes clock-spinleft {
from {
-o-transform: rotate(0deg);
transform: rotate(0deg);
}

to {
-o-transform: rotate(-360deg);
transform: rotate(-360deg);
}
}

@-ms-keyframes clock-spinleft {
from {
-ms-transform: rotate(0deg);
transform: rotate(0deg);
}

to {
-ms-transform: rotate(-360deg);
transform: rotate(-360deg);
}
}

@keyframes clock-spinleft {
from {
transform: rotate(0deg);
}

to {
transform: rotate(-360deg);
}
}

@-moz-keyframes clock-spinright {
from {
-moz-transform: rotate(0deg);
transform: rotate(0deg);
}

to {
-moz-transform: rotate(360deg);
transform: rotate(360deg);
}
}

@-webkit-keyframes clock-spinright {
from {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}

to {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}

@-o-keyframes clock-spinright {
from {
-o-transform: rotate(0deg);
transform: rotate(0deg);
}

to {
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}

@-ms-keyframes clock-spinright {
from {
-ms-transform: rotate(0deg);
transform: rotate(0deg);
}

to {
-ms-transform: rotate(360deg);
transform: rotate(360deg);
}
}

@keyframes clock-spinright {
from {
transform: rotate(0deg);
}

to {
transform: rotate(360deg);
}
}

.spinner.clock {
position: relative;
height: 46px;
width: 46px;
}

.spinner.clock > div {
position: absolute;
border-style: solid;
background-color: transparent;
}

.spinner.clock .circle {
width: 36px;
height: 36px;
border-width: 5px;
border-color: #555;
-webkit-border-radius: 36px;
-moz-border-radius: 36px;
-ms-border-radius: 36px;
-o-border-radius: 36px;
border-radius: 36px;
}

.spinner.clock .wedge {
width: 0;
height: 0;
border-width: 23px;
border-color: #999 transparent transparent transparent;
-webkit-border-radius: 23px;
-moz-border-radius: 23px;
-ms-border-radius: 23px;
-o-border-radius: 23px;
border-radius: 23px;
-webkit-animation: clock-spinright 1250ms linear 0s infinite;
-moz-animation: clock-spinright 1250ms linear 0s infinite;
-ms-animation: clock-spinright 1250ms linear 0s infinite;
-o-animation: clock-spinright 1250ms linear 0s infinite;
animation: clock-spinright 1250ms linear 0s infinite;
}
.spinner.clock .wedge:before, .spinner.clock .wedge:after {
content: ' ';
position: absolute;
margin: 0;
top: -23px;
left: -23px;
width: 0;
height: 0;
border-style: solid;
border-width: 23px;
border-color: transparent #fff #fff #fff;
-webkit-border-radius: 23px;
-moz-border-radius: 23px;
-ms-border-radius: 23px;
-o-border-radius: 23px;
border-radius: 23px;
}
.spinner.clock .wedge:before {
-webkit-transform: rotate(-30deg);
-moz-transform: rotate(-30deg);
-ms-transform: rotate(-30deg);
-o-transform: rotate(-30deg);
transform: rotate(-30deg);
}
.spinner.clock .wedge:after {
-webkit-transform: rotate(20deg);
-moz-transform: rotate(20deg);
-ms-transform: rotate(20deg);
-o-transform: rotate(20deg);
transform: rotate(20deg);
}

.spinner.clock .arc {
width: 36px;
height: 36px;
border-width: 5px;
border-color: transparent transparent #bbb transparent;
-webkit-border-radius: 36px;
-moz-border-radius: 36px;
-ms-border-radius: 36px;
-o-border-radius: 36px;
border-radius: 36px;
-webkit-animation: clock-spinleft 750ms linear 0s infinite;
-moz-animation: clock-spinleft 750ms linear 0s infinite;
-ms-animation: clock-spinleft 750ms linear 0s infinite;
-o-animation: clock-spinleft 750ms linear 0s infinite;
animation: clock-spinleft 750ms linear 0s infinite;
}



Way too many vendor prefixes to deal with. I'd probably forget to change half of these if I wanted to tweak something. With Sass mixins, I should be able to reduce the amount of duplication due to the vendor prefixes. Sass is a Ruby gem so if you already have Ruby installed, get Sass is pretty straight forward:


gem install sass


Once installed, I just needed to create a SCSS file and wrap my existing CSS with some Sass magic:


@mixin animation($name: none, $duration: 0s, $timing: ease, $delay: 0s, $iteration: 1, $direction: normal, $fillmode: none) {
animation: $name $duration $timing $delay $iteration $direction $fillmode;
-moz-animation: $name $duration $timing $delay $iteration $direction $fillmode;
-webkit-animation: $name $duration $timing $delay $iteration $direction $fillmode;
-o-animation: $name $duration $timing $delay $iteration $direction $fillmode;
-ms-animation: $name $duration $timing $delay $iteration $direction $fillmode;
}

@mixin transform($params) {
transform: $params;
-moz-transform: $params;
-webkit-transform: $params;
-o-transform: $params;
-ms-transform: $params;
}

// https://gist.github.com/1607696
@mixin keyframes($name) {
@-webkit-keyframes #{$name} {
@content;
}
@-moz-keyframes #{$name} {
@content;
}
@-ms-keyframes #{$name} {
@content;
}
@keyframes #{$name} {
@content;
}
}

.spinner > div {
position: absolute;
background-color: transparent;
}

.spinner .circle {
border-color: #555;
width: 30px;
height: 30px;
border-radius: 30px;
border-width: 4px;
}

.spinner .wedge {
width: 0;
height: 0;
border-radius: 19px;
border-width: 19px;
border-color: #555 transparent transparent transparent;
opacity: 0.4;
@include animation(spinleft, 750ms, linear, 0s, infinite);
}

.spinner .arc {
width: 30px;
height: 30px;
border-width: 4px;
border-radius: 30px;
border-color: transparent transparent #999 transparent;
@include animation(spinright, 750ms, linear, 0s, infinite);
}

@include keyframes(spinleft) {
from { @include transform(rotate(0deg)); }
to { @include transform(rotate(-360deg)); }
}

@include keyframes(spinright) {
from { @include transform(rotate(0deg)); }
to { @include transform(rotate(360deg)); }
}


Much less code. A whole lot easier to read too. The next question that crossed my mind was that some helpful person out there probably already made a handy library that contains all those CSS mixins for vendor prefixes and other helpful short-cuts. And, to my delight, there is - Compass has mixins for a whole bunch of CSS3 styles so you don't have to make them all. Like Sass, Compass is a Ruby gem. Once installed, you simple create a project using:


compass create <myproject>


That will create several subdirectories with a config.rb file in the directory and some base Sass files. You simply add your Sass files to the indicated directory and start coding. To use one of the Compass libraries, just import them:


@import "compass/css3";


This pulls in all the available CSS3 mixins defined in Compass. Browse the Compass reference to see all the available functions. Once you want to generate your CSS file, you need to use Compass, not Sass to compile everything:


compass compile <myproject>


Now all the CSS files will be saved in the stylesheets directory in your project folder.

With Compass, I can now remove the mixins I defined because they are in the Compass library. With that change, I've created this final version:


@import "compass/css3";
@import "animation";

@mixin spinner-clock($name: null, $size: 35px) {

$class: "";
@if $name { $class: ".#{$name}"; };

// Need to work with whole numbers
// when divided by 2, otherwise the wedge
// will be offset from the center which is distracting
@if $size%2 > 0 { $size: $size+1; };

$border: ceil(10 * $size / 80);
$half: $size/2;

.spinner.clock#{$class} {
position: relative;
height: $size+$border*2;
width: $size+$border*2;
}

.spinner.clock#{$class} > div {
position: absolute;
border-style: solid;
background-color: transparent;
}

.spinner.clock#{$class} .circle {
width: $size;
height: $size;
border: {
width: $border;
color: #555;
}
@include border-radius($size);
}

.spinner.clock#{$class} .wedge {
width: 0;
height: 0;
border: {
width: $half+$border;
color: #999 transparent transparent transparent;
}
@include border-radius($half+$border);
@include animation(clock-spinright 1250ms linear 0s infinite);


// This does create a minor artifact in FF. The idea was
// to adjust the size of the wedge by sliding another semi-circle
// over it to make it look smaller. Comment it out or remove this section
// if you don't like it.
// or, even better, add a parameter to the mixin to not use it.

&:before, &:after {
content: ' ';
position: absolute;
margin: 0;
top: -1*($half+$border);
left: -1*($half+$border);
width: 0;
height: 0;
border: {
style: solid;
width: $half+$border;
color: transparent #fff #fff #fff;
}
@include border-radius($half+$border);
}

&:before {
@include transform(rotate(-30deg));
}

&:after {
@include transform(rotate(20deg));
}

}

.spinner.clock#{$class} .arc {
width: $size;
height: $size;
border: {
width: $border;
color: transparent transparent #bbb transparent;
}
@include border-radius($size);
@include animation(clock-spinleft 750ms linear 0s infinite);
}

}

@include keyframes(clock-spinleft) {
from { @include rotate(0deg); }
to { @include rotate(-360deg); }
}

@include keyframes(clock-spinright) {
from { @include rotate(0deg); }
to { @include rotate(360deg); }
}


That code will generate my spinner with all the different vendor prefixes with the additional perk of being able to specify an optional size. Since I was using Sass, I figured why not add some extra flexibility and reuse to the design of my spinner by making it a Sass mixin.

You might notice the @import "animation" at the beginning of the script. As it turns out, Compass does not have anything to generate the animation style or keyframes definition. However, there is a Compass extension that does add this support. Just install the gem:


gem install animation --pre


Open the Compass config.rb file in your project directory and add this line:


require 'animation'


Now that the required libraries are added, you can use the spinner mixin to generate all the required CSS for a specific size:


@import "spinner-clock";

@include spinner-clock("size15", 15px);
@include spinner-clock("size35", 35px);
@include spinner-clock("size55", 55px);


In this example, I named the spinner mixin code file _spinner-clock.scss and the above code css-spinner-clock-custom.scss. Once compiled, I'll be left with a css-spinner-clock-custom.css file with 3 distinct sets of spinner CSS style definitions each named differently (.size15, .size35, and .size55). In the HTML file, I just add the appropriate markup referencing the required styles:


<div class="spinner clock size15">
<div class="wedge"></div>
<div class="circle"></div>
<div class="arc"></div>
</div>

<div class="spinner clock size35">
<div class="wedge"></div>
<div class="circle"></div>
<div class="arc"></div>
</div>

<div class="spinner clock size55">
<div class="wedge"></div>
<div class="circle"></div>
<div class="arc"></div>
</div>


Here, the extra clock class is used to distinguish from other spinners I might make later. The most current version and full source, including an already generated CSS file, is available on GitHub. I also published a quick demo on the project page for the repository. In addition to the spinner described here, I've also made several other variations all using Sass/Compass as a basis for generating the required CSS.

Now these spinners don't work in all versions of IE (only version 10), but it illustrates the power of using a meta-language to define CSS style sheets. You can quickly appreciate the readability of the code and ability to organize and reuse common styles. I had to do a little extra work to get everything compiled and rolled out. However, the benefits were definitely worth the extra build step. Despite my attempts, it looks like I'll never be able to completely ditch the compiler.

Thursday, December 13, 2012

A CSS3-Only Animated Spinner

There was a time when animated GIFs dominated the web. Now, even using Javascript to animate objects on a page might be coming to an end. The CSS animation style has a lot of power available to create some nice animation sequences. I decided to test it out and make a spinner entirely from CSS.

To make this shorter and easier to read, I'm not adding the vendor-specific prefixes. Since most spinners are a circular shape, we're going to need some circles or parts of circles. By creatively using border-radius, we can turn a square DIV into a circle:


.circle {
width: 60px;
height: 60px;
border-style: solid;
border-radius: 35px;
border-width: 5px;
border-color: #999;
}


The height/width determines the size. The border-radius accounts for a half the width/height but must also include the border. In this case half of 60px is 30px plus the border of 5px to get to the correct value of 35px. Really, as long as its large enough, you'll have a circle - you could just error on the side of really big and use 60px - the browser will figure it out for you.


<div class="circle"></div>


Now, add the class to a DIV to get the following:

css3-rounded-corners-circle

A variation on the circle is to only include one side of the border to create an arc segment. Reusing the above circle definition, we just need to change the border-color style to use transparent borders for all the sides except the top:


.arc-top {
...
border-color: #999 transparent transparent transparent;
...
}



<div class="arc-top"></div>


css3-rounded-corners-arc

Another method to making shapes is to use a 0px height/width DIV and manipulate the borders to create triangles:


.triangle {
width: 0;
height: 0;
border-style: solid;
border-width: 35px;
border-color: #999 transparent transparent transparent;
}



<div class="triangle"></div>


This will only show the top border which will look like a triangle:

css-borders-triangle

Taking that concept and applying a border-radius will give the triangle a rounded edge.


.wedge {
width: 0;
height: 0;
border-style: solid;
border-radius: 35px;
border-width: 35px;
border-color: #999 transparent transparent transparent;
}



<div class="wedge"></div>


Which results in a wedge shape:

css3-rounded-corners-border-wedge

With those individual pieces created, I just need to make them move in a spinner like fashion. The animation style has a lot of options. Among them is the name of a keyframes definition which actually defines the path of the animation.


.spinner .wedge {
animation: spinleft 750ms linear 0s infinite normal none;
}

This definition provides a lot of flexibility for designing animation sequences. However, for my purposes, I just needed it to rotate a part of the spinner around in circles:

@keyframes spinleft {
from { transform: rotate(0deg); }
to { transform: rotate(-360deg); }
}


Now, just smash together a circle, arc, and wedge:


<div class="spinner">
<div class="circle"></div>
<div class="wedge"></div>
<div class="arc"></div>
</div>


And then apply the animations to infinitely perform the rotations and, without using an image or writing any Javascript, you have yourself a spinner. The full source and demo is on my sandbox. It won't work in IE since it does not support the animation style. However, it provides an interesting glimpse to the power provided in the CSS specification slowly making its way into the browsers.

Sunday, December 9, 2012

Creating a Perspective based Animated 3D Ribbon Effect

css3-ribbon-effect
It seems that I've seen a lot of uses of this ribbon effect springing up lately. Maybe they've been there and I just haven't really noticed. On RedHat's careers page, it has them every where. I even saw them in a Popular Science article I was reading the other day. The effect does make headings stand out better by offsetting them from the main page alignment. I figured someone probably found a way to do this with just CSS3 and sure enough, I found this article with a solution.

What's really nice about this approach is that it doesn't require any additional markup. However, there are some limits to using :before and :after to create the extra content required for the effect. If you look at the available variations, you need to create content for the wrapping part with either the :before or :after. This leaves you with one pseudo-element to either create a wrap on the other side or one of those tail effects (on either side). If you want a complete ribbon with left-tail, left-wrap, right-wrap, and right-tail, you'll need at least one more element in the markup to attach these styles to. Additionally, animating the styles created by the pseudo-elements would be a challenge. Notice that the image above has two different wrap effects for the top (Apples) and bottom (Bananas). If I wanted to transition from the top to the bottom, it would not be possible with content generated by :before and :after because I need to animate those little triangles that create the wrap effect.  

Those triangles are actually another CSS trick that creatively uses the border style to make the correct shape. Since I want to change the border style based on the position of the element to maintain the illusion of perspective, I'll need to add markup around my original block and style it similarly.  While this approach does add extra content, it does turn this solution into something that possibly more flexible and easier to debug.

Before digging into the calculations to position and size the border, we need to create the base markup and static styling:

<div class="wrapper">
<div id="move"></div>

<div id="mv1" class="banner">
<div class="wrap"></div>
<div class="main">Apples</div>
</div>
</div>


The wrapper element is the guide the banner will appear to be wrapping around. It can be styled with any kind of height/width/color you'd like. The important thing to note is the relative positioning and the padding. The banner element will need to be positioned accordingly based on that setup:


.wrapper {
position: relative;
width: 100px;
height: 360px;
margin: 30px;
padding: 30px;
background-color: #ddd;
border-radius: 5px;
}


Now, for the actual banner/ribbon element. Since our intention is to move this element around, it needs to be absolutely positioned. I setup the padding of the wrapper to be 30px above so I'll initialize the top to 30px to match. I want the banner to be 10px left of the wrapper but the padding is 30px so I need to move it -40px. Everything else needs to be moved around accordingly so it fits together properly. The .main class will hold the text and the .wrap class implements the triangle border trick to complete the ribbon effect.


.banner {
position: absolute;
top: 30px;
margin-left: -40px;
}

.banner .main {
cursor: pointer;
position: relative;
padding: 10px;
padding-left: 20px;
padding-right: 20px;
min-width: 75px;
font-weight: bold;
color: white;
background-color: gray;
border-radius: 0px 5px 5px 0px;
}

.banner .wrap {
position: absolute;
top: 0px;
left: 0;
width: 0;
height: 100%;
border-width: 0px 10px 5px 0px;
border-style: solid;
border-color: transparent #aaa transparent transparent;
}


The wrap class is set to the initial position of the banner which is at the top with the wrap appearing on the bottom of the banner (the Apples one in the image above). We only need the right border on this element and will adjust the top/bottom widths to manipulate the shape of the triangle formed by the visible corner of the border. Since the wrap element is behind the main element, we'll carefully move the wrap element to only show the triangle portion of the border.

As a test, I originally setup a slider control to see the result of my calculations at different positions. The top point is at 30px and the bottom extreme is at 330px (creating a 300px range). Over that 300px range, the wrap element needs to adjust the bottom triangle from 5px high to 0px and then adjust the top triangle from 0px to 5px tall. An important point to notice here is that both top and bottom borders do not change at the same time. At our fictitious eye level, we should not see the wrapping since it should be directly behind the main element. So the full range is really 10px that will be traveled from top to bottom. The first calculation is to scale the current position of the banner to the range of the wrap element. To simplify the logic, I move it so the range is from -5 to 5. The final adjustment is to shift the wrap element up and down 5px to properly expose either the top or bottom border triangle:



var p = Math.round(10 / 300 * (ui.value - 30)) - 5;

$('#mv1')
.css('top', ui.value)
.find('.wrap')
.css({
top: (p >= 0 ? -p : 0),
borderTopWidth: (p >= 0 ? p : 0),
borderBottomWidth: (p <= 0 ? -p : 0)
})
.end()
.find('.main')
.css({
boxShadow: '3px '+(-p/2)+'px 3px 0 #aaa'
});



After successfully figuring out that logic, I moved it to an animation which will occur when the banner is clicked. It will transition up and down from the top to the bottom of the wrapper on each click:



$(function() {

var dir = 1;
$('#mv2')
.click(function ( e ) {

// Cache these for use in the step function
var $w = $('#mv2 .wrap'),
$m = $('#mv2 .main');

// Animate the position of the whole banner element but
// use the step callback to manipulate the position of the
// wrap element based on the current position.
$(this)
.animate({top: (dir == 1 ? 330 : 30)}, {
step: function (now, fx) {

var p = Math.round(10 / 300 * (now - 30)) - 5;

$w.css({
top: (p >= 0 ? -p : 0),
borderTopWidth: (p >= 0 ? p : 0),
borderBottomWidth: (p <= 0 ? -p : 0)
});

$m.css('box-shadow', '3px '+(-p/2)+'px 3px 0 #aaa');
}
});

dir *= -1;

});

// jQuery makes this easy to apply for all browser flavors.
$('#mv2 .main').css('box-shadow', '3px 3px 3px 0 #aaa');

});


I setup a demonstration of this code on my sandbox with both the slider implementation and the animated version. Following along the same approach, you can add more elements to the wrapper and position their wrapper to maintain the correct perspective that occurs during the animation. Add some more logic and could build an accordion style menu or content container.

Saturday, December 1, 2012

Proxying Touch Events to Enable jQuery UI Mouse Interactions

All the jQuery mouse interaction widgets (draggable, droppable, etc) are great until you try them on a touch enabled device like a tablet or phone. Those browsers do not generate mousedown, mousemove, or mouseup events. Instead, they generate toushstart, touchmove, and touchend events. Since jQuery UI is only binding to the mouse events, it will never receive a touch event which results in nothing working. You might be tempted to just bind to the touch events and try to treat them as mouse events. However, because most devices allow multiple touch points, the event does not have a single pageX/pageY variable. Each touch point is represented in an array of touches so any code would have to detect the touch event and look at that array instead of the standard pageX/Y variables.

An alternative approach would be to simply capture the touch events, pull out one of the touches, and then fire the equivalent mouse events. Fortunately, someone already thought of this and created a little hack called Touch Punch to basically proxy touch events and convert them to mouse events. The nice thing about this little library is that it doesn't require to do anything other than including it on your page to use it. Once its added, it just works - all the normal mouse interactions in jQuery UI function on a touch device.

I added it to my sandbox so my demos would be useable on a touch enabled device. As I tested several pages, all the jQuery UI elements worked properly, however, anything I personally bound to mousedown did not work as intended. I created a page on my sandbox specifically to try out the Touch Punch functionality. The goal was to animate something while the mouse was held (mousedown) and animate back to the start when the mouse was released (mouseup). Using Touch Punch, the mousedown event didn't behave the same between my computer and tablet. On the computer, it worked as expected - the animation continued as long as I held the mouse down. On the tablet, it just skipped to the end and then animated back to the beginning as if I on clicked on the element.

I suspect the difference in behavior is due to how the jQuery UI mouse interactions bind to the different events. I spent some time reviewing the UI code and there are several things happening that my simple setup is not doing. Technically, in my code, I don't care where you click - just that you did. So I could bind to the mousedown and touchstart events simultaneously since I don't need the x/y coordinates. However, in other examples in my sandbox, I do need the x/y so I was hoping Touch Punch would be a simple all-in-one solution.

I will probably need to spend a little more time digging to find the solution to my mousedown problem. However, jQuery UI works quite well so if you're looking for something to get your UI interactions working on a tablet, Touch Punch will do the trick.

Tuesday, November 27, 2012

Guiding User Entry using jQuery Input Masks

Providing masked input entry on a form is an excellent way to guide the user to both enter correct values and require less keystrokes to make those entries. The challenge is determining the boundaries of the functionality the mask provides to achieve various formatted entry. As I worked with the different options available, I quickly found different limits that each library was capable of providing. I've broken down the features I was interested in enabling into three main groups:


  • Masks with fixed formats and no range validation on the user entry other than numeric and/or alpha characters. Phone numbers, tax ID, serial numbers, etc all fall into this group. Its the simplest implementation that is basically only interested in preserving a specific format and forcing certain characters to be entered.

  • Masks with fixed formats with specific range validation on each "field" of the mask. Dates and times are the two most common examples. Each part of the date/time must fall in a certain numerical range to be valid. Dates are further complicated by dependencies on other parts of the date (ie number of days in a month and leap years).

  • Formatted entries that are not really a mask but can use some of the logic provided in the mask library to manage the input. Currency and numbers are the primary concerns in this case. Providing visual cues for thousand separators, decimals, etc. provide helpful feedback to the user when entering larger numbers. You don't want to really show a mask for the entry - you just want the number to format as the user enters the value.



As I evaluated different libraries, I attempted to recreate the functionality outlined above. I started by looking at the options available in the jQuery UI library since I'd like to use masked entry in conjunction with other jQuery UI widgets. At this point, there is no production version of a mask control. On the development wiki, a mask widget is discussed and its basic functionality defined. As I dug through GitHub, I did find it in a separate branch. Given that its clearly not ready for production use, I needed to find a suitable alternative.

I started by trying out Josh Bush's (digitalbush.com/projects/masked-input-plugin) mask plugin. It's functionality is very similar to that described by the Wiki page. One common feature I found both libraries do not have is more advanced control input constraints on the mask. For instance, you can create a mask for dates (99/99/9999) but it does not constrain the range of numbers to only actual dates. Additionally, it does not auto-format the numeric entry with thousand separators, dollar signs, etc.

After some further searching, I found RobinHerbots/jquery.inputmask which actually credits Josh's work as the foundation for the plugin. This more robust implementation has a lot more extensibility. Out-of-the-box, it can understand dates, numeric values, and provides sufficient hooks to add more. One of the features that impressed me was the auto-complete logic for dates and times. If you type 2512 in an input with "m/d/y" specified as the mask, it will properly expand to 02/05/2012. Additionally, the time mask will take 22 hours and convert it to 10 PM. The plugin also has a "decimal" mask that will auto-format numeric entries as they are typed and constrain the values appropriately. When testing the numeric input mask, I had issues with the numericInput option which is suppose to right along the text in the input box. Whenever I'd try to click on the decimal, it would jump to the integer portion of the value. Turning that off, fixed the issue. I also had issues trying to extend the decimal mask to add a dollar sign to the input and integrating it with a jQuery UI spinner. The documentation is a little thin so I may just not be entirely grasping the concept of how to extend it further.

Looking at these different options made me wonder whether there may be an argument to split the physical masking component from the actual validation of the entry. Even though, by definition, the mask is providing a certain degree of validation (only numbers, letters, etc), its main purpose is to provide the formatting so the user is not required to provide it. This is both a short-cut and a method to ensure consistent entry. However, I still believe its important to attempt to provide validation cues to the user as quickly as possible to help guide proper entry. If it can be done while entering parts of a value (like a the month or day in a date), then the user will less likely be required to revisit a field when the final form validation is performed.

As an experiment, I created a demo in my sandbox which uses the digitalbush library to provide the base mask functionality. I extended the library to recognize the different fields of the mask and request validation of each field as the user enters the values into the mask. This functionality allowed me to provide date/time validation similar to the RobinHerbots/jquery.inputmask implementation. The setup of the mask for the date includes the basic mask (99/99/9999) and then a validation function to handle the range checks:


$("#date2")
.mask(
'99/99/9999',
{ validate: function (fld,cur) {
// 0 == month; 1 == day; 2 == year
var mm = parseInt(fld[0]),
dd = parseInt(fld[1]),
yy = parseInt(fld[2]),
vl = true;

if (!(mm >= 0 && mm < 13) && cur == 0) {
fld[0] = '12';
vl = false;
}

if (!(dd >= 0 && dd < 31) && cur == 1) {
fld[1] = '01';
vl = false;
}

if (!(yy >= 1976 && yy < 2199) && cur == 2 && fld[2].replace('_','').length == 4) {
fld[2] = '2012';
vl = false;
}

return vl;
}
});


Its not a perfect implementation - it doesn't handle the dependencies of number days in a month and leap year. It would also make sense to move the validation into a global function in the library (ie $.mask.validate.date) so it could easily be reused and provide different validation for other formats.

For formatted numeric fields, I mainly used the Globalize library to validate and format the entry on each keystroke. I only used the caret() plugin provided in the digitalbush library to help place the cursor in the correct spot in the field as the entry was made:


var old = '';
$("#decimal2")
.on('focus', function (e) {
old = '';
})
.on('keydown', function (e) {
old = $(this).val();
})
.on('keyup', function (e) {
var n = Globalize.parseFloat($(this).val()),
pos;

if (isNaN(n)) {
pos = old.indexOf('.');
$(this).val( old )
.caret( pos, pos);
} else {
n = Globalize.format( n, 'n' );
pos = n.indexOf('.');
$(this).val( n )
.caret( pos, pos);
}
});


Again, a fairly rough implementation but it provides a template for how to approach the problem. The mask plugin already is attached to these events and with some tweaking could be augmented to provide the cursor control but not enforce a fixed mask for the input. Globalize already has a substantial amount of logic to handle numeric parsing/formatting across multiple locales so it makes sense to utilize this functionality in this situation.

Using masked input makes a lot of sense to help guide the user to enter correct data with minimal effort. While there is some work required to achieve certain features, the foundation exists to build these components. It might be awhile before the jQuery UI implementation is ready, but there are some good alternatives available that provide similar functionality that you can expect to see in the final jQuery version.

Tuesday, November 20, 2012

Selecting Ranges with the jQuery UI Datepicker

The current supported method of selecting a date range with the jQuery UI Datepicker is to use two pickers which represent the start and end of the range. In certain scenarios, where there is a large range, using two fields is a preferable choice. However, for shorter ranges, having the ability to allow the user to pick it from one box seems like a feasible alternative.

The Google Analytics report date filter takes a kind of hybrid approach. The UI has one field representing the range with a drop down menu that shows several different range selection options. There are two fields to enter a range but you can also select the range from the picker and achieve the same result. Its a fairly specific use case but an interesting approach worth considering:



I did find a jQuery plugin that provided this feature at eyecon.ro/datepicker/. I tested the plugin to see how it worked and ran into several issues. Typically, you don't have to specify any options to use a widget, however, minimally, you need to specify the date option otherwise it will throw an error. Additionally, the code referenced the depreciated jQuery.curCSS(). That needed to change to get it to work with a more recent version of jQuery. There are some other quirks as well - its last build was in 2009 so its probably not as well maintained as one would like. Regardless of the issues, the demos provided some ideas on how to modify the jQuery Datepicker to provide a similar feature.

With a little work, I was able to get the jQuery UI Datepicker to select more than one date. It was a little frustrating trying to deal with the two modes of operation - inline in a DIV or attached to an input field. In the former, the picker is always visible while, in the latter, it only appears when focusing on the input and hides once the user selects a date. I unfortunately needed a hybrid behavior:


  • Show the picker when the input receives focus. However, don't hide the picker once a date is selected, instead stay visible to allow the user to pick a second date to complete the range.

  • Show the selected range in the picker by highlighting the date included in the range. Additionally, update the input field with the selected range as the user picks different dates.

  • Only hide the picker when the user explicitly says they are done selecting the range.



The first feature can be accomplished using an inline picker, manually positioning it under the input field, and handling the binding to the focus event outside of the pickers built-in functionality. Here's the setup:


<div id="jrange" class="dates">
<input />
<div></div>
</div>




// global variables to track the date range
var cur = -1, prv = -1;

// Create the picker and align it to the bottom of the input field
// Hide it for later
$('#jrange div')
.datepicker();
.position({
my: 'left top',
at: 'left bottom',
of: $('#jrange input')
})
.hide();

// Listen for focus on the input field and show the picker
$('#jrange input').on('focus', function (e) {
$('#jrange div').show();
});



That part is pretty straight forward - it mimics the functionality that would happen automatically if the picker was attached to the input field. However, since I don't want the picker to hide once a date is selected or have that date set in the input field, I have to attach it inline to the DIV.

The next step is to use several of the callback function available in the Datepicker widget to manage the range selection. Two variables are needed to capture the current day selected and remember the last day selected. Each time a day is selected, the onSelect callback is called. The bulk of the logic needs to go in this function:


$('#jrange div')
.datepicker({
onSelect: function ( dateText, inst ) {
var d1, d2;

prv = +cur;
cur = inst.selectedDay;
if ( prv == -1 || prv == cur ) {
prv = cur;
$('#jrange input').val( dateText );
} else {
d1 = $.datepicker.formatDate(
'mm/dd/yy',
new Date( inst.selectedYear, inst.selectedMonth, Math.min(prv,cur) ),
{}
);

d2 = $.datepicker.formatDate(
'mm/dd/yy',
new Date( inst.selectedYear, inst.selectedMonth, Math.max(prv,cur) ),
{}
);
$('#jrange input').val( d1+' - '+d2 );
}
}
});


This code will update the global range variable and update the input box with the current selected range (or just the one date if only one day is selected). This captures the range but what about showing it on the actual picker? The beforeShowDay callback provides the necessary hook to achieve this feature. Its called for each day in the picker and enables you to set a class on the TD that represents the date passed in as a parameter:


$('#jrange div')
.datepicker({
beforeShowDay: function ( date ) {
return [true,
( (date.getDate() >= Math.min(prv, cur) && date.getDate() <= Math.max(prv, cur)) ?
'date-range-selected' : '')];
}
});


Once the class is set, a small bit of carefully crafted CSS styles are needed to set the background color of the links in the table cells:


.date-range-selected > .ui-state-active,
.date-range-selected > .ui-state-default {
background: none;
background-color: lightsteelblue;
}


So far, I have been able to utilize all the built-in options of the Datepicker to make this feature work. However, hiding the picker proved to be a challenge. Since, internally to the picker, it is considered inline, any logic that would allow the picker to be hidden is disabled. This includes clicking outside the picker, the close button that can optionally be enabled, or any other method normally available when the picker is attached to the input field. I decided to enable the button line and manually add the close button back onto that line myself. Unfortunately, it can't be done only once when the widget is created because the HTML for the picker is constantly being regenerated which would immediately remove the button. Instead, I added an option for an onAfterUpdate callback to the Datepicker and added a proxy to the internal _updateDatepicker function to call that callback. This will enable me to add the button back each time the calendar HTML is rebuilt:


$.datepicker._defaults.onAfterUpdate = null;

var datepicker__updateDatepicker = $.datepicker._updateDatepicker;
$.datepicker._updateDatepicker = function( inst ) {
datepicker__updateDatepicker.call( this, inst );

var onAfterUpdate = this._get(inst, 'onAfterUpdate');
if (onAfterUpdate)
onAfterUpdate.apply((inst.input ? inst.input[0] : null),
[(inst.input ? inst.input.val() : ''), inst]);
}


The implementation of the callback simply adds the same button HTML that the widget would create if it were in non-inline mode:


$('#jrange div')
.datepicker({
onAfterUpdate: function ( inst ) {
$('<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" data-handler="hide" data-event="click">Done</button>')
.appendTo($('#jrange div .ui-datepicker-buttonpane'))
.on('click', function () { $('#jrange div').hide(); });
}
});


The full source of this implementation and a demo are on my sandbox. There are probably some improvements that can be made to make it easier to reuse the customizations and allow it to work properly with other non-range pickers. However, its a good proof of concept that illustrates how to provide range selection in one Datepicker widget.

Wednesday, November 14, 2012

CSS3 3D Transforms: Rotating Around All the Axises

It seems like a natural evolution to add 3D abilities to the browser.  If you're going to perform 2D transforms.  Why not add 3D transforms?  While not fully supported, 3D transforms are fun and when used appropriately, can really enhance the user experience.

I've been avoiding 3D simply because of the lack of support (IE9 and Opera do not support 3D - see this site for the current support matrix).  However, for fun, I decided to try it out and see how it works.  From a UI perspective, the carousel pattern seems to be a good place to use 3D.  As I was looking for examples, I found this site which has an implementation.

As I looked over the code, I noted several things:

  1. The order of the transform is important.  The wrong order will cause elements to align in unexpected places.

  2. The perspective style will affect the look of the 3D effect.  Most likely, setting it on a container and using transform-style: preserve-3d will be desirable.

  3. Using transform-origin can make it a lot easier to rotate elements in 3D space.



In my sandbox, I created a test to tinker with the different 3D styles to see how they worked. The basic setup is to create a wrapper DIV which will have the perspective style set to some value between 200px and 1000px. Each child DIV will then use transform-style: preserve-3d to inherit the perspective. Additionally, the transform-origin is set so all transforms occur relative to the center of the element but behind the element (by default it is -100px behind each element). Once all the elements have a common transform point established, a rotation transform is applied on one of the DIVs on a specific axis. In this example, the "outer" DIV is rotated around the x-axis, the "inner" DIV is rotated around the z-axis, and the "box" DIVs are rotated around the y-axis.


<div class="wrapper">
<div class="outer">
<div class="inner">
<div class="box">One</div>
<div class="box">Two</div>
<div class="box">Three</div>
<div class="box">Four</div>
<div class="box">Five</div>
</div>
</div>
</div>


The result enables rotating all the elements around the established point of origin on the different axises. There are probably different ways to achieve the same result. The carousel example I linked to above translated the elements on the z-axis to position them properly in space. I prefer using the transform-origin style since the browser does the math for me and makes the code much simpler write and maintain.

While probably not something to use extensively, 3D transforms certainly can add some nice transition effects. If its used appropriately, the transitions could degrade well enough that users visiting with browsers that do not support 3D will still be able to access content. This might require some creative coding or even browser support checks. However, over time, the features will become mainstream and enable a whole new dimension to page design.

Tuesday, November 13, 2012

Using Raphael Sets to Mimic SVG Groups

Raphael provides a nice library for simplifying working with vector graphics. One of its strengths is to work with both SVG and VML making it compatible with older versions of IE. However, with that compatibility comes some sacrifice in features. For instance, you are not able to define SVG groups with the Raphael API. Instead, you use the sets feature to mimic some of the functionality available with the grouping element.

Consider the example on the W3 site related to SVG arcs. This example SVG document does two things - a) it defines a reusable set of SVG objects which are later referenced in the document so they don't have to be declared again, and b) it uses groups to apply transforms to all the contained elements which simplifies the definition.

If you look at the source, you'll see the two ellipses and some text are defined in the "defs" tag:


<defs>
<g id="baseEllipses" font-size="20" >
<ellipse cx="125" cy="125" rx="100" ry="50"
fill="none" stroke="#888888" stroke-width="2" />
<ellipse cx="225" cy="75" rx="100" ry="50"
fill="none" stroke="#888888" stroke-width="2" />
<text x="35" y="70">Arc start</text>
<text x="225" y="145">Arc end</text>
</g>
</defs>


That definition is then reference five times to create the base objects that form the background that other elements are drawn on. The "g" tag is utilized to define each section of the example and translate it to a different point on the screen:


<g transform="translate(400,0)">
<text x="50" y="210">large-arc-flag=0</text>
<text x="50" y="250">sweep-flag=0</text>
<use xlink:href="#baseEllipses"/>
<path d="M 125,75 a100,50 0 0,0 100,50"
fill="none" stroke="red" stroke-width="6" />
</g>


In Raphael, there is not a way to create "defs" and "g" tags. However, you can use the clone() and sets to achieve similar results. I replicated the example SVG document completely with Raphael on my sandbox. The first step was to define the SVG canvas and then create a set that will represent the base ellipse objects that were defined in the "defs" tag:


var r = Raphael( $('.wrapper')[0], '12cm', '5.25cm' ).setViewBox(0,0,1200,525,false),
g1 = r.set(),
g2, attr;

attr = { 'stroke':'#888888', 'stroke-width':'2' };
g1.push( r.ellipse(125,125,100,50).attr(attr) );
g1.push( r.ellipse(225,75,100,50).attr(attr) );

attr = { 'text-anchor': 'left', 'font':'1.3em "Verdana"' };
g1.push( r.text(70,60,'Arc start').attr(attr) );
g1.push( r.text(270,140,'Arc end').attr(attr) );


You can see that the code matches pretty well with the actual SVG document. The difference with the above code is that the objects created will serve as both the first ellipses/text displayed in the top/left of the page and as the cloned object for the remaining four groups. In the SVG example, the "defs" group is not visible - a separate declaration was required to use it.

Next, I used clone() on the set object to create a new set of SVG objects. I then added the specific elements to the group and then transformed them to move the elements to the correct position on the screen:


g2 = g1.clone();

attr = { 'stroke':'red', 'stroke-width':'6' };
g2.push( r.path('M 125,75 a100,50 0 0,0 100,50').attr(attr) );

attr = { 'text-anchor': 'left', 'font':'1.5em "Verdana"' };
g2.push( r.text(150,210,'large-arc-flag=0').attr(attr) );
g2.push( r.text(150,250,'sweep-flag=0').attr(attr) );

g2.transform('t400,0');


If you inspect the elements on the page, you'll see that no "defs" or "g" tags are generated by Raphael. It produces a fairly flat document constructed with the basic SVG objects. The transform operation is applied to all the elements in the set and not just to a containing group tag. The set operations are internal to Raphael and have no real implementation in the DOM. This allows Raphael to select between SVG/VML depending on the browser to ensure compatibility. However, the downside is that you don't get all the native SVG features. The Raphael API does still provide flexible features to manage groups of elements - it just abstracts it from the underlying implementation.

Thursday, November 1, 2012

Finding Points Along an SVG Path using Raphael

In my last post, I discussed how using paths with Raphael is a good thing and that certain functions in the library only work with SVG elements of type "path". Now I'm going to do something a little more complex with paths which will utilize these functions. I've posted a demo on my sandbox which implements a slider like control that is bound to a SVG path. Typically, you'd be restricted to a slider that basically moves over a straight line - up/down or left/right. With this functionality, all kinds of slider controls can be created. With the tools in Raphael, this feature is pretty easy to implement.

First, we need to create a path that will serve as the constraint for our slider knob. There are two types of paths we can create:


  • A closed path that is semi-circular. The knob will continue around from start to finish and start again.

  • A open path that is linear in nature. There is a start and end which the knob can travel back-and-forth between.



While the path does not need to be completely circular or exactly linear, the closer they are to these basic shapes, the better the effect. This is because the points are being estimated based on these shapes. If you look at the demo and compare the circle to the ellipse, you might notice that the knob drifts slight from the mouse pointer as you move around the ellipse whereas the circle does not. This is due to the error in the estimation. This is more evident in closed paths that use the circle estimation than the linear path approximations.

In the example, I created a list of predefined shapes that can be created as reference paths. The following line of code uses the currently selected shape (based on clicking one of the buttons) and adds it to the DOM using the Raphael path() function. I pass the path definition through Raphael.transformPath() because all the paths need to start at a specific place for the logic to work.



path = paper.path( Raphael.transformPath(pdefs[useDef].path, pdefs[useDef].transform) )
.attr( 'stroke-width', 10 )
.attr( 'stroke', 'rgb(80,80,80)' ),



Next, I create a simple slider knob. I purposely created this as an ellipse to show how the shape follows the angle of the path as well as the position on the path. Additionally, I need something to track the current mouse position so I add a DIV and position it directly over the knob object. This ensure all dragging operations are caught by the DIV. I did this so even if you move outside of the path, the knob will keep moving relative to the dragging DIV.



knob = paper.ellipse( 0, 0, 25, 15 )
.attr( 'fill', 'lime' )
.attr( 'stroke', 'rgba(80,80,80,0.5)' ),

$shim = $('<div>')
.css( {position: 'absolute', width: 50, height: 50 } )
.appendTo( $('.wrapper') ),



Now, I need a few other pieces of information about the reference path. I initialize the total length of the path, find the center of the path based on its bounding box, and set the starting point of the knob to the start of the path:



len = path.getTotalLength(),
bb = path.getBBox(),
mid = {x: bb.x+bb.width/2, y: bb.y+bb.height/2},

pal = path.getPointAtLength(0);



The Raphael getPointAtLength() function is a really useful function to find a specific point on the path based on the length along the path. So if you have a circle with a parameter of 100 pixels, getPointAtLength(10) would return a point on the circle 10px from the start. In my demo, if I want to know where to position my slider knob is suppose to be on the reference path, I need to figure out how far along the reference path the proxy DIV has been dragged.

For a circular shape, we can figure this out by converting the Cartesan coordinates to Polar coordinates. This transformation provides an angle around a circle which when divided by 360 can be used as a percentage around the circle. That percentage can be multiplied by the total length of the path to find the length to pass to getPointAtLength().

I illustrated this concept below by drawing a reference circle over the rectangle. The yellow dot represents where the DIV has been currently dragged. Using this point relative to the center of the rectangle, we can find the angle around the circle. For this calculation, 0 degrees is on the positive side of x-axis. This is important and the reason why I transformed the reference paths - I needed their start point to align with 0 degrees. In this example, the point is at the 45 degree angle on the circle. 45/360 = 0.125 which I can multiple by the total length of the path to find the point indicated in the second drawing.



So how do we find that angle? In turns out Raphael has a function that does this - angle(). Pass it two points and it will return the angle (0-360 degrees) the first point is relative to the second point on the imaginary circle. Divide the result by 360 and we have the percentage to multiply by the length to find where to position the slider knob.

All of this explanation turns into these few lines of code:


$shim.draggable({
drag: function ( e, ui ) {

// Find lines and then angle to determine
// percentage around an imaginary circle.
var t = ( Raphael.angle( ui.position.left+25, ui.position.top+25, mid.x, mid.y ) ) / 360;

// Using t, find a point along the path
pal = path.getPointAtLength( (t * len) % len );

// Move the knob to the new point
knob.transform( 't' + [pal.x, pal.y] + 'r' + pal.alpha );
},
stop: function ( e, ui ) {
$shim.css({ left: pal.x-25, top: pal.y-25 });
}
});


I enabled a jQuery UI Draggable on the DIV and implemented the drag callback to use the current position of the DIV to find the angle, get the point via getPointAtLength(), and then use a transform to position the slider knob. Notice that I also rotate the knob based on the value "alpha" return by getPointAtLength(). This value represents the tangent line at the current point on the path. It be used to angle an object to follow along with the curve of the path:





The final step in the drag operation is to move the proxy shim back over the knob so the next attempt to move the knob will trigger the movement logic.

This example highlighted one of the possible uses of the path functionality available in Raphael. I'm sure it only scratches the surface of some of the possibilities. What's nice about the implementation is that it is fast enough to keep up with the mouse move events triggered by the Draggable. I was a little concerned that the math operations involved might make the movement look choppy.

I did not implement the linear option in this demo. It is a different set of calculations to project the current mouse coordinates onto a line that represents the path and find the correct point based on that calculation. Additionally, a circular open path implementation would be interesting as well as some other variations. Those are all little exercises for another day.

Wednesday, October 31, 2012

Building SVG Paths with Raphaël

SVG enables building really complex vector graphics.  Most of that power comes from path shapes which are constructed through a series of special commands to define points in the shape and how to connect them.  Any primitive shape (ie circle, rectangle, line, etc) can be expressed as a path so paths could be universally used to draw all of the SVG shapes you define.  The problem is learning and reading the path strings - they can get quite cryptic.  Even if you use a library like Raphael, you still will need to know how to define paths using the SVG syntax.

If that's the case, then why even use Raphael?  Well, even though you'll still need to learn how to define paths, Raphael does have some functionality to make it easier to manage them.  As I was digging around the library, it became evident that many of the utilities in the library are designed to work with paths.  There are functions for primitive shapes like circles and rectangles which will generate those SVG objects, however, they will not be compatible with the path utilities.  As much as I've avoided learning the path commands, it seemed a good time to learn the basics and see how Raphael could help me leverage them.

Once you've defined your SVG canvas via the Raphael constructor, you can use it to define a path via the cleverly named path() function.  The documentation states this function takes a string that follows the same syntax as the standard SVG Path commands.  After reading the intro to that document, there's a few things to note:

  • There are only four basic commands - move, line, curve, and close path.  Some of these have specialized commands that make some assumptions thus simplifying the syntax.

  • Case matters - upper case uses absolute coordinates whereas lower case calculates actions relative to the last command's end point.

  • These commands have parallels to the Canvas API functions.  If you've used those, then the inputs to these commands might be easier to understand.

  • The SVG documentation states that commas are not necessary, however, the Raphael reference indicates that command parameters should be separated by commas.


One thing I noticed when working with the path() function is that it will also take an array of arrays that define the path.  So instead of writing "M450,175 l0,90 a 10,10,0,0,1,-10,10 l-390,0 a 10,10,0,0,1,-10,-10 l0,-190 a 10,10,0,0,1,10,-10 l390,0 a 10,10,0,0,1,10,10 l0,90 z" to create a rectangle with rounded corners, I can write:



path = paper.path( [
['M',450,175],
['l',0,90],
['a',10,10,0,0,1,-10,10],
['l',-390,0],
['a',10,10,0,0,1,-10,-10],
['l',0,-190],
['a',10,10,0,0,1,10,-10],
['l',390,0],
['a',10,10,0,0,1,10,10],
['l',0,90],
['z']
] );



This is much more readable and feels more like chaining function calls together to construct a path. As best I can tell, all of the path handling functions in Raphael use the array syntax to represent the path definition.

Although not documented (which means it might not be the same in future releases), there are functions that will create primitive shapes as paths. Inside the Raphael._getPath object, there are several functions that take similar arguments to the built-in shape functions:


path3 = paper.path( Raphael._getPath.circle({ attrs: { cx:120, cy:120, r:50 } }))
.attr( 'stroke-width', 3 )
.attr( 'stroke', 'rgb(80,80,255)' );


As a final example that illustrates the power of Raphael's path functions, I took a fairly complex path string and transformed it using Raphael.transformPath() prior to creating the SVG element:


pstr = "M295.186,122.908c12.434,18.149,32.781,18.149,45.215,0l12.152-17.736c12.434-18.149,22.109-15.005,21.5,6.986l-0.596,21.49c-0.609,21.992,15.852,33.952,36.579,26.578l20.257-7.207c20.728-7.375,26.707,0.856,13.288,18.29l-13.113,17.037c-13.419,17.434-7.132,36.784,13.971,43.001l20.624,6.076c21.103,6.217,21.103,16.391,0,22.608l-20.624,6.076c-21.103,6.217-27.39,25.567-13.971,43.001l13.113,17.037c13.419,17.434,7.439,25.664-13.287,18.289l-20.259-7.207c-20.727-7.375-37.188,4.585-36.578,26.576l0.596,21.492c0.609,21.991-9.066,25.135-21.5,6.986L340.4,374.543c-12.434-18.148-32.781-18.148-45.215,0.001l-12.152,17.736c-12.434,18.149-22.109,15.006-21.5-6.985l0.595-21.492c0.609-21.991-15.851-33.951-36.578-26.576l-20.257,7.207c-20.727,7.375-26.707-0.855-13.288-18.29l13.112-17.035c13.419-17.435,7.132-36.785-13.972-43.002l-20.623-6.076c-21.104-6.217-21.104-16.391,0-22.608l20.623-6.076c21.104-6.217,27.391-25.568,13.972-43.002l-13.112-17.036c-13.419-17.434-7.439-25.664,13.288-18.29l20.256,7.207c20.728,7.374,37.188-4.585,36.579-26.577l-0.595-21.49c-0.609-21.992,9.066-25.136,21.5-6.986L295.186,122.908z",

path1 = paper.path( Raphael.transformPath(pstr, 't-50,-60r125s0.5') )
.attr( 'stroke-width', 3 )
.attr( 'stroke', 'rgb(255,80,80)' );


There might not seem to be any benefit to doing this because you could just as easily create the element and then transform it once its attached to the DOM. However, some of the Raphael functions don't always account for the transform on the element. By altering the path prior to creating it, those functions will work as expected.

I've added the above examples to my sandbox. In my next post, I'll dig a little deeper into how to leverage some the other Raphael path functions to build something a little more complex.

Saturday, October 27, 2012

HTML5 Canvas and the Cracked Glass Effect - Revisited

After much toiling and tinkering, I think I have found a reasonably good method to produce a cracking glass effect. While not absolutely perfect, you can achieve some pretty interesting results. I used the same path construction as described in my original attempt. However, I broke the rendering into five different layers. Each layer adds a different dimension to the effect. These include refracting the image, adding reflections, the main cracking lines, fracture points, and some noise. In the demo I created, you can tinker with all the input options. The demo is on my sandbox and below are some examples I created with it.

The default settings will create something that looks like this first image. Since everything is random, you might have to draw the cracks a few times to find a variation you like.



I added a solid background to see what it would look like. Most of the samples out there have a black background so I wanted to see something to compare.



The white background is fun for playing with colors.



Most of the algorithm is finding places to draw lines.  All of the parameters of the lines are precalculated and each layer uses parts of those calculations to guide it in placing various canvas elements (lines, gradients, and curves) around those lines that were calculated as part of the path generation logic.

From a performance perspective, its not too bad.  Chrome seems to render the fastest.  The Noise layer can really slow it down because of the amount of tiny lines it can draw (depending on the decay and frequency selected).  Each layer can be completely disabled by turning the opacity all the way down (the show/hide button will render the layer but its just not visible).  Overall, there's a lot of Canvas API calls being generated and the rendering times are not unacceptable.   Its not something you'd want to try calling over and over again, but it can function in a real time setting.

I suppose the next task is to package it up into something that can actually be called on an image.  Maybe make it a jQuery plugin.  However, that's another project for another day ...

Friday, October 26, 2012

Thoughts on Extending jQuery UI Spinner: Adding a Slider Widget

I've been thinking more about the spinner and how to best extend its functionality beyond the basic options it currently offers.  I really like the simplicity of the current design and don't really want to disrupt that basic implementation. 

Most enhancements probably fall into two categories:


  • Additional visual components to assist in picking values. These might include a slider to quickly move to a different point in the valid range or a drop down menu with common choices.


  • Other formatted values that are incremental in nature.  Anything that can be represented as a number can be used with the spinner widget. Dates and times seem like excellent candidates to use with the spinner.



The question becomes how much functionality should be included/supported by the base spinner. The current approach seems to extend spinner to make more specialized versions. Although not in the current release, development on the timepicker includes a spinner to allow stepping through each part of the time value (hours, minutes, seconds). The same theme can be used to add extra visual controls to the spinner to provide alternative ways of selecting values.

Enforcing certain values in the field is a little bit more challenging. The primary design goal of the Spinner is to provide globalized formatting of numbers, etc. That validation is deferred to the Globalize library. For different formats, different values are allowable. Another widget in development right now - Mask - provides a means to add validation to any field. Combine a mask with the spinner and you get incremental stepping with the spin controls and enforcement of manually input numbers that agree with the selected locale.

I created an example that combines a slider and spinner. The code is not too complicated, however, you can see there is some work to ensure all the visual components stay in sync. Additionally, the demo uses the alternative layout enhancements I made to the spinner.

Keeping with the example set in the demos and development of jQuery UI, I created an extension to encapsulate the code required to draw and coordinate the spinner and slider controls. I called it SlideSpinner and you can see how it works on the project page. The implementation does not address restricting manually entered values. I did make an attempt, but to keep it generic enough to work with different locales, requires a lot more effort so it does not interfere with the users typing. However, I was able to get a feel for how to extend the functionality of a base widget to provided enhanced features like adding the slider. The SlideSpinner handles all the details of drawing the two widgets, positioning them, and then maintaining the values when the user interacts with either widget.

The jQuery UI library provides an excellent starting point for building customized widgets for more specific situations. Instead of bloating the Spinner widget with features that may only be useful in certain applications, it provides the basic framework to enable incremental controls that can be easily extended to create more specific enhancements as required. As long as the library includes appropriate hooks to build these extensions, then the available feature set is adequate to allow these new widgets to be constructed. As the library continues to mature, you can see that there will be a lot of extensible components that can be combined in specific ways to perform special tasks. Depending on the circumstances, you may need to build new widgets that extend the library or just combine the existing basic widgets in creative ways to build your solution.

Wednesday, October 24, 2012

Drawing Curves with the HTML5 Canvas quadraticCurveTo() Function

The HTML5 Canvas API has a built-in bezier function to draw smoothly curved lines.  However, it requires you to specify a control point to draw the line.  If you have points you want to draw the line through, you will need to find the control point to properly draw the line.  Visually, you can see on the left that I have three green points that I want to draw a curve through.   The red point represents the control point required to draw that curve through those points.  The question is: how do I find that point so I can draw the line I want using the Canvas quadraticCurveTo()? Intuitively, it doesn't seem too difficult. You can look at the points and see the relationship between the second point and the control point and see its about twice the distance from the line formed between the first and last points. Additionally, it appears to be on a certain angle from that line.

If we draw some lines on the sample, you can see that the control point is found by translating P2 up and over by L1 and L2.  The question is how do we find L1 and L2?  The problem can be broken down into two steps:

1) L1 is the length of the line that is perpendicular to P1,P3 and runs to P2.

2) L2 is the length of the line from the midpoint of P1,P3 to the intersection found in step 1.

The first step requires us to project P2 onto P1,P3 to find the intersection point.  Once we have that point, the two length are easy to calculate to perform the translation on P2 to determine the control point. Below is the implementation of this process:


function findControlPoint(s1, s2, s3)
{
var // Unit vector, length of line s1,s3
ux1 = s3.x - s1.x,
uy1 = s3.y - s1.y,
ul1 = Math.sqrt(ux1*ux1 + uy1*uy1)
u1 = { x: ux1/ul1, y: uy1/ul1 },

// Unit vector, length of line s1,s2
ux2 = s2.x - s1.x,
uy2 = s2.y - s1.y,
ul2 = Math.sqrt(ux2*ux2 + uy2*uy2),
u2 = { x: ux2/ul2, y: uy2/ul2 },

// Dot product
k = u1.x*u2.x + u1.y*u2.y,

// Project s2 onto s1,s3
il1 = { x: s1.x+u1.x*k*ul2, y: s1.y+u1.y*k*ul2 },

// Unit vector, length of s2,il1
dx1 = s2.x - il1.x,
dy1 = s2.y - il1.y,
dl1 = Math.sqrt(dx1*dx1 + dy1*dy1),
d1 = { x: dx1/dl1, y: dy1/dl1 },

// Midpoint
mp = { x: (s1.x+s3.x)/2, y: (s1.y+s3.y)/2 },

// Control point on s2,il1
cpm = { x: s2.x+d1.x*dl1, y: s2.y+d1.y*dl1 },

// Translate based on distance from midpoint
tx = il1.x - mp.x,
ty = il1.y - mp.y,
cp = { x: cpm.x+tx, y: cpm.y+ty };

return cp;
}


Here, points are described by objects with a x and y property (ie. { x: 150, y: 210 }). To use the function, just pass in the points you want used to draw the curve and use the returned control point in the quadraticCurveTo() function:


function drawCurve($canvas, p1, p2, p3)
{
var ctx = $canvas[0].getContext('2d'),
cp = findControlPoint(p1, p2, p3);

ctx.strokeStyle = 'black';
ctx.strokeWidth = 1;

ctx.beginPath();
ctx.moveTo(p1.x, p1.y);
ctx.quadraticCurveTo(cp.x,cp.y,p3.x,p3.y);
ctx.stroke();
}


I created a demo in my sandbox to allow moving the points around and visually see the resulting control point and curve.

There is a small caveat to keep in mind - the second point won't always be on the extreme point of the curve. You can see this best by moving the point further to the side by the start/end points. I believe this has to do with the function assuming that the second point is at the midpoint of curve (in terms of the parametric function, this is t=0.5). Unfortunately, you have no control over that selection. One way to work around it is to use path interpolation to draw the curve.

The demo draws the same curve using the PathJS library which will put the second point much closer to the extreme of the curve. This might cause some sharper angles in some cases but will put the second point very close to where the curve changes directions. The image on the left uses the same points as the example above but used PathJS to interpolate the path.

With the examples here, you can now draw bezier curves with whatever information you have available. If you already have the control point, then you're all set. If not, the function presented here will provide an accurate point to use as the control point required by the quadraticCurveTo() function. The same basic approach can be extended to find two control points that are required in the cubicCurveTo() function as well.

Thursday, October 18, 2012

Exploring the New jQuery UI Spinner - Beyond the Basics

jQuery UI 1.9 is out and with it a bunch of new goodies to play with. I thought I'd start by digging into the new Spinner widget and see what kind of features it offers. Very simply, Spinner adds a up/down arrow to the left of a input box thus allowing a user to increment/decrement a value in the input box. It adds keyboard support so you can use up/down arrows and page up/down to move through values. It also has a step feature to skip values. In addition to the basic numeric features, it also enables globalized formatting options (ie currency, thousand separator, decimals, etc.) thus providing a convenient internationalized masked entry box.  The demo on the jQuery UI site even extends Spinner to enable time aware "spinning".

As I started using the widget, I noticed it scaled nicely with different sized input fields.  The CSS enables you to use any font size in the input field and still maintain a visually appealing layout.  The optional min/max values provide a way to prevent the spinner from going outside a valid range.  However, the min/max controls the spinning functionality - you can still type a invalid value into the box and it won't revert the entry.  It seems that this would be important feedback to the user that they manually entered a value that the spin control would not allow.  Additionally, the spin controls can only be placed on the left of the input box.  In some situations, it may be desirable to have the buttons above and below the input box.

In light of this, I thought I'd try extending the Spinner widget to add some of these enhancements.   I created a demo that implements the min/max constraint on entries manually entered in the input box by the user and also provides a means to move the spin buttons to be positioned above and below the input box.  The screenshot on the left shows the default field and how it looks compared to two different examples of a top/bottom alternative.  The last example extends the base Spinner widget to allow it to step through the alphabet.

Top/Bottom Spin Buttons



I started by inspecting the base Spinner widget's CSS properties.  I was curious how difficult it would be to shift the UI components around to achieve this look without having to change any functionality.   The input field is wrapped in a span and then the buttons are added to this wrapper.  CSS controls the positioning of the buttons to align them to the right.  To move them, I just needed to override the default CSS with rules that would position the buttons above and below the input.


.c-topbottom .ui-spinner-input {
margin: 0;
margin-top: 10px;
margin-bottom: 10px;
text-align: center;
}

.c-topbottom .ui-spinner-button {
height: 10px;
left: 0px;
width: 100%;
}

.c-topbottom a.ui-spinner-button {
border: none;
}

.c-topbottom .ui-spinner .ui-icon {
margin-left: -7px;
top: 5px;
left: 50%;
}


These rules follow along with the original definitions but change the necessary properties to position the buttons above and below the input field.

Those changes move the buttons to the correct place, however, if you zoom into the page, the top/bottom corners of the buttons are not rounded.  This is because they are squared off in the base widget to fit nicely against the right side of the input box. However, now they need to be rounded to look correct in their new home.  Looking at the generated HTML, the corners are rounded using a CSS class assigned to the anchor tag that represents the button (ui-corner-tl and ui-corner-bl).   These two classes only round the left corners not the right corners.  I could have just added a corner radius rule to my new CSS but then there would be a chance that a future change to the other jQuery UI ui-corner-* rules would make my custom rule break and not look right.  Instead, I'd like to just add the ui-corner-tr and ui-corner-br to the button elements. This required some Javascript to call jQuery.addClass() on the elements after creating the widget:


$('#topbottom input').spinner()
.parent()
.find('.ui-spinner-up')
.addClass('ui-corner-tl')
.end()
.find('.ui-spinner-down')
.addClass('ui-corner-bl');


So after making the CSS tweaks and adding the new classes, the spin buttons are now in the correct place and format.

Extending Spinner



I wanted to try extending the Spinner widget to enable it to understand how to spin through the alphabet. Since each letter is just an sequential ASCII code, it should be fairly simple to add logic to convert to/from the string/numeric representation so the spinner can iterate through the letters. Working from the example time extension source found on the jQuery UI site, I came up with the following implementation:


$.widget( "ui.alphaspinner", $.ui.spinner, {
options: {
max: 'Z',
min: 'A'
},

_create: function( ) {

this._super();

// Make this a top/bottom spinner. Add rounded corners.
this.uiSpinner
.addClass('ui-spinner-alpha')
.find('.ui-spinner-up').addClass('ui-corner-tl').end()
.find('.ui-spinner-down').addClass('ui-corner-bl');

},

_parse: function( value ) {
if ( typeof value === "string" ) {
// Only one letter is valid
if (value.length > 1) {
return "";
}
return value.toUpperCase().charCodeAt(0);
}
return value;
},

_format: function( value ) {
return String.fromCharCode(value);
}

});


Here I added an override for _create to add classes to the base Spinner so my widget will automatically have the spin controls above and below the input box. Additionally, _parse() is used to convert from the value in the input box so it can be manipulated by the spin control and _format() is used to convert back to the value displayed in the input field. Overriding those two functions is all that is needed to enable the remaining spinner widget to understand how to "spin" through the alphabet.

Validating Manual Input



So far, the new widget does not address validating manual entry in the field to ensure it is A) a single valid letter, and B) inside the range specified by min/max. The _parse() function does parse strings that are only 1 character long and will convert everything to uppercase. However, this only affects the internal representation and not the value in the input box. The user is not provided any feedback that they are entering bad values.

Looking at the example, I output the value of the three controls to the right of the widgets. I first attempted to enter a letter into the box. Since this widget only wants numeric values, the internal value will be null. You can see that the output on the right does not show the "j" because of this parsing.



Next, the field only is suppose to accept 0-9 as a valid input range. However, I can type a 12 into the box and it will accept the value and it will appear in the output on the right.



In my extension, I added an override to the _stop() function to check the value to ensure it is one that parses correctly. _stop() is called on each keyup event so it seemed like a good place to add this code. There are probably half a dozen other ways to add this logic. In the end, this is where I decided to place it:



_stop: function( event ) {
var value = this.value() || 0;

if (event.type == 'keyup' && value != this._adjustValue(value) ) {
this.value(this.previous);
this._trigger( "invalid", event );
}
else {
this.previous=value;
this.element.val(this._format(value));
}

this._super(event);
}



The _adjustedValue() function is used in a similar fashion when the spin functionality occurs. If the adjusted value is not the same, something is wrong, so set the value back to the previous value. I also trigger a custom "invalid" event that can be caught and an error/help box could be shown to guide the user to enter a correct value. Additionally, this code writes the formatted value back to the input box so if you type a lower case "g", the input will update with a upper case "G" for consistency.

The full source and demo is in my sandbox. Some of these features seem like them might be a good addition to the base Spinner widget. Enabling multiple layouts and checking manual entries seem like good options that can make building solutions with the widget a little bit easier.