Progress Ring Plugin for Corona
About the Plugin
The Progress Ring Plugin for Corona allows developers to add customizable circular "progress rings" to their Corona apps and games in as little as a single line of code. These rings can rotate to indicate a timer countdown, player/enemy health bars, or even be used in chart & graph demonstrations in a business app. It was designed to be lightweight and flexible, with good performance even on older mobile devices.
Adding the Plugin to your App
- Activate the plugin at the Corona Marketplace
- Add an entry into the plugins table of your project's build.settings file. Below is an example of a minimal build.settings file with the required entry for the progressRing plugin:
settings = {}
settings.plugins = {
['plugin.progressRing'] = {publisherId = 'com.schroederapps'},
}
Creating a Progress Ring
To create a progressRing
object, first require
the plugin into your app/scene:
local progressRing = require('plugin.progressRing')
Next, call progressRing.new()
, which will return a progress ring object with the default parameters:
local myRing = progressRing.new()
NOTE: Progress rings are, at their core, just Corona GroupObjects. They can be rotated, repositioned, inserted into other group objects, and destroyed just like any other group object. Basically, if you can do it to a Corona group object, then you can do it to your progress ring.
Customizing Your Progress Ring
Calling progressRing.new()
is the fastest way to add a progress ring to your app, but the returned object will always look the same, and may not suit your needs. But it's very easy to customize your ring by passing a table of parameters as an argument to progressRing.new()
. This table can include any of the following key/value pairs. None are required:
key | type | value notes |
---|---|---|
parent |
Corona display group | a variable name referencing a Corona display group to insert the ring into. Defaults to display.currentStage . |
x |
number | the x coordinate where the ring should be placed. Defaults to nil (effectively 0 ). |
y |
number | the y coordinate where the ring should be placed. Defaults to nil (effectively 0 ). |
radius |
number | the radius of your ring in Corona content units. Defaults to 100 . |
diameter |
number | the diameter of your ring in Corona content units. If both radius and diameter are specified, diameter is given priority. Defaults to 200 |
ringDepth |
number | a number between 0 and 1 representing the depth of your ring. A ringDepth of 1 will result in a fully-round ring (“all donut, no hole”), while a ringDepth of 0 would result in a practically invisible ring (“all hole, no donut”). Defaults to .33 . |
ringColor |
table | a table containing 4 numbers between 0 and 1, representing the RGBA values of your ring’s bar color. Defaults to {1, .5, 0, 1} . |
bgColor |
table | a table containing 4 numbers between 0 and 1, representing the RBGA values of your ring’s background color. Defaults to {.7, .7, .7, 1} . |
strokeColor |
table | a table containing 4 numbers between 0 and 1, representing the RBGA values of your ring’s stroke color. If not specified, this value will default to whatever your ring's bgColor is. |
strokeWidth |
number | a number representing the width of your ring’s stroke in Corona content units. Defaults to 0 . |
counterclockwise |
boolean | whether or not the ring should advance in a counter-clockwise manner. Defaults to false . |
hideBG |
boolean | whether or not the background should be invisible. Defaults to false . |
time |
number | the amount of time in milliseconds your ring will take to make a full 360-degree rotation. Defaults to 10000 (10 seconds). |
position |
number | a number between 0 and 1 representing the starting position of your progress bar. 0 would result in no visible progress bar (i.e. 0 degrees), whereas 1 would result in a full progress bar (i.e. 360 degrees). Defaults to 0 . |
bottomImage |
string | path to an image file (i.e. images/bottom.png ) that will appear “underneath” or “behind” your progress ring. Automatically supports dynamic image scaling (@2x, @3x, etc.). Defaults to nil . |
topImage |
string | path to an image file (i.e. images/top.png ) that will appear “on top of” or “in front of” your progress ring. Automatically supports dynamic image scaling (@2x, @3x, etc.). Defaults to nil . |
Object Methods & Properties
Calling progressRing.new()
will create a progress ring object, but it's just going to sit there statically until you call one or more of the following methods on your created objects. Note that methods expect a reference to the ring object, so be sure to call them using a :
colon operator, not a .
dot, as indicated below.
ring:goTo(position, [time], [onComplete])
Advances or retreats the position of the progress ring to the position specified. Will cancel and override any currently-running
goTo
call. Upon completion, aprogressRingEvent
is fired with a phase ofcompleted
.Accepts the following arguments:
key | type | value notes |
---|---|---|
position |
number | a number between 0 and 1 representing the position of the bar should advance or retreat to. 0 would result in no visible progress bar (i.e. 0 degrees), whereas 1 would result in a full progress bar (i.e. 360 degrees). (required) |
time |
number | the number of milliseconds for the ring to advance or retreat to the specified position. If nil , the time is determined by the time value specified for a full rotation when the ring was created. For example, if a ring has a time value of 10000 for a full rotation, and you are moving the ring from position 0 to position .5 , then it will take 5 seconds to advance by default. (optional) |
onComplete |
function | a function to call when the ring completes its advancement or retreat. A reference to the ring object will be passed as a single argument to the function. (optional) |
Example:
local progressRing = require('plugin.progressRing')
local myRing = progressRing.new()
local function onMovementComplete(target)
print('progress ring ' .. tostring(target) .. ' completed its rotation!')
end
myRing:goTo(.9, 3000, onMovementComplete)
ring:pause()
Pauses a progress ring in motion. Accepts no arguments.
Example:
local progressRing = require('plugin.progressRing')
local myRing = progressRing.new()
local function pauseThatRing()
myRing:pause()
end
myRing:goTo(1) --start moving the ring (takes 10 seconds)
timer.performWithDelay(2000, pauseThatRing) --pause the ring after 2 seconds
ring:resume()
Resumes motion on a paused progress ring. Accepts no arguments.
Example:
local progressRing = require('plugin.progressRing')
local myRing = progressRing.new()
local function pauseThatRing()
myRing:pause()
end
local function resumeThatRing()
myRing:resume()
end
myRing:goTo(1) -- start moving the ring (takes 10 seconds)
timer.performWithDelay(2000, pauseThatRing) -- pause the ring after 2 seconds
timer.performWithDelay(4000, resumeThatRing) -- resume the motion 2 seconds later
ring:reset()
Returns ring to the starting position defined when the object was created. Cancels any currently-running
goTo
call. Accepts no arguments.
Example:
local progressRing = require('plugin.progressRing')
local myRing = progressRing.new()
local function resetMe()
myRing:reset()
end
myRing:goTo(1, 2000, resetMe) -- do a full 2-second rotation, then reset
ring:setBgColor([r], [g], [b], [a])
Changes the background color of the ring object after it's already been created. accepts up to 4 numeric arguments indicating the RGBA values of the new background color.
Example:
local progressRing = require('plugin.progressRing')
-- create a ring object with a red background
local myRing = progressRing.new({
bgColor = {1, 0, 0},
})
-- change the background color to blue after 2 seconds:
timer.performWithDelay(2000, function()
myRing:setBgColor(0, 0, 1)
end)
ring:setRingColor([r], [g], [b], [a])
Changes the ring color of the ring object after it's already been created. accepts up to 4 numeric arguments indicating the RGBA values of the new ring color.
Example:
local progressRing = require('plugin.progressRing')
-- create a ring object with a red ring
local myRing = progressRing.new({
ringColor = {1, 0, 0},
})
-- change the ring color to blue after 2 seconds:
timer.performWithDelay(2000, function()
myRing:setRingColor(0, 0, 1)
end)
ring:setStrokeColor([r], [g], [b], [a])
Changes the stroke color of the ring object after it's already been created. accepts up to 4 numeric arguments indicating the RGBA values of the new stroke color.
Example:
local progressRing = require('plugin.progressRing')
-- create a ring object with a red stroke
local myRing = progressRing.new({
strokeColor = {1, 0, 0},
})
-- change the stroke color to blue after 2 seconds:
timer.performWithDelay(2000, function()
myRing:setStrokeColor(0, 0, 1)
end)
ring.position
A number between 0 and 1 indicating where the progress ring currently stands in its progression. Setting this value will cause the ring to instantly jump to that position.
Example:
local progressRing = require('plugin.progressRing')
local myRing = progressRing.new()
-- check ring's position:
print(myRing.position) -- >> 0
-- set ring's position
myRing.position = .75 -- ring will visually jump to the .75 position
ring.strokeWidth
A number representing the strokeWidth of the ring object. As with other Corona display objects, adjusting this value will instantly change the object's visuals.
Example:
local progressRing = require('plugin.progressRing')
-- create a ring object with a strokeWidth of zero
local myRing = progressRing.new({
strokeWidth = 0
})
-- change the ring's strokeWidth to 10 after 2 seconds:
timer.performWithDelay(2000, function()
myRing.strokeWidth = 20
end)
Adding Event Listeners
Progress Ring objects dispatch progressRingEvents
at key moments that can be listened for and acted on. The event table that is dispatched contains the following key/value pairs:
key | type | value notes |
---|---|---|
name |
string | will always be progressRingEvent |
target |
reference | a reference to the progress ring object that dispatched the event |
phase |
string | will be one of the following: completed , paused , resumed , or reset |
position |
number | a number between 0 and 1 indicating the ring's position when the event was dispatched. |
time |
number | the time value that was passed to a completed goTo call. Will always be nil when event.phase ~= 'completed' . |
To listen for these events, add an event listener to your ring object as such:
local progressRing = require('plugin.progressRing')
local function ringEventListener(event)
print('Ring event fired! Target: ' .. event.target .. '; Phase: ' .. event.phase)
end
local myRing = progressRing.new()
myRing:addEventListener('progressRingEvent', ringEventListener)
myRing:goTo(1)
Gotchas
Progress ring objects utilize multiple masks to create the "donut hole" effect, and have perfectly round edges. Corona has a nested masking limit of three. Because of this, progress ring objects with a ringDepth
less than 1
cannot be inserted into masked groups, including display.newContainer
, widget.newScrollView
or widget.newTableView
objects.
Rings with a ringDepth
of 1
only use two nested masks because they don't have a 'hole' and can be inserted into masked groups, but only "one deep."