Balloon Room

Table of Contents

  1. Introduction
  2. Design
  3. Development
  4. Custom CMS
  5. XHTML & CSS
  6. JavaScript

Details

  • Date Completed:11 February 2008
  • Days/hours spent:10 days
  • About the company:Balloon Room
  • Software used:Photoshop CS3, Illustrator CS3, Textmate
  • Tag:CMS, CSS, Framework, Javascript, MySQL, PHP, PSD, XHTML

Currently working on…

Lots of freelance and e-commerce projects.

Latest Work

Search Portfolio

Designed and developed this site for Balloon Room, using my dust framework and all XHTML, CSS and JavaScript was handed coded.

1. Introduction

Top of page

Professionally trained NABAS balloon decorators came to me about setting up a website for them. I was shown some pictures and given some text to think about the image for the site as well as a brochure that was designed for them.

2. Design

Top of page

What instantly came to mind was balloons floating up into the sky – like one would see countless times on TV. I pictured a blue sky, clouds and the website being taken up. I drew a couple of sketches of what I thought it could like but sadly I can't show them here as they've been misplaced.

Their main colours were pink and gold. I loved the idea of making a colourful like this, as most of the time I've been subjected to endless alterations to what I believed was a perfect design.

Initial designs

I went through several designs with different shapes, clouds and balloons. I seem to put a lot into the design at first. I learnt that after I finish a design I should see what I could remove to simplify it. Initially I had loads of balloons tied up and pulling the site upwards. I realised it made the top heavy and so stuck with two balloons (pink and gold-yellow colour) for each hole.

Concept design of balloon room home pageConcept design of balloon room home page

I also came up with an alterative design in case the client didn't like the one I so instinctively visualised.

Alternative concept design of balloon room home pageAlternative concept design of balloon room home page

The client really liked the first concept (but of course!) and I was pleased as that's how I saw the website would look best.

Further concepts

I could now develop the design the client chose. The other pages would have a two column layout, where trying to promote or highlight their services and work.

Concept design of balloon room services pageConcept design of balloon room services page

Concept design of balloon room work entry pageConcept design of balloon room work entry page

3. Development

Top of page

In terms of development, the site only needed a couple of pages with a good CMS which controlled the work entries as well as pages.

Dust framework

I used my dust framework to develop the front and back end of the site. Dust is a PHP framework designed and developed from the MVC methodology (thank you RubyonRails and CodeIgniter) in case you didn't know.

I took code from the previous project Annie which had a similar type of pages but I wanted to improve the functionality.

CSS into one file

I was reading the book High Performance Web Sites: Essential Knowledge for Front-end Engineers and I learnt a lot of good things that a site change to dramatically improve its performance. One chapter it talked about putting all your CSS into one file. I've come along way and realised that putting all your CSS into one file was nasty for updating and debugging. I thought about how PHP could do what the book suggested. I began writing some code to see how I could make my modular CSS into one file. After a couple of days of experimenting, I added my CSS caching functions to my XHTML framework class. The CSS caching functions basically read through the file and searched for any @import url('file.css');. Then through a regular expression I was able to import the file.css into the file it was being imported into and cache it. The cache function would check to see if the file had been modified since it was last cached and therefore was rerun every day or so.

Minify JavaScript

I downloaded a PHP class for reducing whitespace inside a JavaScript. Any JavaScript using dust's add_js_file() function would be minfied, gzipped and cached. Reducing the amount of data the user would have to download.

4. Custom CMS

Top of page

By using dust it was easy enough to create a custom built content management system. Using a pre-built one would be complex and I'd have to train the client. The CMS consisted of two sections, Work and Pages.

Screenshot of admin site - adding a work entryScreenshot of admin site – adding a work entry

Screenshot of admin - editing a work entryScreenshot of admin site – editing a work entry

Screenshot of admin site - editing pageScreenshot of admin site – editing page

Tiny MCE, FCK Editor...

After a lot of contemplation, I realised that sometimes adding more technology won't improve a user's experience but might hinder it instead. I thought "What's the most they'll need?" Simple paragraphs and if she wanted more that could be implemented at a later stage. So I created a simple regular expression to find two line breaks in a row (using PHP preg_replace() function to find and replace the line breaks preg_replace('/[\n|\r]{2,}/', '</p><p>', $string) I know its not a fancy expression but it works) and apply end and beginning paragraph tags when the portfolio entry was read.

MySQL tables

I don't like using IDs to link database tables together, as numbers are lost when one deletes a row in the table (because of auto_increment feature). All the rows had title primary keys and were dependent on the title. To access these a second field called query was created to find a specific row. The 'query' was a parsed version of the title field with all characters, expect letters, numbers, underscores and hyphens removed (regular expression [^a-zA-Z0-9_-]+).

Images

I created an image class that resizes the images to the specific thumbnail size and the large ones were restricted to 700px width or height.

