@@ -38,7 +38,7 @@ function Winwheel(options, drawWheel)
3838 'outerRadius' : null , // The radius of the outside of the wheel. If left null it will be set to the radius from the center of the canvas to its shortest side.
3939 'innerRadius' : 0 , // Normally 0. Allows the creation of rings / doughnuts if set to value > 0. Should not exceed outer radius.
4040 'numSegments' : 1 , // The number of segments. Need at least one to draw.
41- 'drawMode' : 'code' , // The draw mode. Possible values are 'code' and 'image'. Default is code which means segments are drawn using canvas arc() function.
41+ 'drawMode' : 'code' , // The draw mode. Possible values are 'code', 'image', 'segmentImage '. Default is code which means segments are drawn using canvas arc() function.
4242 'rotationAngle' : 0 , // The angle of rotation of the wheel - 0 is 12 o'clock position.
4343 'textFontFamily' : 'Arial' , // Segment text font, you should use web safe fonts.
4444 'textFontSize' : 20 , // Size of the segment text.
@@ -182,7 +182,7 @@ function Winwheel(options, drawWheel)
182182
183183 // ------------------------------------------
184184 // On that note, if the drawMode is image change some defaults provided a value has not been specified.
185- if ( this . drawMode == 'image' )
185+ if ( ( this . drawMode == 'image' ) || ( this . drawMode == 'segmentImage' ) )
186186 {
187187 // Remove grey fillStyle.
188188 if ( typeof ( options [ 'fillStyle' ] ) === 'undefined' )
@@ -237,6 +237,23 @@ function Winwheel(options, drawWheel)
237237 if ( drawWheel == true )
238238 {
239239 this . draw ( this . clearTheCanvas ) ;
240+ }
241+ else if ( this . drawMode == 'segmentImage' )
242+ {
243+ // If segment image then loop though all the segments and load the images for them setting a callback
244+ // which will call the draw function of the wheel once all the images have been loaded.
245+ winwheelToDrawDuringAnimation = this ;
246+ winhweelAlreadyDrawn = false ;
247+
248+ for ( y = 1 ; y <= this . numSegments ; y ++ )
249+ {
250+ if ( this . segments [ y ] . image !== null )
251+ {
252+ this . segments [ y ] . imgData = new Image ( ) ;
253+ this . segments [ y ] . imgData . onload = winwheelLoadedImage ;
254+ this . segments [ y ] . imgData . src = this . segments [ y ] . image ;
255+ }
256+ }
240257 }
241258}
242259
@@ -350,6 +367,25 @@ Winwheel.prototype.draw = function(clearTheCanvas)
350367 this . drawSegments ( ) ;
351368 }
352369 }
370+ else if ( this . drawMode == 'segmentImage' )
371+ {
372+ // Draw the wheel by rendering the image for each segment.
373+ this . drawSegmentImages ( ) ;
374+
375+ // If we are to draw the text, do so before the overlay is drawn
376+ // as this allows the overlay to be used to create some interesting effects.
377+ if ( this . drawText == true )
378+ {
379+ this . drawSegmentText ( ) ;
380+ }
381+
382+ // If image overlay is true then call function to draw the segments over the top of the image.
383+ // This is useful during development to check alignment between where the code thinks the segments are and where they appear on the image.
384+ if ( this . imageOverlay == true )
385+ {
386+ this . drawSegments ( ) ;
387+ }
388+ }
353389 else
354390 {
355391 // The default operation is to draw the segments using code via the canvas arc() method.
@@ -427,6 +463,109 @@ Winwheel.prototype.drawWheelImage = function()
427463 }
428464}
429465
466+ // ====================================================================================================================
467+ // This function draws the wheel on the canvas by rendering the image for each segment.
468+ // ====================================================================================================================
469+ Winwheel . prototype . drawSegmentImages = function ( )
470+ {
471+ // Again check have context in case this function was called directly and not via draw function.
472+ if ( this . ctx )
473+ {
474+ // Draw the segments if there is at least one in the segments array.
475+ if ( this . segments )
476+ {
477+ // Loop though and output all segments - position 0 of the array is not used, so start loop from index 1
478+ // this is to avoid confusion when talking about the first segment.
479+ for ( x = 1 ; x <= this . numSegments ; x ++ )
480+ {
481+ // Get the segment object as we need it to read options from.
482+ seg = this . segments [ x ] ;
483+
484+ // Check image has loaded so a property such as height has a value.
485+ if ( seg . imgData . height )
486+ {
487+ // Work out the correct X and Y to draw the image at which depends on the direction of the image.
488+ // Images can be created in 4 directions. North, South, East, West.
489+ // North: Outside at top, inside at bottom. Sits evenly over the 0 degrees angle.
490+ // South: Outside at bottom, inside at top. Sits evenly over the 180 degrees angle.
491+ // East: Outside at right, inside at left. Sits evenly over the 90 degrees angle.
492+ // West: Outside at left, inside at right. Sits evenly over the 270 degrees angle.
493+ var imageLeft = 0 ;
494+ var imageTop = 0 ;
495+ var imageAngle = 0 ;
496+
497+ if ( seg . imageDirection == 'S' )
498+ {
499+ // Left set so image sits half/half over the 180 degrees point.
500+ imageLeft = ( this . centerX - ( seg . imgData . width / 2 ) ) ;
501+
502+ // Top so image starts at the centerY.
503+ imageTop = this . centerY ;
504+
505+ // Angle to draw the image is its starting angle + half its size.
506+ // Here we add 180 to the angle to the segment is poistioned correctly.
507+ imageAngle = ( seg . startAngle + 180 + ( ( seg . endAngle - seg . startAngle ) / 2 ) ) ;
508+ }
509+ else if ( seg . imageDirection == 'E' )
510+ {
511+ // Left set so image starts and the center point.
512+ imageLeft = this . centerX ;
513+
514+ // Top is so that it sits half/half over the 90 degree point.
515+ imageTop = ( this . centerY - ( seg . imgData . height / 2 ) ) ;
516+
517+ // Again get the angle in the center of the segment and add it to the rotation angle.
518+ // this time we need to add 270 to that to the segment is rendered the correct place.
519+ imageAngle = ( seg . startAngle + 270 + ( ( seg . endAngle - seg . startAngle ) / 2 ) ) ;
520+ }
521+ else if ( seg . imageDirection == 'W' )
522+ {
523+ // Left is the centerX minus the width of the image.
524+ imageLeft = ( this . centerX - seg . imgData . width ) ;
525+
526+ // Top is so that it sits half/half over the 270 degree point.
527+ imageTop = ( this . centerY - ( seg . imgData . height / 2 ) ) ;
528+
529+ // Again get the angle in the center of the segment and add it to the rotation angle.
530+ // this time we need to add 90 to that to the segment is rendered the correct place.
531+ imageAngle = ( seg . startAngle + 90 + ( ( seg . endAngle - seg . startAngle ) / 2 ) ) ;
532+ }
533+ else // North is the default.
534+ {
535+ // Left set so image sits half/half over the 0 degrees point.
536+ imageLeft = ( this . centerX - ( seg . imgData . width / 2 ) ) ;
537+
538+ // Top so image is its height out (above) the center point.
539+ imageTop = ( this . centerY - seg . imgData . height ) ;
540+
541+ // Angle to draw the image is its starting angle + half its size.
542+ // this sits it half/half over the center angle of the segment.
543+ imageAngle = ( seg . startAngle + ( ( seg . endAngle - seg . startAngle ) / 2 ) ) ;
544+ }
545+
546+ // --------------------------------------------------
547+ // Rotate to the position of the segment and then draw the image.
548+ this . ctx . save ( ) ;
549+ this . ctx . translate ( this . centerX , this . centerY ) ;
550+
551+ // So math here is the rotation angle of the wheel plus half way between the start and end angle of the segment.
552+ this . ctx . rotate ( this . degToRad ( this . rotationAngle + imageAngle ) ) ;
553+ this . ctx . translate ( - this . centerX , - this . centerY ) ;
554+
555+ // Draw the image.
556+ this . ctx . drawImage ( seg . imgData , imageLeft , imageTop ) ;
557+
558+ this . ctx . restore ( ) ;
559+ }
560+ else
561+ {
562+ console . log ( 'Segment ' + x + ' imgData is not loaded' ) ;
563+ }
564+ }
565+ }
566+ }
567+ }
568+
430569// ====================================================================================================================
431570// This function draws the wheel on the page by rendering the segments on the canvas.
432571// ====================================================================================================================
@@ -1843,7 +1982,10 @@ function Segment(options)
18431982 'textMargin' : null ,
18441983 'textFillStyle' : null ,
18451984 'textStrokeStyle' : null ,
1846- 'textLineWidth' : null
1985+ 'textLineWidth' : null ,
1986+ 'image' : null , // Name/path to the image
1987+ 'imageDirection' : 'N' , // The direction the image is facing. Can be North, South, East, West.
1988+ 'imgData' : null // Image object created here and loaded with image data.
18471989 } ;
18481990
18491991 // Now loop through the default options and create properties of this class set to the value for
@@ -1876,6 +2018,33 @@ function Segment(options)
18762018 this . endAngle = 0 ;
18772019}
18782020
2021+ // ====================================================================================================================
2022+ // Changes an image for a segment by setting a callback to render the wheel once the image has loaded.
2023+ // ====================================================================================================================
2024+ Segment . prototype . changeImage = function ( image , imageDirection )
2025+ {
2026+ // Change image name, blank image data.
2027+ this . image = image ;
2028+ this . imgData = null ;
2029+
2030+ // Set direction.
2031+ if ( imageDirection )
2032+ {
2033+ this . imageDirection = imageDirection ;
2034+ }
2035+ else
2036+ {
2037+ // North is the default.
2038+ this . imageDirection = 'N' ;
2039+ }
2040+
2041+ // Set imgData to a new image object, change set callback and change src (just like in wheel constructor).
2042+ winhweelAlreadyDrawn = false ;
2043+ this . imgData = new Image ( ) ;
2044+ this . imgData . onload = winwheelLoadedImage ;
2045+ this . imgData . src = this . image ;
2046+ }
2047+
18792048// ====================================================================================================================
18802049// Class that is created as property of the wheel. Draws line from center of the wheel out to edge of canvas to
18812050// indicate where the code thinks the pointer location is. Helpful to get alignment correct esp when using images.
@@ -1923,8 +2092,6 @@ function winwheelPercentToDegrees(percentValue)
19232092// In order for the wheel to be re-drawn during the spin animation the function greesock calls needs to be outside
19242093// of the class as for some reason it errors if try to call winwheel.draw() directly.
19252094// ====================================================================================================================
1926- var winwheelToDrawDuringAnimation = null ; // This global is set by the winwheel class to the wheel object to be re-drawn.
1927-
19282095function winwheelAnimationLoop ( )
19292096{
19302097 if ( winwheelToDrawDuringAnimation )
@@ -1956,6 +2123,8 @@ function winwheelAnimationLoop()
19562123// This function is called-back when the greensock animation has finished. We remove the event listener to the function
19572124// above to stop the wheel being re-drawn all the time even though it is not animating as the greensock ticker keeps going.
19582125// ====================================================================================================================
2126+ var winwheelToDrawDuringAnimation = null ; // This global is set by the winwheel class to the wheel object to be re-drawn.
2127+
19592128function winwheelStopAnimation ( canCallback )
19602129{
19612130 // Remove the redraw from the ticker.
@@ -1970,4 +2139,39 @@ function winwheelStopAnimation(canCallback)
19702139 eval ( winwheelToDrawDuringAnimation . animation . callbackFinished ) ;
19712140 }
19722141 }
2142+ }
2143+
2144+ // ====================================================================================================================
2145+ // Called after the image has loaded for each segment. Once all the images are loaded it then calls the draw function
2146+ // on the wheel to render it. Used in constructor and also when a segment image is changed.
2147+ // ====================================================================================================================
2148+ var winhweelAlreadyDrawn = false ;
2149+
2150+ function winwheelLoadedImage ( )
2151+ {
2152+ // Prevent multiple drawings of the wheel which ocurrs without this check due to timing of function calls.
2153+ if ( winhweelAlreadyDrawn == false )
2154+ {
2155+ // Set to 0.
2156+ var winwheelImageLoadCount = 0 ;
2157+
2158+ // Loop though all the segments of the wheel and check if image data loaded, if so increment counter.
2159+ for ( i = 1 ; i <= winwheelToDrawDuringAnimation . numSegments ; i ++ )
2160+ {
2161+ // Check the image data object is not null and also that the image has completed loading by checking
2162+ // that a property of it such as the height has some sort of true value.
2163+ if ( ( winwheelToDrawDuringAnimation . segments [ i ] . imgData != null ) && ( winwheelToDrawDuringAnimation . segments [ i ] . imgData . height ) )
2164+ {
2165+ winwheelImageLoadCount ++ ;
2166+ }
2167+ }
2168+
2169+ // If number of images loaded matches the segments then all the images for the wheel are loaded.
2170+ if ( winwheelImageLoadCount == winwheelToDrawDuringAnimation . numSegments )
2171+ {
2172+ // Call draw function to render the wheel.
2173+ winhweelAlreadyDrawn = true ;
2174+ winwheelToDrawDuringAnimation . draw ( ) ;
2175+ }
2176+ }
19732177}
0 commit comments