Jump to content

[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]
Photo

How can I make a pie timer? I need to be able to draw a circular arc with dynamically changing angles.
Started by gains Nov 30 2014 01:33 PM

- - - - -
33 replies to this topic
[TOPIC CONTROLS]
Page 1 of 2 1 2
This topic has been archived. This means that you cannot reply to this topic.
[/TOPIC CONTROLS]
[modOptionsDropdown]
[/modOptionsDropdown]
[reputationFilter]
[TOPIC: post.html]
#1

gains

[GLOBAL: userInfoPane.html]
gains
  • Enthusiast

  • 79 posts
  • Corona SDK

Here is what I am trying to achieve. Circular arc with dynamically changing angles every second or so. 

 

I found this piece of code to draw an arc but cant change the angle dynamically. I attached an image of what I am trying to accomplish.

local function newArc(startAngle, widthAngle, radius )
    startAngle = startAngle or 0
    widthAngle = widthAngle or 90
    radius = radius or 100
    local vert = {}
    vert[#vert+1] = 0 
    vert[#vert+1] = 0
    
    for i = 0, widthAngle do
        local a = (startAngle+i)*math.pi/180
        vert[#vert+1] = radius * math.cos(a)
        vert[#vert+1] = radius * math.sin(a)
    end
    
    local arc = display.newPolygon(0, 0, vert)
    arc:setFillColor(1,1,1)
   
    return arc
end

pietimer.png

 

 



[TOPIC: post.html]
#2

schroederapps

[GLOBAL: userInfoPane.html]
schroederapps
  • Contributor

  • 469 posts
  • Corona SDK

I've modularized an implementation of this (so you can create a "progressRing" with one line of code: "loal ring = progressRing.new()" ) and have been meaning to share it in a blog post. This will give me cause to finally get that done. Stay tuned. I'll post a link here once that's done. Give me maybe a day or two...I'll want to clean up and comment my module before making it public.

[TOPIC: post.html]
#3

gains

[GLOBAL: userInfoPane.html]
gains
  • Enthusiast

  • 79 posts
  • Corona SDK

I solved it using image masks. Not a cool solution.



[TOPIC: post.html]
#4

thomas6

[GLOBAL: userInfoPane.html]
thomas6
  • Contributor

  • 982 posts
  • Corona SDK

I did this a while ago with 4 images for each quadrant, each with a rotating mask. Should be feasible to create a nice and clean class for this without too much effort. Regardless of the framework, it's not a simple primitive shape, so never a straightforward thing to do.



[TOPIC: post.html]
#5

atrizhong

[GLOBAL: userInfoPane.html]
atrizhong
  • Enthusiast

  • 95 posts
  • Corona SDK

local function newArc(startAngle, widthAngle, radius, xPos, yPos, color)
    startAngle = startAngle or 0
    widthAngle = widthAngle or 90
    radius = radius or 100
    local vert = {}
    vert[#vert+1] = 0 
    vert[#vert+1] = 0
    
	
	local minX, minY = 0,0
	local initX, initY = radius * math.cos(startAngle), radius * math.sin(startAngle)
	if (initX < minX) then minX = initX end
	if (initY < minY) then minY = initY end
	
    for i = 0, widthAngle do
        local a = (startAngle+i)*math.pi/180
		
		local x, y = radius * math.cos(a), radius * math.sin(a)
        vert[#vert+1] = x
        vert[#vert+1] = y
		
		if (x < minX) then minX = x end
		if (y < minY) then minY = y end
    end
    
    local arc = display.newPolygon(xPos + minX, yPos + minY, vert)
	arc.anchorX, arc.anchorY = 0, 0
    arc:setFillColor(color[1],color[2],color[3])
   
    return arc
end

local startAngle = 270
local curAngle = 0
local radius = 100
local buffer = 25
local color1, color2 = {0,0,.8}, {0.4,0.4,.8}
local xPos, yPos = display.contentCenterX, display.contentCenterY
local arc1 = newArc(startAngle,curAngle,radius+buffer,xPos,yPos,color1);
local arc2 = newArc(startAngle,curAngle,radius,xPos,yPos,color2);

local function update()
	curAngle = curAngle + 1
	if (curAngle == 360) then curAngle = 0 end
	arc1:removeSelf();
	arc2:removeSelf();
	arc1 = newArc(startAngle,curAngle,radius+buffer,xPos,yPos,color1);
	arc2 = newArc(startAngle,curAngle,radius,xPos,yPos,color2);

	timer.performWithDelay(10, update)
end

update()

Is this what you want? The hardest part, imo, is getting the polygon to stay in one place, which I added some code to the drawArc function.



[TOPIC: post.html]
#6

schroederapps

[GLOBAL: userInfoPane.html]
schroederapps
  • Contributor

  • 469 posts
  • Corona SDK

I know I said "give me a couple of days" like 3 weeks ago, but seriously - I'm very VERY close to having a nice fully-fleshed out module for this - hopefully it'll be worth the wait. You can add a new progressRing by simply calling progressRing.new(), customize it in many ways very easily, and then start it, tell it to advance/retreat to any position (and specify how long it'll take to get there), pause current movement, resume, reset, etc. It's taken me a lot longer than I anticipated, but I think it's nice and polished. The moment it's available online, I'll post a link here.



[TOPIC: post.html]
#7

gains

[GLOBAL: userInfoPane.html]
gains
  • Enthusiast

  • 79 posts
  • Corona SDK

Thanks looking forward to it.



[TOPIC: post.html]
#8

horacebury

[GLOBAL: userInfoPane.html]
horacebury
  • Corona Geek

  • 3,070 posts
  • Corona SDK

[TOPIC: post.html]
#9

horacebury

[GLOBAL: userInfoPane.html]
horacebury
  • Corona Geek

  • 3,070 posts
  • Corona SDK

How's this:

 

[EDIT] Get this mathlib library too: http://code.coronalabs.com/code/mathliblua

require("mathlib")

local function newCycle( x, y, inner, outer, from, range )
	range = range or 1
	if (range < 1) then
		range = 1
	elseif (range > 360) then
		range = 360
	end
	local centre = {x=0,y=0}
	local pt = {x=0, y=-(inner+(outer-inner)/2)}
	pt = math.rotateTo( pt, from, centre )
	local path = {pt.x,pt.y}
	for i=1, range do
		pt = math.rotateTo( pt, 1, centre )
		path[#path+1] = pt.x
		path[#path+1] = pt.y
	end
	local line = display.newLine( unpack( path ) )
	line.strokeWidth = outer-inner
	line.x, line.y = x, y
	return line
end

local range = display.newGroup()
range.i = 1
transition.to( range, { time=2000, i=271 } )
Runtime:addEventListener( "enterFrame", function()
	newCycle( 200, 100, 50, 100, 0, range.i )
end )



[TOPIC: post.html]
#10

horacebury

[GLOBAL: userInfoPane.html]
horacebury
  • Corona Geek

  • 3,070 posts
  • Corona SDK

Actually, this is better:

 

[EDIT] Btw, you will need to download the mathlib library here: http://code.coronalabs.com/code/mathliblua

require("mathlib")

local function newCycle( parent, x, y, inner, outer, from, range )
	range = range or 1
	if (range < 1) then
		range = 1
	elseif (range > 360) then
		range = 360
	end
	local centre = {x=0,y=0}
	local offsetY = -(inner+(outer-inner)/2)
	local pt = {x=0, y=offsetY}
	pt = math.rotateTo( pt, from, centre )
	local path = {pt.x,pt.y}
	for i=1, range do
		pt = math.rotateTo( pt, 1, centre )
		path[#path+1] = pt.x
		path[#path+1] = pt.y
	end
	local line = display.newLine( parent, unpack( path ) )
	line.strokeWidth = outer-inner
	return line
end

local function newTimer( parent, x, y, inner, outer, colour, from, range, time )
	local group = display.newGroup()
	group.x, group.y = x, y
	group.i = 1
	local function render()
		if (group.numChildren > 0) then
			group[1]:removeSelf()
		end
		local line = newCycle( group, 0, 0, inner, outer, from, group.i )
		line:setStrokeColor( colour[1], colour[2], colour[3], colour[4] or 1 )
	end
	local function onComplete()
		Runtime:removeEventListener( "enterFrame", render )
	end
	transition.to( group, { time=time, i=range, onComplete=onComplete } )
	Runtime:addEventListener( "enterFrame", render )
	return group
end

newTimer( display.currentStage, 200, 100, 50, 100, {.5,.5,1}, 180, 181, 1000 )
display.newCircle( 200, 100, 5 )



[TOPIC: post.html]
#11

schroederapps

[GLOBAL: userInfoPane.html]
schroederapps
  • Contributor

  • 469 posts
  • Corona SDK

@Horacebury: as usual, your solution is elegantly simple and effective! Mine has lots of options and I think is still a good option, but I don't think I can compete with yours in terms of compactness. I think my module clocks in at around 250 lines, not including the commented-out documentation lines. ...Of course, 50 of those 250 lines are to fix the finalize() bug (using @sergeylerg's solution) so that I can automatically clean up my Runtime listeners if the ring object's parent group is removed. Hope to post my module tonight ... Between the full-time non-app job, two active client projects and twin 19-month olds, any extracurricular coding takes waaay longer than it should!

[TOPIC: post.html]
#12

horacebury

[GLOBAL: userInfoPane.html]
horacebury
  • Corona Geek

  • 3,070 posts
  • Corona SDK

Hey, you know, it's just time and repetition. I've had practice with this one a few weeks ago and that solution got real messy. This one just popped into my head as I'm having a good coding day and fortunately plenty of time.



[TOPIC: post.html]
#13

schroederapps

[GLOBAL: userInfoPane.html]
schroederapps
  • Contributor

  • 469 posts
  • Corona SDK

Well, at long last I've finally finished polishing my progressRing module (with documentation, a sample project, and demo video) and I'm ready to share it. Hopefully it'll feel worth the wait. Sorry it took so long!

 

A detailed description of how it works, plus download links for the module itself and a sample project can be found at my website: http://www.jasonschroeder.com/2014/12/21/progress-ring-module-for-corona-sdk/

 

And here's a video of how it looks in action (this is also the sample project you can download):

 

And in case you're not in the mood to visit my site, here's the full content of progressRing.lua - just require it into your project and call progressRing.new() to create a progress ring (but you can customize it by passing a table with your parameters):

 


-- progressRing Module for Corona SDK
-- Copyright (c) 2014 Jason Schroeder
-- http://www.jasonschroeder.com
-- http://www.twitter.com/schroederapps
 
--[[ Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
 
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. ]]--
 
------------------------------------------------------------------------------------
-- HOW TO USE THIS MODULE
------------------------------------------------------------------------------------
-- Step One: put this lua file in your project's root directory
-- Step Two: require the module in your project as such:
   -- local progressRing = require("progressRing")
-- Step Three: create a progress ring object as such:
   -- local ringObject = progressRing.new([params])
   -- You can customize the look of your progress ring by including a single table as an argument when calling progressRing.new(). The table can include any of the following key/value pairs (but none are required):
      -- radius: a number representing the radius of your ring in pixels. Defaults to 100.
      -- ringColor: a table containing 4 numbers between 0 and 1, representing the RGBA values of your ring's bar color. Defaults to {1, 1, 1} (white).
      -- bgColor: a table containing 4 numbers between 0 and 1, representing the RBGA values of your ring's background color. Defaults to {0, 0, 0} (black).
      -- ringDepth: 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 an invisible ring ("all hole, no donut"). Defaults to .33.
      -- strokeColor: a table containing 4 numbers between 0 and 1, representing the RBGA values of your ring's stroke (border) color. Defaults to whatever your bgColor is.
      -- strokeWidth: a number representing the width of your ring's stroke (border) in pixels. Defaults to 0 (no stroke).
      -- counterclockwise: a boolean (true/false) value indicating whether or not the ring should advance in a counter-clockwise manner. Defaults to false.
      -- hideBG: a boolean (true/false) value indicating whether or not the background should be visible. Defaults to false.
      -- time: a number representing the amount of time (in milliseconds) your ring will take to make a full rotation, from 0 to 360 degrees. Defaults to 10000 (10 seconds).
      -- position: 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). 1 would result in a full progress bar (i.e. 360 degrees). .5 would result in a halfway-full progress bar (i.e. 180 degrees). Defaults to 0.
      -- bottomImage: a string representing the 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: a string representing the 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.
      
-- Step Four: you can change the position of your progress ring using the following methods:
   -- ringObject:goTo(position, [time]) is used to advance/retreat the position of the progress ring. Note:
      -- position (required) is a number between 0 and 1 representing the position the bar should advance or retreat to.
      -- time (optional) is a number representing the amount of time (in milliseconds) it will take for your ring to reach the position you defined. By default, this is determined by the time you set for a full rotation. (i.e. if you set a time of 10 seconds for a full 360-degree rotation, and your ring is advancing 180 degrees, it will take 5 seconds). Setting a time of 0 will result in an immediate repositioning of your progress ring.
   -- ringObject:pause() will pause your progress ring while advancing or retreating.
   -- ringObject:resume() will resume a paused progress ring.
   -- ringObject:reset() will return your progress ring to the starting position you defined when creating the object.
 
------------------------------------------------------------------------------------
-- FINALIZE BUG FIX:
-- Prior to Corona build # 2015.2544, there was a bug in Corona SDK that prevented
-- finalize events from being called for children objects when group is removed. This
-- code fixes that bug, in case you are using an older build for your app.
-- This bug fix was written by the incomparable @SergeyLerg - thanks, Sergey!
------------------------------------------------------------------------------------
local function fixFinalize()
   print("Fixing finalize() bug...")
   local function finalize(event)
      local g = event.target
      for i = 1, g.numChildren do
         if g[i]._tableListeners and g[i]._tableListeners.finalize then
            for j = 1, #g[i]._tableListeners.finalize do
               g[i]._tableListeners.finalize[j]:dispatchEvent{name = 'finalize', target = g[i]}
            end
         end
         if g[i]._functionListeners and g[i]._functionListeners.finalize then
            for j = 1, #g[i]._functionListeners.finalize do
               g[i]._functionListeners.finalize[j]({name = 'finalize', target = g[i]})
            end
         end
         if g[i].numChildren then
            finalize{target = g[i]}
         end
      end
   end
 
   local newGroup = display.newGroup
 
   function display.newGroup()
      local g = newGroup()
      g:addEventListener('finalize', finalize)
      return g
   end
end
 
local finalizeFixed = false
local testGroup = display.newGroup()
testGroup.isVisible = false
local testObject = display.newRect(testGroup, 0, 0, 1, 1)
function testObject.finalize(self, event)
   finalizeFixed = true
   print("no longer any need to fix finalize() - thanks Corona Labs!")
end
testObject:addEventListener("finalize")
display.remove(testGroup)
timer.performWithDelay(1, function()
   if not finalizeFixed then fixFinalize() end
   finalizeFixed, testGroup, testObject = nil, nil, nil
end)
 
------------------------------------------------------------------------------------
-- CREATE TABLE TO HOLD MODULE
------------------------------------------------------------------------------------
local progressRing = {}
 
------------------------------------------------------------------------------------
-- SCREEN POSITIONING VARIABLES
------------------------------------------------------------------------------------
local centerX = display.contentCenterX
local centerY = display.contentCenterY
local screenTop = display.screenOriginY
local screenLeft = display.screenOriginX
local screenBottom = display.screenOriginY+display.actualContentHeight
local screenRight = display.screenOriginX+display.actualContentWidth
local screenWidth = screenRight - screenLeft
local screenHeight = screenBottom - screenTop
 
------------------------------------------------------------------------------------
-- CREATE NEW PROGRESS RING
------------------------------------------------------------------------------------
function progressRing.new(params)
   -- available params are: radius, ringColor, bgColor, strokeColor, ringDepth, strokeWidth, hideBG, time, position, topImage, bottomImage
   if params == nil then params = {} end
   
   --------------------------------------------------------------------------------
   -- LOCALIZE PARAMS & SET DEFAULTS
   --------------------------------------------------------------------------------
   local radius = params.radius or 100
   local counterclockwise = params.counterclockwise
   local ringColor = params.ringColor or {1, 1, 1}
   local bgColor = params.bgColor or {0, 0, 0}
   local strokeColor = params.strokeColor or bgColor
   local ringDepth = params.ringDepth or .33
   local strokeWidth = params.strokeWidth or 0
   local hideBG = params.hideBG
   local time = params.time or 10000
   local startPosition = params.position or 0
   local topImage = params.topImage
   local bottomImage = params.bottomImage
   
   if ringDepth > 1 then ringDepth = 1 elseif ringDepth <0 then ringDepth = 0 end
   
   --------------------------------------------------------------------------------
   -- CREATE PROGRESS RING VISUALS
   --------------------------------------------------------------------------------
   local group = display.newGroup()
   group.position = startPosition
   local objectName = tostring(group)
   if counterclockwise == true then group.xScale = -1 end
   local sliceGroup = display.newGroup()
   sliceGroup.group = sliceGroup
   function sliceGroup.invalidate() end
   if ringColor[4] ~= nil and ringColor[4] < 1 and ringColor[4] > 0 then
      sliceGroup = nil
      sliceGroup = display.newSnapshot(radius*2, radius*2)
      sliceGroup.alpha = ringColor[4]
   end
   local sliceContainer = display.newContainer(group, radius*2, radius*2)
   sliceContainer.anchorChildren = false
   sliceContainer.anchorX = 0
   group.isVisible = false
   
   local slices = {}
   
   local bg = display.newCircle(group, 0, 0, radius)
   bg:setFillColor(unpack(bgColor))
   group:insert(sliceGroup)
   if hideBG then bg.isVisible = false end
   local stroke1 = display.newCircle(group, 0, 0, radius + strokeWidth*.5)
   stroke1:setFillColor(0, 0, 0, 0)
   stroke1:setStrokeColor(unpack(strokeColor))
   stroke1.strokeWidth = strokeWidth
   local stroke2 = display.newCircle(group, 0, 0, radius - radius*ringDepth - strokeWidth/2)
   stroke2:setFillColor(0, 0, 0, 0)
   stroke2:setStrokeColor(unpack(strokeColor))
   stroke2.strokeWidth = strokeWidth
   
   if bottomImage ~= nil then
      local getDims = display.newImage(bottomImage)
      getDims.isVisible = false
      local w, h = getDims.width, getDims.height
      group.bottomImage = display.newImageRect(bottomImage, w, h)
      group.bottomImage.x, group.bottomImage.y = 0, 0
      group:insert(1, group.bottomImage)
      display.remove(getDims)
      getDims = nil
   end
   
   if topImage ~= nil then
      local getDims = display.newImage(topImage)
      getDims.isVisible = false
      local w, h = getDims.width, getDims.height
      group.topImage = display.newImageRect(group, topImage, w, h)
      group.topImage.x, group.topImage.y = 0, 0
      display.remove(getDims)
      getDims = nil
   end
   
   --------------------------------------------------------------------------------
   -- ADD PROGRESS RING "SLICES"
   --------------------------------------------------------------------------------
   local sliceHeight = radius * 1.5
   for i = 0, 350, 10 do
      local slice = display.newPolygon(0, 0, {0, 0, 0, -sliceHeight, sliceHeight*.182, -sliceHeight})
      local ringColor = {ringColor[1], ringColor[2], ringColor[3]}
      slice:setFillColor(unpack(ringColor))
      slice.anchorX, slice.anchorY = 0, 1
      slice.target = i
      if i >=180 then
         sliceGroup.group:insert(slice)
      else
         sliceContainer:insert(slice)
      end
      slice.rotation = -10
      slices[#slices+1] = slice
   end
   
   --------------------------------------------------------------------------------
   -- CREATE CIRCULAR MASK FOR SLICES
   --------------------------------------------------------------------------------
   local stageColor = display.getDefault("background")
   local coverUp = display.newRect(centerX, centerY, screenWidth, screenHeight)
   coverUp:setFillColor(stageColor)
   display.getCurrentStage():insert(1, coverUp)
   
   local squareSize = screenWidth - 16
   if screenWidth > screenHeight then squareSize = screenHeight - 16 end
   squareSize = math.floor(squareSize*.25)*4 + 16
   local maskRadius = squareSize * .5 - 16
   local maskScaleX = radius / (maskRadius / display.contentScaleX)
   local maskScaleY = radius / (maskRadius / display.contentScaleY)
   
   local maskGroup = display.newGroup()
   maskGroup.x, maskGroup.y = centerX, centerY
   display.getCurrentStage():insert(1, maskGroup)
   local square = display.newRect(maskGroup, 0, 0, squareSize, squareSize)
   square:setFillColor(0)
   local circle = display.newCircle(maskGroup, 0, 0, maskRadius)
   circle:setFillColor(1)
   local circle2 = display.newCircle(maskGroup, 0, 0, maskRadius*(1-ringDepth))
   circle2:setFillColor(0)
   timer.performWithDelay(10, function()
      local maskImage = display.capture(maskGroup, { saveToPhotoLibrary=false, isFullResolution=false } )
      display.getCurrentStage():insert(1, maskImage)
   
      timer.performWithDelay(1, function()
         display.save( maskImage, {filename=objectName..".jpg", baseDir=system.TemporaryDirectory, isFullResolution=true} )
         local mask = graphics.newMask( objectName..".jpg", system.TemporaryDirectory )
         
         timer.performWithDelay(1, function()
            bg:setMask(mask)
            bg.maskScaleX, bg.maskScaleY = maskScaleX, maskScaleY
            sliceGroup.group:setMask(mask)
            sliceGroup.maskScaleX, sliceGroup.maskScaleY = maskScaleX, maskScaleY
            display.remove(maskGroup)
            display.remove(maskImage)
            display.remove(coverUp)
            sliceGroup.group:insert(sliceContainer)
            group.isCreated = true
            group:goTo(startPosition, 0)
            group.isVisible = true
         end)
      end)
   end)
   
   --------------------------------------------------------------------------------
   -- FUNCTION TO RUN WHEN ROTATION IS COMPLETED
   --------------------------------------------------------------------------------
   local function onComplete()
      group:dispatchEvent({name = "completed"})
   end
   
   --------------------------------------------------------------------------------
   -- SET RING POSITION (i.e. start rotation)
   --------------------------------------------------------------------------------
   function group.goTo(self, position, customTime)
      if group.isCreated then
         transition.cancel(objectName)
         customTime = customTime or math.abs(group.position - position)*time
         if position > 1 then position = 1 elseif position < 0 then position = 0 end
         transition.to(slices[#slices], {rotation = position*360 - 10, time = customTime, tag = objectName, onComplete = onComplete})
      else
         timer.performWithDelay(50, function()
            group:goTo(position, customTime)
         end)
      end
   end
   
   --------------------------------------------------------------------------------
   -- RESET RING ROTATION
   --------------------------------------------------------------------------------
   function group.reset(self)
      transition.cancel(objectName)
      group:dispatchEvent({name = "reset"})
      group:goTo(startPosition, 0)
   end
   
   --------------------------------------------------------------------------------
   -- PAUSE RING ROTATION
   --------------------------------------------------------------------------------
   function group.pause(self)
      transition.pause(objectName)
      group:dispatchEvent({name = "paused"})
   end
   
   --------------------------------------------------------------------------------
   -- RESUME PAUSED ROTATION
   --------------------------------------------------------------------------------
   function group.resume(self)
      transition.resume(objectName)
      group:dispatchEvent({name = "resumed"})
   end
   
   --------------------------------------------------------------------------------
   -- RUNTIME LISTENER TO SET SLICE ROTATIONS & MAKE VISIBLE/INVISIBLE
   --------------------------------------------------------------------------------
   function group.runtimeListener(event)
      local targetSlice = slices[#slices]
      targetSlice.isVisible = targetSlice.rotation >=0
      group.position = (targetSlice.rotation + 10)/360
      for i = 1,#slices-1 do
         local slice = slices[i]
         slice.isVisible = slice.rotation > -10
         if targetSlice.rotation <= slice.target then
            slice.rotation = targetSlice.rotation
         else
            slice.rotation = slice.target
         end
         if i >= #slices*.5 then
            slice.isVisible = slice.rotation >=0
         end
      end
      sliceGroup:invalidate()
   end
   
   Runtime:addEventListener("enterFrame", group.runtimeListener)
   
   --------------------------------------------------------------------------------
   -- FINALIZE CLEANUP WHEN PROGRESS RING IS REMOVED
   --------------------------------------------------------------------------------
   function group.finalize(self, event)
      transition.cancel(objectName)
      Runtime:removeEventListener("enterFrame", group.runtimeListener)
   end
   group:addEventListener("finalize")
   
   --------------------------------------------------------------------------------
   -- RETURN PROGRESS RING OBJECT
   --------------------------------------------------------------------------------
   return group
end
 
 
------------------------------------------------------------------------------------
-- RETURN MODULE
------------------------------------------------------------------------------------
return progressRing



[TOPIC: post.html]
#14

jonjonsson

[GLOBAL: userInfoPane.html]
jonjonsson
  • Corona Geek

  • 1,051 posts
  • Corona SDK

@schroederapps wow that looks great, thank you for sharing!



[TOPIC: post.html]
#15

Alan PlantPot

[GLOBAL: userInfoPane.html]
Alan PlantPot
  • Contributor

  • 928 posts
  • Corona SDK

Really nice, good work!



[TOPIC: post.html]
#16

visualstation

[GLOBAL: userInfoPane.html]
visualstation
  • Observer

  • 20 posts
  • Corona SDK

wow, this is great!

 

Perry



[TOPIC: post.html]
#17

toga

[GLOBAL: userInfoPane.html]
toga
  • Contributor

  • 103 posts
  • Corona SDK

@schroederapps: Thanks a lot for sharing.

The progress ring is not working correctly anymore, if i change the resolution in the config.lua file like:

application = {
   content = {
      width = 800,
      height = 1200,
      scale = "letterbox",
      fps = 60,

      imageSuffix = {
         ["@2x"] = 1.3,
      },
   },  
}

Is it possible to fix this issue?



[TOPIC: post.html]
#18

schroederapps

[GLOBAL: userInfoPane.html]
schroederapps
  • Contributor

  • 469 posts
  • Corona SDK

Hi @toga:

 

That's strange - I see what you are talking about, when I add a config.lua file to my sample project, even when I try adjusting the settings, the progressRing doesn't behave like it should. It likely has something to do with the way I generate a mask file - give me a little time and I'll try to work out a solution. Thanks for finding this bug!



[TOPIC: post.html]
#19

schroederapps

[GLOBAL: userInfoPane.html]
schroederapps
  • Contributor

  • 469 posts
  • Corona SDK

Just a follow-up: I've identified the problem. Since my module creates the mask image file procedurally, and mask files have very strict requirements (must be divisible by 4, must have at least a 3-pixel border, etc.), it gets tricky to create a proper mask image file on a device where the stage is being scaled (letterbox, zoomEven, etc.). To see what I mean @toga, try running your app in a simulator window sized exactly 800x1200 and everything should work as expected.

 

I've gotten things fixed, roughly, but I'm just doing a little more work to neaten up my code and make sure my solution is as flexible as it needs to be - I'm also working to fix an issue that arises if your progressRing is too large (and the mask image file gets stretched to the device's native resolution because of a bug with the display.capture API). It's unlikely that somebody would need a progressRing that large, but I want things to work in all cases if possible. I'll post an updated version of the module later this week.

 

Thanks for your patience!



[TOPIC: post.html]
#20

toga

[GLOBAL: userInfoPane.html]
toga
  • Contributor

  • 103 posts
  • Corona SDK

Hi @schroederapps,

thanks a lot for your work.



[TOPIC: post.html]
#21

schroederapps

[GLOBAL: userInfoPane.html]
schroederapps
  • Contributor

  • 469 posts
  • Corona SDK

Hey everybody,

 

With thanks to toga for discovering a flaw in my module, I have corrected it so that the progressRing works as expected in all circumstances, including when content is being scaled (letterbox, zoomEven, zoomStretch), and if the progressRing is wider than the device's screen width or height. You can download the corrected module here, and I've also updated the sample project available here. I've also updated the post above to include the corrected code.

 

Here's what was happening: the progressRing utilizes a mask image to create the circular shape and "donut hole." The mask image is procedurally generated, but previously it was not necessarily meeting the strict requirements for a mask image (height/width must be divisible by 4, and must have a 3-pixel black border on all sides) if the device's width and height did not match the content width/height defined in your project's config.lua. I've fixed this by making the mask file a fixed size that is not determined by the progressRing's radius, but rather by the device's screen size. Then the image mask is scaled appropriately to match up with the progressRing's defined radius.

 

Thanks again toga for finding this flaw, and enjoy everybody!



[TOPIC: post.html]
#22

toga

[GLOBAL: userInfoPane.html]
toga
  • Contributor

  • 103 posts
  • Corona SDK

Thanks a lot. I will try it tomorrow.



[TOPIC: post.html]
#23

doubleslashdesign

[GLOBAL: userInfoPane.html]
doubleslashdesign
  • Contributor

  • 727 posts
  • Corona SDK

Really Really nice, good work!!!!,

 

I will download this now!!

 

Great job



[TOPIC: post.html]
#24

schroederapps

[GLOBAL: userInfoPane.html]
schroederapps
  • Contributor

  • 469 posts
  • Corona SDK

UPDATE: With daily build #2015.2544, Corona Labs fixed the finalize() bug that prevented objects' finalize events from being triggered when their parent display group was removed. Woohoo! However, my progressRing module had a bug that only manifests itself now that finalize() is working again. I had utilized a workaround developed by @sergeylerg, but there was an error in my implementation of that fix that causes a crash when run on build 2015.2544 and later. I fixed that error by adding a 1ms timer when checking to see if finalize events are being triggered as they should. The finalize() fix is not necessary for up-to-date Corona builds, but I’m leaving it in for folks who might be running legacy Corona builds.

 
Please get the updated version of the progressRing module at http://www.jasonschroeder.com/2014/12/21/progress-ring-module-for-corona-sdk/
 
I've also updated the code that appears earlier in this thread to the latest version. Thanks Corona Labs for fixing that bug!


[TOPIC: post.html]
#25

krystian6

[GLOBAL: userInfoPane.html]
krystian6
  • Contributor

  • 560 posts
  • Corona SDK

Great work, I'm only wondering if the aliasing visible in the video is still there?

We have our own module using 4 masks and changing the corners position to simulate the progress, but the aliasing problem is not visible. I would gladly change to something less 'hacky' ;)




[topic_controls]
Page 1 of 2 1 2
 
[/topic_controls]