Stars rating system: CFIMAGE, ImageNew and ImagePaste Posted by Mischa S.

Back

Date: February 16, 2012

So my challenge today was to create a report for some of our sister company's customers so they could see the output of some of the satisfaction surveys they have been conducting.  They really want a graphical way to respresent a score, instead of giving it a grade or numerical value.  Since their rating scale was using 1-5, we decided to use stars.  Everyone likes a gold star after all?  Actually, they wanted orange stars.  I fought for gold, but I was overruled.

So, I made the report using FW/1 (as the framework), Coldfusion (naturally) and Rateit.  Rateit is a nifty jQuery plugin, that can allow your users/customers to rate stuff using stars (a la Netflix/Amazon/etc).  I demo'd the report and everyone was thrilled and they really liked the orange stars (no counting for taste).  Just before the meeting was over, they asked where the export feature was....doh!

I set about making an export feature using cfdocument/cfpdf.  This was working great until I came to the understanding that jQuery wasn't gonna render those wicked awesome ORANGE stars for me....enter cfimage/ImageNew/ImagePaste.

I'll show the code now, as I'm sure you're tired of reading my ranting:

<!---set the maximum number of stars in your image, 5 in my sample--->
	<cfset maxstars = 5 />
	
	<!---get your stars image--->
	<cfimage source="#ExpandPath("/")#images/starcuts.png" name="teh_stars">
	
	<!---create my offset.  This assigns a numerical value for each pixel in width.--->
	<cfset offset = teh_stars.width/maxstars />
	
	<cfscript>
		public function build_image(rating,height,width,offset,teh_stars){
			
			width = 0;//init local variables
			
			width = offset*rating;//calculate the width based on the rating times the offset
			
			teh_container = ImageNew("",80,height,"argb","CCCCCC");//create a container the same size as the stars
			teh_background = ImageNew( "",width,height,"rgb","EF7629" );//then create the orange background
			
			ImagePaste(teh_container,teh_background,0,0);//merge those two
			
			ImagePaste(teh_container,teh_stars,0,0);//merge with the stars
				
			return teh_container;
		}
	</cfscript>
	<cfoutput>
		<style type="text/css">
		div.score {
			margin:0;
			padding:0;
			width:#teh_stars.width#px;/* same as my image */
			height:#teh_stars.height#px;/* same as my image */
		}
		</style>
			
		<!---create some ratings--->
		<cfset ratings = [4.75,3.4,4.3,2] />
			
		<!---loop over the ratings array--->
		<cfloop from="1" to="#ArrayLen(ratings)#" index="i">
			
			<div class="score" id="rating#i#">
				<cfimage action="writeTobrowser" source="#build_image(ratings[i],teh_stars.height,teh_stars.width,offset,teh_stars)#">
			</div>
			<br/>
		</cfloop>
		
	</cfoutput>

First 3 things we do:

  1. Set the max number of stars a rating can attain
  2. Read in our stars image (Note: this links to a white image with transparent stars, don't be confused if you can't see anything in your browser window)
  3. Calculate how many pixels each rating point counts as

Next, we create a function that does the following:

  1. Sets width to zero and then calculate the middle orange layer of our image
  2. Create our bottom layer.  This will be the same width and height as our star image
  3. Create our middle layer.  This will fill up the stars with orange, as the inside of the stars on the top layer image are transparent
  4. Merge those layers
  5. Now, merge in the stars
  6. Return teh_container

Set some styles for the div that the image will be stored in.  You could probably skip that if you wanted to.

Create an array of example ratings to loop over.

Now, loop over a cfimage tag that will immediately 'writeToBrowser' and call the function we wrote earlier to return the teh_container object!