Monday, October 22, 2012

Compass / SASS for Placing Elements on a Circle

Need to put some elements on a circle?  Already using Compass / SASS to compile your CSS?

Woot!  Compass has you covered with trigonometric sine and cosine functions and of course the SASSy @for iterator helps, too.

This jsfiddle demonstrates the compiled result.

Your HTML document is composed of a container for the circle, an element for the circle, a container for items placed on the circle, and the items which will appear on the circle.

Your SASS file is composed of the following.  Modify the $positions, $ringSize, and $itemSize variables according to your needs:

// Use either Compass' border-radius mix-in or prefixfree plugin
@import "compass/css3/border-radius";

/**
 * on-circle takes a radius, position on a circle, number of possible positions and returns top and left properties
 *
 * $radius {Number}    radius of the circle in pixels
 * $ordinalPosition {Number} the position of the item on the circle, counting from 1 (North) through N
 * $positions {Number}   the number of positions on the circle
 * $originX {Number}   optional X origin point for the circle, defaults to the radius
 * $originY {Number}   optional Y origin point for the circle, defaults to the radius
 * $offsetX {Number}   optional X offset for the item, you might use 1/2 of the width of an item, default is 0
 * $offsetY {Number}   optional Y offset for the item, you might use 1/2 of the height of an item, default is 0
 */
@mixin on-circle ($radius, $ordinalPosition, $positions, $originX: $radius, $originY: $radius, $offsetX: 0, $offsetY: 0) {

 /*
 Determine the angle for the position:
  Multiply the adjusted zero-based index of the position by the degrees-per-position (360 degrees divided by the 
  number of positions) and subtract 90 degrees (adjusting to begin at North)
  */
 $positionAngleDegrees: ($ordinalPosition - 1)*360/$positions - 90;
 /*
 Convert the angle to radians:
  Multiply the angle by pi and then divide by 180 degrees.

 NOTE: This step is necessary because of a bug in handling of the degrees unit when doing iterations, AFAICT
  */
 $positionAngleRadians: $positionAngleDegrees * pi() / 180;

 /*
 Apply the parametric equation of the circle,
  http://en.wikipedia.org/wiki/Circle#Equations
 via:
  http://stackoverflow.com/questions/839899/how-do-i-calculate-a-point-on-a-circles-circumference

  x = [origin x] + (r * cos angle)
  y = [origin y] + (r * sin angle)
  */
 top: #{$originY + $offsetY + $radius * sin($positionAngleRadians)}px;
 left: #{$originX + $offsetX + $radius * cos($positionAngleRadians)}px;

}

// How many positions will there be on the circle?
$positions: 13;

// How big is the circle (diameter)?
$ringSize: 180;

// How big is an item?  Offsets and margins are based on this value
// such that the center of an item is located on the circle, rather than
// the top left point
$itemSize: 28;

body {
 margin: 50px;
}

.ring-container {
 border: solid 1px black;
 display: inline-block;
}