I discovered that images would need to be resized to the thumbnail size, wouldn't always work because of their dimensions. Therefore, I setup the function for resizing, to find out if both width and heights parameters were set. If so, then this would mean the image had to cropped. So I scaled the image proportionally according to whether the width or height was larger. I found that getting that scaled image and then cropping it produced better results.

5. XHTML & CSS

Top of page

All XHTML, CSS and JavaScript was hand coded.

6. JavaScript

Top of page

Lightview

I decided to use Prototype and script.aculo.us JavaScript libraries along side with lightview (v2.0.0). I was going to use jQuery and Thickbox combo, but I found lightview to be slightly slicker when opening up an image. And first impression was all-important for for the client. A good feature of lightview is that the images are scaled according to the user's resolution. So users won't have to scroll to view the image like with other gallery JavaScript libraries.

Cropping images

I've always wanted to add this feature into my dust framework. As many people don't have the time to learn a program that allows them to crop images and then upload them again. I finally found a JavaScript library (which used Prototype and script.aculo.us) called Cropper and since I was already using Prototype it was perfect match. One thing I do before I implement a JavaScript library is to make sure it works on Safari, Firefox, Opera, Internet Explorer 6 and Internet Explorer 7. This way I know the developer has taken time to create something worthwhile.

The script was pretty straightforward to implement. Although it took some time for me to create the PHP code, which would crop large images as well as thumbnails by coordinates. I got there in the end!

PHP code inside a class:

var $resource;

function jpg($array) {

extract($array, EXTR_OVERWRITE);

// if a width or height is specified inside array
if ( isset($width) || isset($height) ) {

if ( $width && !$height ) {
$height = round($source_height * ($width / $source_width)); // source height * (propotion ratio of width)
}
else if ( $height && !$width ) {
$width = round($source_width * ($height / $source_height)); // source width * (propotion ratio of height)
} //else

// create a new image
$this->_new_image($width, $height);

if ( (!$height && $width) || (!$width && $height) ) {
// resize new image
ImageCopyResampled($this->resource, $source_image, $target_x, $target_y, $source_x, $source_y, $width, $height, $source_width, $source_height);
}
// crop by source coordinates
else if ($source_x != 0 || $source_y != 0 ) {

ImageCopyResampled($this->resource, $source_image, $target_x, $target_y, $source_x, $source_y, $source_width, $source_height, $source_width, $source_height);

}
else {

// proper way to crop
// first we find out which is bigger, width or height
// so we rescale the image in proportion
// then get the rescaled image
// now crop the image according the width and height set in the array

if ( $width > $height ) {
$height = round($source_height * ($width / $source_width)); // source height * (propotion ratio of width)
}
else if ( $height > $width ) {
$width = round($source_width * ($height / $source_height)); // source width * (propotion ratio of height)
} //elseif

// rescale first
ImageCopyResampled($this->resource, $source_image, $target_x, $target_y, $source_x, $source_y, $width, $height, $source_width, $source_height);

// save new thumbnail
ImageJPEG($this->resource, $target_path.$target, $quality);
// save new thumbnail
//ImageDestroy($this->resource);

// get new thumbnail
$rescaled_image = ImageCreateFromJPEG($target_path.$target);

// now crop
ImageCopyResampled($this->resource, $rescaled_image, $target_x, $target_y, $source_x, $source_y, $width, $height, $width, $height);
} //else

} else {

// create a new image
$this->set_resource($source_image);

} //else

// if resource is changed via array
if ( isset($resource) ) $this->set_resource($resource); //if

// save new image
ImageJPEG($this->resource, $target_path.$target, $quality);

// destroy new image
ImageDestroy($this->resource);

// destroy source image
@ImageDestroy($source_image);
}

function _new_image ($width, $height, $mode = 'truecolor')
{
if ( $mode == 'truecolor' ) {
return $this->set_resource(ImageCreateTrueColor($width, $height));
} else {
return $this->set_resource(ImageCreate($width, $height));
} //else
}

function set_resource ($new_resource)
{
$this->resource = $new_resource;
}

Reorder images

Using script.aulo.us library I was able to add the ability to reorder images by simply dragging and dropping where the user wanted the image to appear.

var handler = function(e)
{
if ($('images-list')) {
Sortable.create('images-list',{
tag:'li',
onUpdate: function() {
new Ajax.Request(ADMIN_ROOT+'/update_order_of_images/'+PORTFOLIO_ENTRY_QUERY, {
asynchronous:true,
evalScripts:true,
onComplete:function(request){
new Effect.Highlight("images-list",{});
},
parameters:Sortable.serialize("images-list")
});
}

});
}
}

Event.observe(window, 'load', handler, false);

The function would make the element with an id="images-list" sortable where the tag would be a list item <li>. An AJAX call would be sent to a page called update_order_of_images when the sortable list was updated. The update_order_of_images page would simply take the array posted and loop through the images setting the order by the order they came in the array.

PHP code example:

foreach ($_POST['images-list'] as $order => $image_id) {
mysql_query('INSERT INTO `TABLE` WHERE `order_of_image` = "{$order}" AND `image_id` = "{$image_id}";');
}