├── LICENSE ├── README.md ├── jquery.ht-analog-clock.js └── jquery.ht-analog-clock.min.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 dexise 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Version](https://img.shields.io/github/v/tag/dexise/jQuery-Analog-Clock?label=Version)](https://github.com/dexise/jQuery-Analog-Clock/releases) 2 | [![Demo](https://img.shields.io/badge/Demo-Live-green?color=blueviolet)](https://docs.handy.tools/analog-clock-jquery-plugin/v1.0/configuration-and-preset) 3 | [![Docs](https://img.shields.io/badge/Docs-Read-blue)](https://docs.handy.tools/analog-clock-jquery-plugin/v1.0/getting-started) 4 | [![License](https://img.shields.io/github/license/dexise/jQuery-Analog-Clock?label=License)](https://github.com/dexise/jQuery-Analog-Clock/blob/master/LICENSE) 5 | [![Size](https://img.shields.io/github/size/dexise/jQuery-Analog-Clock/jquery.ht-analog-clock.min.js?color=00aa00&label=Size)](https://github.com/dexise/jQuery-Analog-Clock/releases) 6 | [![Play it](https://img.shields.io/badge/-Try%20it%20Yourself%20on%20CodePen-brightgreen)](https://codepen.io/dexise/pen/vYOpQEG) 7 | 8 | # jQuery Analog Clock 9 | A customizable analog clock based on Vanilla JS as a jQuery addon 10 | 11 | This is a jQuery Plugin and you can apply it to any dom element on the page. 12 | This plugin doesn't use any bitmap and will resize without losing quality as it uses HTML5 Canvas drawing API. 13 | A [full documentation](https://docs.handy.tools/analog-clock-jquery-plugin/v1.0/getting-started) and [live demo](https://docs.handy.tools/analog-clock-jquery-plugin/v1.0/configuration-and-preset) is also provided. 14 | 15 | # Screen Cast: 16 | ![Screen Cast](https://static.xise.io/asset/animated/jquery-analog-clock.gif) 17 | 18 | # Features: 19 | - jQuery Plugin & Full Selector Support 20 | - Resizable & Responsive 21 | - No Bitmap ( Ultra Quality ) 22 | - Timezone Support 23 | - Customizable Interface 24 | - Customizable Font 25 | - 7 Interface Presets out of the box 26 | - Light & Optimized ( Size: 4.7KB ) 27 | 28 | # See more: 29 | - [Try it yourself on CodePen](https://codepen.io/dexise/pen/vYOpQEG) 30 | - [Live Demo](https://docs.handy.tools/analog-clock-jquery-plugin/v1.0/configuration-and-preset) 31 | - [Full Documentation](https://docs.handy.tools/analog-clock-jquery-plugin/v1.0/getting-started) 32 | -------------------------------------------------------------------------------- /jquery.ht-analog-clock.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 3 | function core(id, preset, options) { 4 | var canvas = $(id)[0]; 5 | var ctx = canvas.getContext('2d'); 6 | var bound = canvas.height; 7 | var safepad = 0; 8 | if (preset.hasShadow) { 9 | safepad = preset.shadowBlur; 10 | } 11 | var radius = canvas.height / 2 - safepad; 12 | var secondStep = 2 * Math.PI / 60; 13 | var hourStep = 2 * Math.PI / 12; 14 | 15 | var initialize = function () { 16 | $(canvas).css('max-width', '100%'); 17 | $(canvas).css('width', $(canvas).css('height')); 18 | canvas.width = canvas.height; 19 | if (preset.hasShadow) { 20 | ctx.shadowOffsetX = 0.0; 21 | ctx.shadowOffsetY = 0.0; 22 | ctx.shadowBlur = preset.shadowBlur; 23 | ctx.shadowColor = preset.shadowColor; 24 | } 25 | 26 | draw(); 27 | }; 28 | 29 | 30 | var p2v = function (value) { 31 | return value / 100.0 * radius; 32 | }; 33 | 34 | 35 | var drawMajorLines = function () { 36 | ctx.lineWidth = p2v(preset.majorTicksLength); 37 | ctx.strokeStyle = preset.majorTicksColor; 38 | 39 | for (var i = 1; i <= 12; i++) { 40 | ctx.beginPath(); 41 | ctx.arc(radius + safepad, radius + safepad, radius - ctx.lineWidth / 2, i * hourStep - p2v(preset.majorTicksWidth) / 2, i * hourStep + p2v(preset.majorTicksWidth) / 2); 42 | ctx.stroke(); 43 | } 44 | }; 45 | 46 | 47 | var drawMinorLines = function () { 48 | ctx.lineWidth = p2v(preset.minorTicksLength); 49 | ctx.strokeStyle = preset.minorTicksColor; 50 | 51 | for (var i = 1; i <= 60; i++) { 52 | ctx.beginPath(); 53 | ctx.arc(radius + safepad, radius + safepad, radius - ctx.lineWidth / 2, i * secondStep - p2v(preset.minorTicksWidth) / 2, i * secondStep + p2v(preset.minorTicksWidth) / 2); 54 | ctx.stroke(); 55 | } 56 | }; 57 | 58 | 59 | var drawBorder = function () { 60 | ctx.strokeStyle = preset.borderColor; 61 | ctx.lineWidth = p2v(preset.borderWidth); 62 | ctx.beginPath(); 63 | ctx.arc(radius + safepad, radius + safepad, radius - ctx.lineWidth / 2, 0.0, 2 * Math.PI); 64 | ctx.stroke(); 65 | }; 66 | 67 | 68 | var drawFill = function () { 69 | ctx.fillStyle = preset.fillColor; 70 | ctx.lineWidth = p2v(preset.borderWidth); 71 | ctx.beginPath(); 72 | ctx.arc(radius + safepad, radius + safepad, radius - ctx.lineWidth, 0.0, 2 * Math.PI); 73 | ctx.fill(); 74 | }; 75 | 76 | 77 | var drawHandle = function (angle, lengthPercent, widthPercent, color) { 78 | var x = angle - Math.PI / 2; 79 | x = Math.cos(x) * p2v(lengthPercent); 80 | var x_1 = angle - Math.PI / 2; 81 | var y = Math.sin(x_1) * p2v(lengthPercent); 82 | ctx.lineWidth = p2v(widthPercent); 83 | ctx.strokeStyle = color; 84 | ctx.beginPath(); 85 | ctx.moveTo(radius + safepad, radius + safepad); 86 | ctx.lineTo(radius + safepad + x, radius + safepad + y); 87 | ctx.stroke(); 88 | }; 89 | 90 | 91 | var drawTexts = function () { 92 | for (var i = 1; i <= 12; i++) { 93 | var angle = i * hourStep; 94 | var x = angle - Math.PI / 2; 95 | x = Math.cos(x) * p2v(80.0); 96 | var x_1 = angle - Math.PI / 2; 97 | var y = Math.sin(x_1) * p2v(80.0); 98 | 99 | ctx.textAlign = 'center'; 100 | ctx.textBaseline = 'middle'; 101 | ctx.font = p2v(preset.fontSize).toString() + 'px ' + preset.fontName; 102 | ctx.fillStyle = preset.fontColor; 103 | ctx.beginPath(); 104 | ctx.fillText(i.toString(), radius + safepad + x, radius + safepad + y); 105 | ctx.stroke(); 106 | } 107 | }; 108 | 109 | 110 | var drawPin = function () { 111 | ctx.fillStyle = preset.pinColor; 112 | ctx.beginPath(); 113 | ctx.arc(radius + safepad, radius + safepad, p2v(preset.pinRadius), 0.0, 2 * Math.PI); 114 | ctx.fill(); 115 | }; 116 | 117 | 118 | var changeTimezone = function (date, ianatz) { 119 | var invdate = new Date(date.toLocaleString('en-US', {timeZone: ianatz})); 120 | var diff = date.getTime() - invdate.getTime(); 121 | return new Date(date.getTime() - diff); 122 | }; 123 | 124 | 125 | var draw = function () { 126 | ctx.clearRect(0.0, 0.0, bound, bound); 127 | ctx.lineCap = 'butt'; 128 | 129 | if (preset.drawFill) { 130 | drawFill(); 131 | } 132 | 133 | if (preset.drawMinorTicks) { 134 | drawMinorLines(); 135 | } 136 | 137 | if (preset.drawMajorTicks) { 138 | drawMajorLines(); 139 | } 140 | 141 | if (preset.drawBorder) { 142 | drawBorder(); 143 | } 144 | 145 | if (preset.drawTexts) { 146 | drawTexts(); 147 | } 148 | 149 | var date = new Date(); 150 | if (options.timezone) { 151 | date = changeTimezone(date, options.timezone); 152 | } 153 | 154 | var s = date.getSeconds(); 155 | var m = date.getMinutes(); 156 | var h = date.getHours(); 157 | m += s / 60.0; 158 | h += m / 60.0; 159 | ctx.lineCap = 'round'; 160 | drawHandle(h * hourStep, preset.hourHandLength, preset.hourHandWidth, preset.hourHandColor); 161 | drawHandle(m * secondStep, preset.minuteHandLength, preset.minuteHandWidth, preset.minuteHandColor); 162 | if (preset.drawSecondHand) { 163 | drawHandle(s * secondStep, preset.secondHandLength, preset.secondHandWidth, preset.secondHandColor); 164 | } 165 | 166 | if (preset.drawPin) { 167 | drawPin(); 168 | } 169 | 170 | window.requestAnimationFrame(function () { 171 | draw(this); 172 | }); 173 | }; 174 | 175 | 176 | initialize(); 177 | } 178 | 179 | 180 | $.fn.htAnalogClock = function (preset, options) { 181 | return this.each(function () { 182 | var _preset = $.extend({}, htAnalogClock.preset_default, preset || {}); 183 | var _options = $.extend({}, $.fn.htAnalogClock.defaultOptions, options || {}); 184 | core(this, _preset, _options); 185 | }); 186 | }; 187 | 188 | 189 | $.fn.htAnalogClock.defaultOptions = { 190 | timezone: null 191 | }; 192 | 193 | }(jQuery)); 194 | 195 | 196 | function htAnalogClock() {} 197 | htAnalogClock.preset_default = { 198 | hasShadow: true, 199 | shadowColor: "#000", 200 | shadowBlur: 10, 201 | 202 | drawSecondHand: true, 203 | drawMajorTicks: true, 204 | drawMinorTicks: true, 205 | drawBorder: true, 206 | drawFill: true, 207 | drawTexts: true, 208 | drawPin: true, 209 | 210 | majorTicksColor: "#f88", 211 | minorTicksColor: "#fa0", 212 | 213 | majorTicksLength: 10.0, 214 | minorTicksLength: 7.0, 215 | majorTicksWidth: 0.005, 216 | minorTicksWidth: 0.0025, 217 | 218 | fillColor: "#333", 219 | pinColor: "#f88", 220 | pinRadius: 5.0, 221 | 222 | borderColor: "#000", 223 | borderWidth: 2.0, 224 | 225 | secondHandColor: "#f00", 226 | minuteHandColor: "#fff", 227 | hourHandColor: "#fff", 228 | 229 | fontColor: "#fff", 230 | fontName: "Tahoma", 231 | fontSize: 10.0, 232 | 233 | secondHandLength: 90.0, 234 | minuteHandLength: 70.0, 235 | hourHandLength: 50.0, 236 | 237 | secondHandWidth: 1.0, 238 | minuteHandWidth: 2.0, 239 | hourHandWidth: 3.0 240 | }; 241 | 242 | 243 | htAnalogClock.preset_gray_fantastic = { 244 | minorTicksLength: 100.0, 245 | minorTicksColor: "rgba(255, 255, 255, 0.2)", 246 | majorTicksLength: 100.0, 247 | majorTicksColor: "rgba(255, 255, 255, 0.6)", 248 | pinColor: "#aaa", 249 | pinRadius: 5.0, 250 | hourHandColor: "#fff", 251 | hourHandWidth: 5.0, 252 | minuteHandColor: "#eee", 253 | minuteHandWidth: 3.0, 254 | secondHandLength: 95.0 255 | }; 256 | 257 | 258 | htAnalogClock.preset_black_bolded = { 259 | drawBorder: false, 260 | majorTicksColor: "rgba(255, 150, 150, 0.8)", 261 | majorTicksWidth: 0.05, 262 | drawMinorTicks: false, 263 | fillColor: "#000" 264 | }; 265 | 266 | 267 | htAnalogClock.preset_white_nice = { 268 | fillColor: "#fff", 269 | hourHandColor: "#000", 270 | minuteHandColor: "#000", 271 | fontColor: "#333", 272 | majorTicksColor: "#222", 273 | minorTicksColor: "#555" 274 | }; 275 | 276 | 277 | htAnalogClock.preset_ocean_blue = { 278 | fillColor: "#4460cb", 279 | hourHandColor: "#fff", 280 | minuteHandColor: "#fff", 281 | fontColor: "#ddd", 282 | majorTicksColor: "#bbb", 283 | minorTicksColor: "#aaa", 284 | fontName: "Sahel FD", 285 | fontSize: 15.0, 286 | secondHandColor: "#f80" 287 | }; 288 | 289 | 290 | htAnalogClock.preset_nice_bolded = { 291 | secondHandWidth: 5.0, 292 | hourHandWidth: 10.0, 293 | minuteHandWidth: 7.0, 294 | pinRadius: 10.0, 295 | pinColor: "#fff", 296 | fillColor: "#444", 297 | drawTexts: false, 298 | majorTicksWidth: 0.07, 299 | minorTicksWidth: 0.03, 300 | majorTicksLength: 50.0, 301 | minorTicksLength: 25.0, 302 | majorTicksColor: "rgba(255, 150, 0, 0.6)", 303 | minorTicksColor: "rgba(0, 150, 250, 0.5)" 304 | }; 305 | 306 | htAnalogClock.preset_modern_dark = { 307 | majorTicksLength: 50.0, 308 | minorTicksLength: 50.0, 309 | majorTicksWidth: 0.02, 310 | minorTicksWidth: 0.0075, 311 | 312 | fillColor: "#333", 313 | pinColor: "#000", 314 | pinRadius: 90.0, 315 | 316 | borderColor: "transparent", 317 | 318 | secondHandColor: "#0f0", 319 | minuteHandColor: "#fff", 320 | hourHandColor: "#fff", 321 | 322 | secondHandLength: 100.0, 323 | minuteHandLength: 100.0, 324 | hourHandLength: 97.0, 325 | 326 | secondHandWidth: 5.0, 327 | minuteHandWidth: 3.0, 328 | hourHandWidth: 10.0 329 | }; 330 | -------------------------------------------------------------------------------- /jquery.ht-analog-clock.min.js: -------------------------------------------------------------------------------- 1 | (function(b){function a(n,q,i){var h=b(n)[0];var r=h.getContext("2d");var d=h.height;var s=0;if(q.hasShadow){s=q.shadowBlur}var j=h.height/2-s;var f=2*Math.PI/60;var c=2*Math.PI/12;var e=function(){b(h).css("max-width","100%");b(h).css("width",b(h).css("height"));h.width=h.height;if(q.hasShadow){r.shadowOffsetX=0;r.shadowOffsetY=0;r.shadowBlur=q.shadowBlur;r.shadowColor=q.shadowColor}l()};var m=function(x){return x/100*j};var o=function(){r.lineWidth=m(q.majorTicksLength);r.strokeStyle=q.majorTicksColor;for(var x=1;x<=12;x++){r.beginPath();r.arc(j+s,j+s,j-r.lineWidth/2,x*c-m(q.majorTicksWidth)/2,x*c+m(q.majorTicksWidth)/2);r.stroke()}};var w=function(){r.lineWidth=m(q.minorTicksLength);r.strokeStyle=q.minorTicksColor;for(var x=1;x<=60;x++){r.beginPath();r.arc(j+s,j+s,j-r.lineWidth/2,x*f-m(q.minorTicksWidth)/2,x*f+m(q.minorTicksWidth)/2);r.stroke()}};var p=function(){r.strokeStyle=q.borderColor;r.lineWidth=m(q.borderWidth);r.beginPath();r.arc(j+s,j+s,j-r.lineWidth/2,0,2*Math.PI);r.stroke()};var t=function(){r.fillStyle=q.fillColor;r.lineWidth=m(q.borderWidth);r.beginPath();r.arc(j+s,j+s,j-r.lineWidth,0,2*Math.PI);r.fill()};var u=function(E,D,C,B){var z=E-Math.PI/2;z=Math.cos(z)*m(D);var A=E-Math.PI/2;var F=Math.sin(A)*m(D);r.lineWidth=m(C);r.strokeStyle=B;r.beginPath();r.moveTo(j+s,j+s);r.lineTo(j+s+z,j+s+F);r.stroke()};var g=function(){for(var B=1;B<=12;B++){var C=B*c;var z=C-Math.PI/2;z=Math.cos(z)*m(80);var A=C-Math.PI/2;var D=Math.sin(A)*m(80);r.textAlign="center";r.textBaseline="middle";r.font=m(q.fontSize).toString()+"px "+q.fontName;r.fillStyle=q.fontColor;r.beginPath();r.fillText(B.toString(),j+s+z,j+s+D);r.stroke()}};var k=function(){r.fillStyle=q.pinColor;r.beginPath();r.arc(j+s,j+s,m(q.pinRadius),0,2*Math.PI);r.fill()};var v=function(y,x){var A=new Date(y.toLocaleString("en-US",{timeZone:x}));var z=y.getTime()-A.getTime();return new Date(y.getTime()-z)};var l=function(){r.clearRect(0,0,d,d);r.lineCap="butt";if(q.drawFill){t()}if(q.drawMinorTicks){w()}if(q.drawMajorTicks){o()}if(q.drawBorder){p()}if(q.drawTexts){g()}var y=new Date();if(i.timezone){y=v(y,i.timezone)}var A=y.getSeconds();var x=y.getMinutes();var z=y.getHours();x+=A/60;z+=x/60;r.lineCap="round";u(z*c,q.hourHandLength,q.hourHandWidth,q.hourHandColor);u(x*f,q.minuteHandLength,q.minuteHandWidth,q.minuteHandColor);if(q.drawSecondHand){u(A*f,q.secondHandLength,q.secondHandWidth,q.secondHandColor)}if(q.drawPin){k()}window.requestAnimationFrame(function(){l(this)})};e()}b.fn.htAnalogClock=function(d,c){return this.each(function(){var f=b.extend({},htAnalogClock.preset_default,d||{});var e=b.extend({},b.fn.htAnalogClock.defaultOptions,c||{});a(this,f,e)})};b.fn.htAnalogClock.defaultOptions={timezone:null}}(jQuery));function htAnalogClock(){}htAnalogClock.preset_default={hasShadow:true,shadowColor:"#000",shadowBlur:10,drawSecondHand:true,drawMajorTicks:true,drawMinorTicks:true,drawBorder:true,drawFill:true,drawTexts:true,drawPin:true,majorTicksColor:"#f88",minorTicksColor:"#fa0",majorTicksLength:10,minorTicksLength:7,majorTicksWidth:0.005,minorTicksWidth:0.0025,fillColor:"#333",pinColor:"#f88",pinRadius:5,borderColor:"#000",borderWidth:2,secondHandColor:"#f00",minuteHandColor:"#fff",hourHandColor:"#fff",fontColor:"#fff",fontName:"Tahoma",fontSize:10,secondHandLength:90,minuteHandLength:70,hourHandLength:50,secondHandWidth:1,minuteHandWidth:2,hourHandWidth:3};htAnalogClock.preset_gray_fantastic={minorTicksLength:100,minorTicksColor:"rgba(255, 255, 255, 0.2)",majorTicksLength:100,majorTicksColor:"rgba(255, 255, 255, 0.6)",pinColor:"#aaa",pinRadius:5,hourHandColor:"#fff",hourHandWidth:5,minuteHandColor:"#eee",minuteHandWidth:3,secondHandLength:95};htAnalogClock.preset_black_bolded={drawBorder:false,majorTicksColor:"rgba(255, 150, 150, 0.8)",majorTicksWidth:0.05,drawMinorTicks:false,fillColor:"#000"};htAnalogClock.preset_white_nice={fillColor:"#fff",hourHandColor:"#000",minuteHandColor:"#000",fontColor:"#333",majorTicksColor:"#222",minorTicksColor:"#555"};htAnalogClock.preset_ocean_blue={fillColor:"#4460cb",hourHandColor:"#fff",minuteHandColor:"#fff",fontColor:"#ddd",majorTicksColor:"#bbb",minorTicksColor:"#aaa",fontName:"Sahel FD",fontSize:15,secondHandColor:"#f80"};htAnalogClock.preset_nice_bolded={secondHandWidth:5,hourHandWidth:10,minuteHandWidth:7,pinRadius:10,pinColor:"#fff",fillColor:"#444",drawTexts:false,majorTicksWidth:0.07,minorTicksWidth:0.03,majorTicksLength:50,minorTicksLength:25,majorTicksColor:"rgba(255, 150, 0, 0.6)",minorTicksColor:"rgba(0, 150, 250, 0.5)"};htAnalogClock.preset_modern_dark={majorTicksLength:50,minorTicksLength:50,majorTicksWidth:0.02,minorTicksWidth:0.0075,fillColor:"#333",pinColor:"#000",pinRadius:90,borderColor:"transparent",secondHandColor:"#0f0",minuteHandColor:"#fff",hourHandColor:"#fff",secondHandLength:100,minuteHandLength:100,hourHandLength:97,secondHandWidth:5,minuteHandWidth:3,hourHandWidth:10}; --------------------------------------------------------------------------------