├── Paths.png ├── Setup.png ├── Example.png ├── README.md ├── Radial Progress Bar.sketchplugin └── Contents │ └── Sketch │ ├── manifest.json │ └── script.cocoascript └── LICENSE /Paths.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avdgriendt/sketch-radial-progress-bar/HEAD/Paths.png -------------------------------------------------------------------------------- /Setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avdgriendt/sketch-radial-progress-bar/HEAD/Setup.png -------------------------------------------------------------------------------- /Example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avdgriendt/sketch-radial-progress-bar/HEAD/Example.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Radial Progress Bar 2 | This plugin for Sketch makes it super easy to create circular progress bars. It generates nice clean vector paths that you can style however you want. 3 | 4 | ![Example](Example.png) 5 | 6 | You can: 7 | * Set the radius 8 | * Set the progress in % 9 | * Set the width of the line 10 | * Show a background or not 11 | * Round the ends of the line or not 12 | 13 | ![Create](Setup.png) 14 | 15 | #Full vector paths 16 | It creates a clean vector path, so you don't need to worry when you scale or move it that it's going to be messed up. 17 | ![Paths](Paths.png) 18 | 19 | 20 | #Installation 21 | Download the zip and double click the .sketchplugin file. This will install the plugin automatically 22 | -------------------------------------------------------------------------------- /Radial Progress Bar.sketchplugin/Contents/Sketch/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "author" : "Andreas van der Griendt", 3 | "commands" : [ 4 | { 5 | "script" : "script.cocoascript", 6 | "name" : "Create", 7 | "handlers" : { 8 | "run" : "onRun" 9 | }, 10 | "identifier" : "com.bohemiancoding.sketch.radialprogressbar" 11 | } 12 | ], 13 | "menu" : { 14 | "items" : [ 15 | "com.bohemiancoding.sketch.radialprogressbar" 16 | ], 17 | "title" : "Radial Progress Bar" 18 | }, 19 | "identifier" : "com.example.sketch.0019123d-c953-404e-bfbd-1a64017631bb", 20 | "version" : "0.2.3", 21 | "description" : "A Sketch plugin that can generate beatiful radial progress bars", 22 | "authorEmail" : "andreas@visuo.nl", 23 | "name" : "Radial Progress Bar" 24 | } 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Andreas van der Griendt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Radial Progress Bar.sketchplugin/Contents/Sketch/script.cocoascript: -------------------------------------------------------------------------------- 1 | var onRun = function(context) { 2 | 3 | var doc = context.document; 4 | var parentObject = doc.currentPage(); 5 | var center = NSMakePoint(0,0); 6 | 7 | var selectedLayers = context.selection; 8 | var selectedCount = selectedLayers.count(); 9 | var firstLayer = selectedLayers.firstObject(); 10 | 11 | //Check if there is a selected layer and determine parent object and center 12 | if ( selectedLayers.count() > 0 ) { 13 | if ([firstLayer isKindOfClass:[MSArtboardGroup class]]) { 14 | parentObject = firstLayer; 15 | center = NSMakePoint(firstLayer.frame().width() / 2, firstLayer.frame().height() / 2); 16 | } else { 17 | var layer = firstLayer.parentGroup(); 18 | parentObject = layer; 19 | center = NSMakePoint(firstLayer.frame().x() + firstLayer.frame().width() / 2, firstLayer.frame().y() + firstLayer.frame().height() / 2); 20 | } 21 | } 22 | 23 | 24 | //Set the initial values from the User Defaults if they exist 25 | var userDefaults = [NSUserDefaults standardUserDefaults]; 26 | 27 | var initialRadius = 50; 28 | if ([userDefaults objectForKey:@"initialRadius"]) { 29 | initialRadius = [userDefaults objectForKey:@"initialRadius"]; 30 | } 31 | 32 | var initialThickness = 10; 33 | if ([userDefaults objectForKey:@"initialThickness"]) { 34 | initialThickness = [userDefaults objectForKey:@"initialThickness"]; 35 | } 36 | 37 | var initialProgress = 75; 38 | if ([userDefaults objectForKey:@"initialProgress"]) { 39 | initialProgress = [userDefaults objectForKey:@"initialProgress"]; 40 | } 41 | 42 | var shouldRound = false; 43 | if ([userDefaults objectForKey:@"shouldRound"]) { 44 | shouldRound = [userDefaults objectForKey:@"shouldRound"]; 45 | } 46 | 47 | var showBackground = true; 48 | if ([userDefaults objectForKey:@"showBackground"]) { 49 | showBackground = [userDefaults objectForKey:@"showBackground"]; 50 | } 51 | 52 | //Ask for the user input 53 | var userInput = COSAlertWindow.new(); 54 | 55 | userInput.setMessageText("Create radial progress bar"); 56 | 57 | userInput.addTextLabelWithValue("Radius"); 58 | userInput.addTextFieldWithValue(initialRadius); 59 | 60 | userInput.addTextLabelWithValue("Thickness"); 61 | userInput.addTextFieldWithValue(initialThickness); 62 | 63 | userInput.addTextLabelWithValue("Progress (%)"); 64 | userInput.addTextFieldWithValue(initialProgress); 65 | 66 | var roundedCheckbox = NSButton.alloc().initWithFrame( NSMakeRect( 0, 0, 300, 18 ) ); 67 | roundedCheckbox.setButtonType( NSSwitchButton ); 68 | roundedCheckbox.setTitle( 'Rounded Line' ); 69 | roundedCheckbox.setState(shouldRound); 70 | userInput.addAccessoryView( roundedCheckbox ); 71 | 72 | var showBackgroundCheckbox = NSButton.alloc().initWithFrame( NSMakeRect( 0, 0, 300, 18 ) ); 73 | showBackgroundCheckbox.setButtonType( NSSwitchButton ); 74 | showBackgroundCheckbox.setTitle( 'Show background' ); 75 | showBackgroundCheckbox.setState(showBackground); 76 | userInput.addAccessoryView( showBackgroundCheckbox ); 77 | 78 | userInput.addButtonWithTitle('Create'); 79 | userInput.addButtonWithTitle('Cancel'); 80 | 81 | var responseCode = userInput.runModal(); 82 | 83 | //Get the values from the fields 84 | var radius = userInput.viewAtIndex(1).floatValue(); 85 | var thickness = userInput.viewAtIndex(3).floatValue(); 86 | var shouldRound = userInput.viewAtIndex(6).state(); 87 | var showBackground = userInput.viewAtIndex(7).state(); 88 | var progress = userInput.viewAtIndex(5).floatValue(); 89 | 90 | if (responseCode == 1000) { 91 | 92 | //Save the input to the user defaults 93 | [userDefaults setObject:radius forKey:@"initialRadius"]; 94 | [userDefaults setObject:thickness forKey:@"initialThickness"]; 95 | [userDefaults setObject:progress forKey:@"initialProgress"]; 96 | [userDefaults setObject:showBackground forKey:@"showBackground"]; 97 | [userDefaults setObject:shouldRound forKey:@"shouldRound"]; 98 | [userDefaults synchronize]; 99 | 100 | if (radius < thickness) { 101 | doc.displayMessage('Invalid input, please try again'); 102 | } else { 103 | var group = [MSLayerGroup new] 104 | parentObject.addLayers([group]) 105 | group.setName("Progress Circle") 106 | 107 | var path = NSBezierPath.bezierPath(); 108 | if (shouldRound) { 109 | var cornerRadius = thickness / 2; 110 | var offsetInDegrees = (cornerRadius / (2 * 3.14 * radius)) * 360; 111 | var angle = progress / 100 * 360 - 90; 112 | var angleWithOffset = angle - offsetInDegrees; 113 | [path appendBezierPathWithArcWithCenter:center 114 | radius:radius 115 | startAngle:(-90) 116 | endAngle:angle]; 117 | 118 | var x1 = center.x + (radius - (thickness / 2)) * Math.cos(angle * (3.14 / 180)); 119 | var y1 = center.y + (radius - (thickness / 2)) * Math.sin(angle * (3.14 / 180)); 120 | 121 | [path appendBezierPathWithArcWithCenter:NSMakePoint(x1, y1) 122 | radius:(thickness / 2) 123 | startAngle:(angle) 124 | endAngle:(angle + 180)]; 125 | 126 | [path appendBezierPathWithArcWithCenter:center 127 | radius:(radius - thickness) 128 | startAngle:(progress / 100 * 360 - 90) 129 | endAngle:(-90) 130 | clockwise:true]; 131 | 132 | 133 | [path appendBezierPathWithArcWithCenter:NSMakePoint(center.x, center.y - (radius - (thickness / 2))) 134 | radius:(thickness / 2) 135 | startAngle:(90) 136 | endAngle:(270)]; 137 | 138 | path.closePath(); 139 | 140 | } else { 141 | [path appendBezierPathWithArcWithCenter:center 142 | radius:radius 143 | startAngle:-90 144 | endAngle:(progress / 100 * 360 - 90)]; 145 | 146 | 147 | [path appendBezierPathWithArcWithCenter:center 148 | radius:(radius - thickness) 149 | startAngle:(progress / 100 * 360 - 90) 150 | endAngle:-90 151 | clockwise:true]; 152 | path.closePath(); 153 | } 154 | 155 | var shape = MSShapeGroup.shapeWithBezierPath(path); 156 | shape.setName("Progress") 157 | var fill = shape.style().addStylePartOfType(0); 158 | fill.color = MSColor.colorWithRed_green_blue_alpha(0.1,0.1,0.1,1.0); 159 | 160 | if (showBackground) { 161 | 162 | var backgroundCirclePath = NSBezierPath.bezierPathWithOvalInRect(NSMakeRect( center.x - radius, center.y - radius, radius * 2, radius * 2 )); 163 | backgroundCirclePath.appendBezierPathWithOvalInRect(NSMakeRect( center.x - (radius - thickness), center.y - (radius - thickness), (radius - thickness) * 2 , (radius - thickness) * 2 )); 164 | [backgroundCirclePath addClip]; 165 | var backgroundCircleShape = MSShapeGroup.shapeWithBezierPath(backgroundCirclePath); 166 | var backgroundFill = backgroundCircleShape.style().addStylePartOfType(0); 167 | backgroundFill.color = MSColor.colorWithRed_green_blue_alpha(0.75,0.75,0.75,1.0); 168 | backgroundCircleShape.setName("Background") 169 | group.addLayers([backgroundCircleShape]); 170 | 171 | } 172 | group.addLayers([shape]); 173 | [group resizeToFitChildrenWithOption:0]; 174 | } 175 | } 176 | } 177 | --------------------------------------------------------------------------------