.ring {
 position: relative;
 // Use border-box so that margin and border are not included and the size of the element containing the
 // rendered circle is the circle size + border + ...
 box-sizing: border-box;
 width: #{$ringSize}px;
 height: #{$ringSize}px;
 margin: #{$itemSize/2}px;
 border: solid 1px black;

 // Make any square into a circle by setting the border radius to it's full width/height
 @include border-radius(#{$ringSize}px);
}

.ring-positions {
 position: relative;
 width: #{$ringSize}px;
 height: #{$ringSize}px;
 top: #{$itemSize/-2}px;
 left: #{$itemSize/-2}px;
}

.ring-position {
 position: absolute;
 width: #{$itemSize}px;
 height: #{$itemSize}px;
 @include border-radius(#{$itemSize}px);

 // Use box-sizing: border-box if you'll have a border
 box-sizing: border-box;
 border-color: black;
 border-style: solid;
}

@for $i from 1 through $positions {
 .ring-position-#{$i} {
  @include on-circle($radius: $ringSize / 2, $ordinalPosition: $i, $positions: $positions);

  // Just for demonstration of placement
  border-width: #{$i/2+3}px;
 }
}

Thursday, August 16, 2012

html5 boilerplate build Woes

On Friday I spent a few hours trying to debug doing a build on my current project using the html5 boilerplate build script (ant based).  Didn't happen.

Spent a couple more hours on Monday modifying my project to mirror the configuration for asset directories that the build script uses as its defaults, such as "js" for the scripts directory, where before I was using "javascripts" based on the html5 boilerplate template that I installed with (I forget which) Scout or compass.app.  Thought it would build after changing to the structure it expects / expected.  Didn't happen.

Well, the woes seem to be caused by changes to the default structure that have happened at the same time as the splitting of the build script out into it's own project / repository.

Today I put a little bit more time into the build and got it working by removing my exclusions from the project.properties file and adding the slug.libs property in.

So that's the deal ... if you're going to use something other than "vendors" as your libs directory, you *must* specify not only the dir.js.libs property but the slug.libs property.

Here are the settings I have in my project properties, which seem to match the html5 boilerplate template:

dir.source = ./public
file.stylesheets  = 
dir.js = js
dir.js.main = ${dir.js}
dir.js.libs = ${dir.js}/libs
slug.libs = libs
dir.js.modules = ${dir.js}/modules
dir.css = css
dir.images = img
file.root.stylesheet = style.css
file.root.script = script.js

Good luck with your html5 boilerplate project!

UPDATE:

I also had issues with the error "js/modules/_all.js was specified as an input resource."
I could get past the error by doing an "ant clean" before the "ant build", but obviously the error means there's a problem.

To get past it, modify your build.xml around line 486 to fix the module concatenation which is used to create a checksum, as follows, adding an exclusion for _all.js.

<concat destfile="./${dir.intermediate}/${dir.js.modules}/_all.js" overwrite="no">
    <fileset dir="./${dir.intermediate}/${dir.js.modules}/">
        <include name="*.js">
        <exclude name="_all.js">
    </exclude></include></fileset>
</concat>


UPDATE the Second:

Subdirectories of /libs are not being copied.  :/  This might help ... testing it now...

<copy todir="${dir.publish}/${dir.js}">
  <fileset
        dir="${dir.intermediate}/${dir.js}"
        includes="${file.js.bypass}, ${slug.libs}/*/**, ${slug.modules}/*/**">
        <exclude name="scripts-concat.js"/>
        <exclude name="scripts-concat.min.js"/>
        <exclude name="otherscripts-concat.js"/>
        <exclude name="plugins.js"/>
        <exclude name="${file.root.script}"/>
    </fileset>
    <regexpmapper from="^([^/])*/(.*)$$" to="\1/\2" handledirsep="true"/>
</copy>

Looks good. That's a change for the task around line 532 of build.xml.

Wednesday, August 15, 2012

Making a Compass Site Relative

I'm creating what at least for now is a single-page webapp and using, among other things, Compass.

One of the extensions I'm using (via Compass.app) is thomas-mcdonald-bootstrap-sass.  I've modified it to use the Font-Awesome icon font which replaces and extends the standard twitter icon library, as well as to add an IcoMoon icon font for even more icons.

To add the icon fonts to the compass bootstrap extension, you need to specify the path to the font.  In the instructions, you're told to put the absolute path to the font (such as "/fonts/fontawesome".  But we don't want to do that, we want to have all relative paths in our files.  If we don't include a beginning "/" in the $fontAwesomePath variable, then Compass will assume that the path is relative to the compass project's font directory (because of how the font-file function is coded).

The default font directory is the css directory plus "/fonts".

That made things awkward for using relative paths, so I did some digging and found a post where someone mentions using "font_dir" to configure the font directory path ... well, it's "fonts_dir", folks, not "font_dir".

To make a long story short, using the image-url function for my image paths, and trying to remove the absolute pathing by changing "http_path" to "" didn't work.

These are the settings in my config.rb that *did* work:

http_path = "./"
css_dir = "css"
sass_dir = "sass"
images_dir = "../img"
javascripts_dir = "js"
fonts_dir = "../fonts"

Of course you should modify that to use the same paths that you're using, such as "javascripts" and "stylesheets" instead of "js" and "css".  Just make sure that you have "./" as the http_path and "../" in front of your images and fonts directory paths.  When those are used, it's in the CSS context and your generated CSS file is going to end up in a directory that's parallel to them.

If somehow your final CSS file is in a different place, make sure that the images and fonts variables are changed accordingly.

Monday, July 23, 2012

Pandora, Stardock Objectdock, Large Number of Chrome Processes

I've been using Stardock's Objectdock for a while, because I'm now coding on a Windows 7 machine.  I also have a subscription to Pandora One ... at some point I saw about a bajillion chrome processes coming up in the dock and making it useless.

Turns out that it's because of Pandora One.  Chrome is my default browser and it seems like Pandora is using it and not closing the handle or something.  Who knows.  Problem solved by not using their standalone Adobe AIR application and going back to having one window open for listening.

UPDATE:  Apparently you have to start Pandora One BEFORE you start Chrome and the problem will not occur.  (Thanks to Pandora tech support for providing the workaround)