rails_vivus 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0db1a41c628b5f9f467ded76d16367f76ae21496
4
+ data.tar.gz: 564eb04db8fb8b0cabca4a670c1aafc2ab79edf9
5
+ SHA512:
6
+ metadata.gz: cbb38833cfbde467dad7f516e1b8f4ac5fbacd1f65283e9abc62febb151dba9bd81baf1dcbb2f9fa224098e1b179308713c66a62a9f4b2138d2348fc94a49584
7
+ data.tar.gz: ed3fb8d550270daeb2a4d21082915f7f0a84f04fe84989aa0888776df2eadd5c4c906787562b62bd938b679e01c08f900962f7eedebe67e9dabc772caad8c1cd
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2015 Jack A. Huang
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ = RailsVivus
2
+
3
+ This project rocks and uses MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'RailsVivus'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+
21
+
22
+ Bundler::GemHelper.install_tasks
23
+
@@ -0,0 +1,825 @@
1
+ /**
2
+ * vivus - JavaScript library to make drawing animation on SVG
3
+ * @version v0.1.2
4
+ * @link https://github.com/maxwellito/vivus
5
+ * @license MIT
6
+ */
7
+
8
+ 'use strict';
9
+
10
+ (function () {
11
+
12
+ 'use strict';
13
+
14
+ /**
15
+ * Pathformer
16
+ * Beta version
17
+ *
18
+ * Take any SVG version 1.1 and transform
19
+ * child elements to 'path' elements
20
+ *
21
+ * This code is purely forked from
22
+ * https://github.com/Waest/SVGPathConverter
23
+ */
24
+
25
+ /**
26
+ * Class constructor
27
+ *
28
+ * @param {DOM|String} element Dom element of the SVG or id of it
29
+ */
30
+ function Pathformer(element) {
31
+ // Test params
32
+ if (typeof element === 'undefined') {
33
+ throw new Error('Pathformer [constructor]: "element" parameter is required');
34
+ }
35
+
36
+ // Set the element
37
+ if (element.constructor === String) {
38
+ element = document.getElementById(element);
39
+ if (!element) {
40
+ throw new Error('Pathformer [constructor]: "element" parameter is not related to an existing ID');
41
+ }
42
+ }
43
+ if (element.constructor === SVGSVGElement) {
44
+ this.el = element;
45
+ } else {
46
+ throw new Error('Pathformer [constructor]: "element" parameter must be a string or a SVGelement');
47
+ }
48
+
49
+ // Start
50
+ this.scan(element);
51
+ }
52
+
53
+ /**
54
+ * List of tags which can be transformed
55
+ * to path elements
56
+ *
57
+ * @type {Array}
58
+ */
59
+ Pathformer.prototype.TYPES = ['line', 'elipse', 'circle', 'polygon', 'polyline', 'rect'];
60
+
61
+ /**
62
+ * List of attribute names which contain
63
+ * data. This array list them to check if
64
+ * they contain bad values, like percentage.
65
+ *
66
+ * @type {Array}
67
+ */
68
+ Pathformer.prototype.ATTR_WATCH = ['cx', 'cy', 'points', 'r', 'rx', 'ry', 'x', 'x1', 'x2', 'y', 'y1', 'y2'];
69
+
70
+ /**
71
+ * Finds the elements compatible for transform
72
+ * and apply the liked method
73
+ *
74
+ * @param {object} options Object from the constructor
75
+ */
76
+ Pathformer.prototype.scan = function (svg) {
77
+ var fn, element, pathData, pathDom,
78
+ elements = svg.querySelectorAll(this.TYPES.join(','));
79
+ for (var i = 0; i < elements.length; i++) {
80
+ element = elements[i];
81
+ fn = this[element.tagName.toLowerCase() + 'ToPath'];
82
+ pathData = fn(this.parseAttr(element.attributes));
83
+ pathDom = this.pathMaker(element, pathData);
84
+ element.parentNode.replaceChild(pathDom, element);
85
+ }
86
+ };
87
+
88
+
89
+ /**
90
+ * Read `line` element to extract and transform
91
+ * data, to make it ready for a `path` object.
92
+ *
93
+ * @param {DOMelement} element Line element to transform
94
+ * @return {object} Data for a `path` element
95
+ */
96
+ Pathformer.prototype.lineToPath = function (element) {
97
+ var newElement = {};
98
+ newElement.d = 'M' + element.x1 + ',' + element.y1 + 'L' + element.x2 + ',' + element.y2;
99
+ return newElement;
100
+ };
101
+
102
+ /**
103
+ * Read `rect` element to extract and transform
104
+ * data, to make it ready for a `path` object.
105
+ * The radius-border is not taken in charge yet.
106
+ * (your help is more than welcomed)
107
+ *
108
+ * @param {DOMelement} element Rect element to transform
109
+ * @return {object} Data for a `path` element
110
+ */
111
+ Pathformer.prototype.rectToPath = function (element) {
112
+ var newElement = {},
113
+ x = parseFloat(element.x) || 0,
114
+ y = parseFloat(element.y) || 0,
115
+ width = parseFloat(element.width) || 0,
116
+ height = parseFloat(element.height) || 0;
117
+ newElement.d = 'M' + x + ' ' + y + ' ';
118
+ newElement.d += 'L' + (x + width) + ' ' + y + ' ';
119
+ newElement.d += 'L' + (x + width) + ' ' + (y + height) + ' ';
120
+ newElement.d += 'L' + x + ' ' + (y + height) + ' Z';
121
+ return newElement;
122
+ };
123
+
124
+ /**
125
+ * Read `polyline` element to extract and transform
126
+ * data, to make it ready for a `path` object.
127
+ *
128
+ * @param {DOMelement} element Polyline element to transform
129
+ * @return {object} Data for a `path` element
130
+ */
131
+ Pathformer.prototype.polylineToPath = function (element) {
132
+ var newElement = {};
133
+ var points = element.points.split(' ');
134
+ var path = 'M' + points[0];
135
+ for(var i = 1; i < points.length; i++) {
136
+ if (points[i].indexOf(',') !== -1) {
137
+ path += 'L'+points[i];
138
+ }
139
+ }
140
+ newElement.d = path;
141
+ return newElement;
142
+ };
143
+
144
+ /**
145
+ * Read `polygon` element to extract and transform
146
+ * data, to make it ready for a `path` object.
147
+ * This method rely on polylineToPath, because the
148
+ * logic is similar. THe path created is just closed,
149
+ * so it needs an 'Z' at the end.
150
+ *
151
+ * @param {DOMelement} element Polygon element to transform
152
+ * @return {object} Data for a `path` element
153
+ */
154
+ Pathformer.prototype.polygonToPath = function (element) {
155
+ var newElement = Pathformer.prototype.polylineToPath(element);
156
+ newElement.d += 'Z';
157
+ return newElement;
158
+ };
159
+
160
+ /**
161
+ * Read `elipse` element to extract and transform
162
+ * data, to make it ready for a `path` object.
163
+ *
164
+ * @param {DOMelement} element Elipse element to transform
165
+ * @return {object} Data for a `path` element
166
+ */
167
+ Pathformer.prototype.elipseToPath = function (element) {
168
+ var startX = element.cx - element.rx,
169
+ startY = element.cy;
170
+ var endX = parseFloat(element.cx) + parseFloat(element.rx),
171
+ endY = element.cy;
172
+
173
+ var newElement = {};
174
+ newElement.d = 'M' + startX + ',' + startY +
175
+ 'A' + element.rx + ',' + element.ry + ' 0,1,1 ' + endX + ',' + endY +
176
+ 'A' + element.rx + ',' + element.ry + ' 0,1,1 ' + startX + ',' + endY;
177
+ return newElement;
178
+ };
179
+
180
+ /**
181
+ * Read `circle` element to extract and transform
182
+ * data, to make it ready for a `path` object.
183
+ *
184
+ * @param {DOMelement} element Circle element to transform
185
+ * @return {object} Data for a `path` element
186
+ */
187
+ Pathformer.prototype.circleToPath = function (element) {
188
+ var newElement = {};
189
+ var startX = element.cx - element.r,
190
+ startY = element.cy;
191
+ var endX = parseFloat(element.cx) + parseFloat(element.r),
192
+ endY = element.cy;
193
+ newElement.d = 'M' + startX + ',' + startY +
194
+ 'A' + element.r + ',' + element.r + ' 0,1,1 ' + endX + ',' + endY +
195
+ 'A' + element.r + ',' + element.r + ' 0,1,1 ' + startX + ',' + endY;
196
+ return newElement;
197
+ };
198
+
199
+ /**
200
+ * Create `path` elements form original element
201
+ * and prepared objects
202
+ *
203
+ * @param {DOMelement} element Original element to transform
204
+ * @param {object} pathData Path data (from `toPath` methods)
205
+ * @return {DOMelement} Path element
206
+ */
207
+ Pathformer.prototype.pathMaker = function (element, pathData) {
208
+ var i, attr, pathTag = document.createElementNS('http://www.w3.org/2000/svg','path');
209
+ for(i = 0; i < element.attributes.length; i++) {
210
+ attr = element.attributes[i];
211
+ if (this.ATTR_WATCH.indexOf(attr.name) === -1) {
212
+ pathTag.setAttribute(attr.name, attr.value);
213
+ }
214
+ }
215
+ for(i in pathData) {
216
+ pathTag.setAttribute(i, pathData[i]);
217
+ }
218
+ return pathTag;
219
+ };
220
+
221
+ /**
222
+ * Parse attributes of a DOM element to
223
+ * get an object of attribute => value
224
+ *
225
+ * @param {NamedNodeMap} attributes Attributes object from DOM element to parse
226
+ * @return {object} Object of attributes
227
+ */
228
+ Pathformer.prototype.parseAttr = function (element) {
229
+ var attr, output = {};
230
+ for (var i = 0; i < element.length; i++) {
231
+ attr = element[i];
232
+ // Check if no data attribute contains '%', or the transformation is impossible
233
+ if (this.ATTR_WATCH.indexOf(attr.name) !== -1 && attr.value.indexOf('%') !== -1) {
234
+ throw new Error('Pathformer [parseAttr]: a SVG shape got values in percentage. This cannot be transformed into \'path\' tags. Please use \'viewBox\'.');
235
+ }
236
+ output[attr.name] = attr.value;
237
+ }
238
+ return output;
239
+ };
240
+
241
+ 'use strict';
242
+
243
+ var requestAnimFrame, cancelAnimFrame, parsePositiveInt;
244
+
245
+ /**
246
+ * Vivus
247
+ * Beta version
248
+ *
249
+ * Take any SVG and make the animation
250
+ * to give give the impression of live drawing
251
+ *
252
+ * This in more than just inspired from codrops
253
+ * At that point, it's a pure fork.
254
+ */
255
+
256
+ /**
257
+ * Class constructor
258
+ * option structure
259
+ * type: 'delayed'|'async'|'oneByOne'|'script' (to know if the item must be drawn asynchronously or not, default: delayed)
260
+ * duration: <int> (in frames)
261
+ * start: 'inViewport'|'manual'|'autostart' (start automatically the animation, default: inViewport)
262
+ * delay: <int> (delay between the drawing of first and last path)
263
+ *
264
+ * The attribute 'type' is by default on 'delayed'.
265
+ * - 'delayed'
266
+ * all paths are draw at the same time but with a
267
+ * little delay between them before start
268
+ * - 'async'
269
+ * all path are start and finish at the same time
270
+ * - 'oneByOne'
271
+ * only one path is draw at the time
272
+ * the end of the first one will trigger the draw
273
+ * of the next one
274
+ *
275
+ * All these values can be overwritten individually
276
+ * for each path item in the SVG
277
+ * The value of frames will always take the advantage of
278
+ * the duration value.
279
+ * If you fail somewhere, an error will be thrown.
280
+ * Good luck.
281
+ *
282
+ * @constructor
283
+ * @this {Vivus}
284
+ * @param {DOM|String} element Dom element of the SVG or id of it
285
+ * @param {Object} options Options about the animation
286
+ * @param {Function} callback Callback for the end of the animation
287
+ */
288
+ function Vivus (element, options, callback) {
289
+
290
+ // Setup
291
+ this.setElement(element);
292
+ this.setOptions(options);
293
+ this.setCallback(callback);
294
+
295
+ // Set object variables
296
+ this.frameLength = 0;
297
+ this.currentFrame = 0;
298
+ this.map = [];
299
+
300
+ // Start
301
+ new Pathformer(element);
302
+ this.mapping();
303
+ this.starter();
304
+ }
305
+
306
+
307
+ /**
308
+ * Setters
309
+ **************************************
310
+ */
311
+
312
+ /**
313
+ * Check and set the element in the instance
314
+ * The method will not return anything, but will throw an
315
+ * error if the parameter is invalid
316
+ *
317
+ * @param {DOM|String} element SVG Dom element or id of it
318
+ */
319
+ Vivus.prototype.setElement = function (element) {
320
+ // Basic check
321
+ if (typeof element === 'undefined') {
322
+ throw new Error('Vivus [constructor]: "element" parameter is required');
323
+ }
324
+
325
+ // Set the element
326
+ if (element.constructor === String) {
327
+ element = document.getElementById(element);
328
+ if (!element) {
329
+ throw new Error('Vivus [constructor]: "element" parameter is not related to an existing ID');
330
+ }
331
+ }
332
+ if (element.constructor === SVGSVGElement) {
333
+ this.el = element;
334
+ } else {
335
+ throw new Error('Vivus [constructor]: "element" parameter must be a string or a SVGelement');
336
+ }
337
+ };
338
+
339
+ /**
340
+ * Set up user option to the instance
341
+ * The method will not return anything, but will throw an
342
+ * error if the parameter is invalid
343
+ *
344
+ * @param {object} options Object from the constructor
345
+ */
346
+ Vivus.prototype.setOptions = function (options) {
347
+ var allowedTypes = ['delayed', 'async', 'oneByOne', 'scenario', 'scenario-sync'];
348
+ var allowedStarts = ['inViewport', 'manual', 'autostart'];
349
+
350
+ // Basic check
351
+ if (options !== undefined && options.constructor !== Object) {
352
+ throw new Error('Vivus [constructor]: "options" parameter must be an object');
353
+ }
354
+ else {
355
+ options = options || {};
356
+ }
357
+
358
+ // Set the animation type
359
+ if (options.type && allowedTypes.indexOf(options.type) === -1) {
360
+ throw new Error('Vivus [constructor]: ' + options.type + ' is not an existing animation `type`');
361
+ }
362
+ else {
363
+ this.type = options.type || allowedTypes[0];
364
+ }
365
+
366
+ // Set the start type
367
+ if (options.start && allowedStarts.indexOf(options.start) === -1) {
368
+ throw new Error('Vivus [constructor]: ' + options.start + ' is not an existing `start` option');
369
+ }
370
+ else {
371
+ this.start = options.start || allowedStarts[0];
372
+ }
373
+
374
+ this.isIE = (navigator.userAgent.indexOf('MSIE') !== -1);
375
+ this.duration = parsePositiveInt(options.duration, 120);
376
+ this.delay = parsePositiveInt(options.delay, null);
377
+ this.dashGap = parsePositiveInt(options.dashGap, 2);
378
+ this.forceRender = options.hasOwnProperty('forceRender') ? !!options.forceRender : this.isIE;
379
+ this.selfDestroy = !!options.selfDestroy;
380
+
381
+ if (this.delay >= this.duration) {
382
+ throw new Error('Vivus [constructor]: delay must be shorter than duration');
383
+ }
384
+ };
385
+
386
+ /**
387
+ * Set up callback to the instance
388
+ * The method will not return enything, but will throw an
389
+ * error if the parameter is invalid
390
+ *
391
+ * @param {Function} callback Callback for the animation end
392
+ */
393
+ Vivus.prototype.setCallback = function (callback) {
394
+ // Basic check
395
+ if (!!callback && callback.constructor !== Function) {
396
+ throw new Error('Vivus [constructor]: "callback" parameter must be a function');
397
+ }
398
+ this.callback = callback || function () {};
399
+ };
400
+
401
+
402
+ /**
403
+ * Core
404
+ **************************************
405
+ */
406
+
407
+ /**
408
+ * Map the svg, path by path.
409
+ * The method return nothing, it just fill the
410
+ * `map` array. Each item in this array represent
411
+ * a path element from the SVG, with informations for
412
+ * the animation.
413
+ *
414
+ * ```
415
+ * [
416
+ * {
417
+ * el: <DOMobj> the path element
418
+ * length: <number> length of the path line
419
+ * startAt: <number> time start of the path animation (in frames)
420
+ * duration: <number> path animation duration (in frames)
421
+ * },
422
+ * ...
423
+ * ]
424
+ * ```
425
+ *
426
+ */
427
+ Vivus.prototype.mapping = function () {
428
+ var i, paths, path, pAttrs, pathObj, totalLength, lengthMeter, timePoint;
429
+ timePoint = totalLength = lengthMeter = 0;
430
+ paths = this.el.querySelectorAll('path');
431
+
432
+ for (i = 0; i < paths.length; i++) {
433
+ path = paths[i];
434
+ pathObj = {
435
+ el: path,
436
+ length: Math.ceil(path.getTotalLength())
437
+ };
438
+ // Test if the path length is correct
439
+ if (isNaN(pathObj.length)) {
440
+ if (window.console && console.warn) {
441
+ console.warn('Vivus [mapping]: cannot retrieve a path element length', path);
442
+ }
443
+ continue;
444
+ }
445
+ totalLength += pathObj.length;
446
+ this.map.push(pathObj);
447
+ path.style.strokeDasharray = pathObj.length + ' ' + (pathObj.length + this.dashGap);
448
+ path.style.strokeDashoffset = pathObj.length;
449
+
450
+ // Fix IE glitch
451
+ if (this.isIE) {
452
+ pathObj.length += this.dashGap;
453
+ }
454
+ this.renderPath(i);
455
+ }
456
+
457
+ totalLength = totalLength === 0 ? 1 : totalLength;
458
+ this.delay = this.delay === null ? this.duration / 3 : this.delay;
459
+ this.delayUnit = this.delay / (paths.length > 1 ? paths.length - 1 : 1);
460
+
461
+ for (i = 0; i < this.map.length; i++) {
462
+ pathObj = this.map[i];
463
+
464
+ switch (this.type) {
465
+ case 'delayed':
466
+ pathObj.startAt = this.delayUnit * i;
467
+ pathObj.duration = this.duration - this.delay;
468
+ break;
469
+
470
+ case 'oneByOne':
471
+ pathObj.startAt = lengthMeter / totalLength * this.duration;
472
+ pathObj.duration = pathObj.length / totalLength * this.duration;
473
+ break;
474
+
475
+ case 'async':
476
+ pathObj.startAt = 0;
477
+ pathObj.duration = this.duration;
478
+ break;
479
+
480
+ case 'scenario-sync':
481
+ path = paths[i];
482
+ pAttrs = this.parseAttr(path);
483
+ pathObj.startAt = timePoint + (parsePositiveInt(pAttrs['data-delay'], this.delayUnit) || 0);
484
+ pathObj.duration = parsePositiveInt(pAttrs['data-duration'], this.duration);
485
+ timePoint = pAttrs['data-async'] !== undefined ? pathObj.startAt : pathObj.startAt + pathObj.duration;
486
+ this.frameLength = Math.max(this.frameLength, (pathObj.startAt + pathObj.duration));
487
+ break;
488
+
489
+ case 'scenario':
490
+ path = paths[i];
491
+ pAttrs = this.parseAttr(path);
492
+ pathObj.startAt = parsePositiveInt(pAttrs['data-start'], this.delayUnit) || 0;
493
+ pathObj.duration = parsePositiveInt(pAttrs['data-duration'], this.duration);
494
+ this.frameLength = Math.max(this.frameLength, (pathObj.startAt + pathObj.duration));
495
+ break;
496
+ }
497
+ lengthMeter += pathObj.length;
498
+ this.frameLength = this.frameLength || this.duration;
499
+ }
500
+ };
501
+
502
+ /**
503
+ * Interval method to draw the SVG from current
504
+ * position of the animation. It update the value of
505
+ * `currentFrame` and re-trace the SVG.
506
+ *
507
+ * It use this.handle to store the requestAnimationFrame
508
+ * and clear it one the animation is stopped. So this
509
+ * attribute can be used to know if the animation is
510
+ * playing.
511
+ *
512
+ * Once the animation at the end, this method will
513
+ * trigger the Vivus callback.
514
+ *
515
+ */
516
+ Vivus.prototype.drawer = function () {
517
+ var self = this;
518
+ this.currentFrame += this.speed;
519
+
520
+ if (this.currentFrame <= 0) {
521
+ this.stop();
522
+ this.reset();
523
+ } else if (this.currentFrame >= this.frameLength) {
524
+ this.stop();
525
+ this.currentFrame = this.frameLength;
526
+ this.trace();
527
+ if (this.selfDestroy) {
528
+ this.destroy();
529
+ }
530
+ this.callback(this);
531
+ } else {
532
+ this.trace();
533
+ this.handle = requestAnimFrame(function () {
534
+ self.drawer();
535
+ });
536
+ }
537
+ };
538
+
539
+ /**
540
+ * Draw the SVG at the current instant from the
541
+ * `currentFrame` value. Here is where most of the magic is.
542
+ * The trick is to use the `strokeDashoffset` style property.
543
+ *
544
+ * For optimisation reasons, a new property called `progress`
545
+ * is added in each item of `map`. This one contain the current
546
+ * progress of the path element. Only if the new value is different
547
+ * the new value will be applied to the DOM element. This
548
+ * method save a lot of resources to re-render the SVG. And could
549
+ * be improved if the animation couldn't be played forward.
550
+ *
551
+ */
552
+ Vivus.prototype.trace = function () {
553
+ var i, progress, path;
554
+ for (i = 0; i < this.map.length; i++) {
555
+ path = this.map[i];
556
+ progress = (this.currentFrame - path.startAt) / path.duration;
557
+ progress = Math.max(0, Math.min(1, progress));
558
+ if (path.progress !== progress) {
559
+ path.progress = progress;
560
+ path.el.style.strokeDashoffset = Math.floor(path.length * (1 - progress));
561
+ this.renderPath(i);
562
+ }
563
+ }
564
+ };
565
+
566
+ /**
567
+ * Method forcing the browser to re-render a path element
568
+ * from it's index in the map. Depending on the `forceRender`
569
+ * value.
570
+ * The trick is to replace the path element by it's clone.
571
+ * This practice is not recommended because it's asking more
572
+ * ressources, too much DOM manupulation..
573
+ * but it's the only way to let the magic happen on IE.
574
+ * By default, this fallback is only applied on IE.
575
+ *
576
+ * @param {Number} index Path index
577
+ */
578
+ Vivus.prototype.renderPath = function (index) {
579
+ if (this.forceRender && this.map && this.map[index]) {
580
+ var pathObj = this.map[index],
581
+ newPath = pathObj.el.cloneNode(true);
582
+ pathObj.el.parentNode.replaceChild(newPath, pathObj.el);
583
+ pathObj.el = newPath;
584
+ }
585
+ };
586
+
587
+ /**
588
+ * Trigger to start of the animation.
589
+ * Depending on the `start` value, a different script
590
+ * will be applied.
591
+ *
592
+ * If the `start` value is not valid, an error will be thrown.
593
+ * Even if technically, this is impossible.
594
+ *
595
+ */
596
+ Vivus.prototype.starter = function () {
597
+ switch (this.start) {
598
+ case 'manual':
599
+ return;
600
+
601
+ case 'autostart':
602
+ this.play();
603
+ break;
604
+
605
+ case 'inViewport':
606
+ var self = this,
607
+ listener = function () {
608
+ if (self.isInViewport(self.el, 1)) {
609
+ self.play();
610
+ window.removeEventListener('scroll', listener);
611
+ }
612
+ };
613
+ window.addEventListener('scroll', listener);
614
+ listener();
615
+ break;
616
+ }
617
+ };
618
+
619
+
620
+ /**
621
+ * Controls
622
+ **************************************
623
+ */
624
+
625
+ /**
626
+ * Reset the instance to the initial state : undraw
627
+ * Be careful, it just reset the animation, if you're
628
+ * playing the animation, this won't stop it. But just
629
+ * make it start from start.
630
+ *
631
+ */
632
+ Vivus.prototype.reset = function () {
633
+ this.currentFrame = 0;
634
+ this.trace();
635
+ return this;
636
+ };
637
+
638
+ /**
639
+ * Play the animation at the desired speed.
640
+ * Speed must be a valid number (no zero).
641
+ * By default, the speed value is 1.
642
+ * But a negative value is accepted to go forward.
643
+ *
644
+ * And works with float too.
645
+ * But don't forget we are in JavaScript, se be nice
646
+ * with him and give him a 1/2^x value.
647
+ *
648
+ * @param {number} speed Animation speed [optional]
649
+ */
650
+ Vivus.prototype.play = function (speed) {
651
+ if (speed && typeof speed !== 'number') {
652
+ throw new Error('Vivus [play]: invalid speed');
653
+ }
654
+ this.speed = speed || 1;
655
+ if (!this.handle) {
656
+ this.drawer();
657
+ }
658
+ return this;
659
+ };
660
+
661
+ /**
662
+ * Stop the current animation, if on progress.
663
+ * Should not trigger any error.
664
+ *
665
+ */
666
+ Vivus.prototype.stop = function () {
667
+ if (this.handle) {
668
+ cancelAnimFrame(this.handle);
669
+ delete this.handle;
670
+ }
671
+ return this;
672
+ };
673
+
674
+ /**
675
+ * Destroy the instance.
676
+ * Remove all bad styling attributes on all
677
+ * path tags
678
+ *
679
+ */
680
+ Vivus.prototype.destroy = function () {
681
+ var i, path;
682
+ for (i = 0; i < this.map.length; i++) {
683
+ path = this.map[i];
684
+ path.el.style.strokeDashoffset = null;
685
+ path.el.style.strokeDasharray = null;
686
+ this.renderPath(i);
687
+ }
688
+ };
689
+
690
+
691
+ /**
692
+ * Utils methods
693
+ * from Codrops
694
+ **************************************
695
+ */
696
+
697
+ /**
698
+ * Parse attributes of a DOM element to
699
+ * get an object of {attributeName => attributeValue}
700
+ *
701
+ * @param {object} element DOM element to parse
702
+ * @return {object} Object of attributes
703
+ */
704
+ Vivus.prototype.parseAttr = function (element) {
705
+ var attr, output = {};
706
+ if (element && element.attributes) {
707
+ for (var i = 0; i < element.attributes.length; i++) {
708
+ attr = element.attributes[i];
709
+ output[attr.name] = attr.value;
710
+ }
711
+ }
712
+ return output;
713
+ };
714
+
715
+ /**
716
+ * Reply if an element is in the page viewport
717
+ *
718
+ * @param {object} el Element to observe
719
+ * @param {number} h Percentage of height
720
+ * @return {boolean}
721
+ */
722
+ Vivus.prototype.isInViewport = function (el, h) {
723
+ var scrolled = this.scrollY(),
724
+ viewed = scrolled + this.getViewportH(),
725
+ elBCR = el.getBoundingClientRect(),
726
+ elHeight = elBCR.height,
727
+ elTop = scrolled + elBCR.top,
728
+ elBottom = elTop + elHeight;
729
+
730
+ // if 0, the element is considered in the viewport as soon as it enters.
731
+ // if 1, the element is considered in the viewport only when it's fully inside
732
+ // value in percentage (1 >= h >= 0)
733
+ h = h || 0;
734
+
735
+ return (elTop + elHeight * h) <= viewed && (elBottom) >= scrolled;
736
+ };
737
+
738
+ /**
739
+ * Alias for document element
740
+ *
741
+ * @type {DOMelement}
742
+ */
743
+ Vivus.prototype.docElem = window.document.documentElement;
744
+
745
+ /**
746
+ * Get the viewport height in pixels
747
+ *
748
+ * @return {integer} Viewport height
749
+ */
750
+ Vivus.prototype.getViewportH = function () {
751
+ var client = this.docElem.clientHeight,
752
+ inner = window.innerHeight;
753
+
754
+ if (client < inner) {
755
+ return inner;
756
+ }
757
+ else {
758
+ return client;
759
+ }
760
+ };
761
+
762
+ /**
763
+ * Get the page Y offset
764
+ *
765
+ * @return {integer} Page Y offset
766
+ */
767
+ Vivus.prototype.scrollY = function () {
768
+ return window.pageYOffset || this.docElem.scrollTop;
769
+ };
770
+
771
+ /**
772
+ * Alias for `requestAnimationFrame` or
773
+ * `setTimeout` function for deprecated browsers.
774
+ *
775
+ */
776
+ requestAnimFrame = (function () {
777
+ return (
778
+ window.requestAnimationFrame ||
779
+ window.webkitRequestAnimationFrame ||
780
+ window.mozRequestAnimationFrame ||
781
+ window.oRequestAnimationFrame ||
782
+ window.msRequestAnimationFrame ||
783
+ function(/* function */ callback){
784
+ return window.setTimeout(callback, 1000 / 60);
785
+ }
786
+ );
787
+ })();
788
+
789
+ /**
790
+ * Alias for `cancelAnimationFrame` or
791
+ * `cancelTimeout` function for deprecated browsers.
792
+ *
793
+ */
794
+ cancelAnimFrame = (function () {
795
+ return (
796
+ window.cancelAnimationFrame ||
797
+ window.webkitCancelAnimationFrame ||
798
+ window.mozCancelAnimationFrame ||
799
+ window.oCancelAnimationFrame ||
800
+ window.msCancelAnimationFrame ||
801
+ function(id){
802
+ return window.clearTimeout(id);
803
+ }
804
+ );
805
+ })();
806
+
807
+ /**
808
+ * Parse string to integer.
809
+ * If the number is not positive or null
810
+ * the method will return the default value
811
+ * or 0 if undefined
812
+ *
813
+ * @param {string} value String to parse
814
+ * @param {*} defaultValue Value to return if the result parsed is invalid
815
+ * @return {number}
816
+ *
817
+ */
818
+ parsePositiveInt = function (value, defaultValue) {
819
+ var output = parseInt(value, 10);
820
+ return (output >= 0) ? output : defaultValue;
821
+ };
822
+
823
+
824
+ window.Vivus = Vivus;
825
+ }());
@@ -0,0 +1,7 @@
1
+ /**
2
+ * vivus - JavaScript library to make drawing animation on SVG
3
+ * @version v0.1.2
4
+ * @link https://github.com/maxwellito/vivus
5
+ * @license MIT
6
+ */
7
+ "use strict";!function(){function t(t){if("undefined"==typeof t)throw new Error('Pathformer [constructor]: "element" parameter is required');if(t.constructor===String&&(t=document.getElementById(t),!t))throw new Error('Pathformer [constructor]: "element" parameter is not related to an existing ID');if(t.constructor!==SVGSVGElement)throw new Error('Pathformer [constructor]: "element" parameter must be a string or a SVGelement');this.el=t,this.scan(t)}function e(e,r,n){this.setElement(e),this.setOptions(r),this.setCallback(n),this.frameLength=0,this.currentFrame=0,this.map=[],new t(e),this.mapping(),this.starter()}t.prototype.TYPES=["line","elipse","circle","polygon","polyline","rect"],t.prototype.ATTR_WATCH=["cx","cy","points","r","rx","ry","x","x1","x2","y","y1","y2"],t.prototype.scan=function(t){for(var e,r,n,o,a=t.querySelectorAll(this.TYPES.join(",")),i=0;i<a.length;i++)r=a[i],e=this[r.tagName.toLowerCase()+"ToPath"],n=e(this.parseAttr(r.attributes)),o=this.pathMaker(r,n),r.parentNode.replaceChild(o,r)},t.prototype.lineToPath=function(t){var e={};return e.d="M"+t.x1+","+t.y1+"L"+t.x2+","+t.y2,e},t.prototype.rectToPath=function(t){var e={},r=parseFloat(t.x)||0,n=parseFloat(t.y)||0,o=parseFloat(t.width)||0,a=parseFloat(t.height)||0;return e.d="M"+r+" "+n+" ",e.d+="L"+(r+o)+" "+n+" ",e.d+="L"+(r+o)+" "+(n+a)+" ",e.d+="L"+r+" "+(n+a)+" Z",e},t.prototype.polylineToPath=function(t){for(var e={},r=t.points.split(" "),n="M"+r[0],o=1;o<r.length;o++)-1!==r[o].indexOf(",")&&(n+="L"+r[o]);return e.d=n,e},t.prototype.polygonToPath=function(e){var r=t.prototype.polylineToPath(e);return r.d+="Z",r},t.prototype.elipseToPath=function(t){var e=t.cx-t.rx,r=t.cy,n=parseFloat(t.cx)+parseFloat(t.rx),o=t.cy,a={};return a.d="M"+e+","+r+"A"+t.rx+","+t.ry+" 0,1,1 "+n+","+o+"A"+t.rx+","+t.ry+" 0,1,1 "+e+","+o,a},t.prototype.circleToPath=function(t){var e={},r=t.cx-t.r,n=t.cy,o=parseFloat(t.cx)+parseFloat(t.r),a=t.cy;return e.d="M"+r+","+n+"A"+t.r+","+t.r+" 0,1,1 "+o+","+a+"A"+t.r+","+t.r+" 0,1,1 "+r+","+a,e},t.prototype.pathMaker=function(t,e){var r,n,o=document.createElementNS("http://www.w3.org/2000/svg","path");for(r=0;r<t.attributes.length;r++)n=t.attributes[r],-1===this.ATTR_WATCH.indexOf(n.name)&&o.setAttribute(n.name,n.value);for(r in e)o.setAttribute(r,e[r]);return o},t.prototype.parseAttr=function(t){for(var e,r={},n=0;n<t.length;n++){if(e=t[n],-1!==this.ATTR_WATCH.indexOf(e.name)&&-1!==e.value.indexOf("%"))throw new Error("Pathformer [parseAttr]: a SVG shape got values in percentage. This cannot be transformed into 'path' tags. Please use 'viewBox'.");r[e.name]=e.value}return r};var r,n,o;e.prototype.setElement=function(t){if("undefined"==typeof t)throw new Error('Vivus [constructor]: "element" parameter is required');if(t.constructor===String&&(t=document.getElementById(t),!t))throw new Error('Vivus [constructor]: "element" parameter is not related to an existing ID');if(t.constructor!==SVGSVGElement)throw new Error('Vivus [constructor]: "element" parameter must be a string or a SVGelement');this.el=t},e.prototype.setOptions=function(t){var e=["delayed","async","oneByOne","scenario","scenario-sync"],r=["inViewport","manual","autostart"];if(void 0!==t&&t.constructor!==Object)throw new Error('Vivus [constructor]: "options" parameter must be an object');if(t=t||{},t.type&&-1===e.indexOf(t.type))throw new Error("Vivus [constructor]: "+t.type+" is not an existing animation `type`");if(this.type=t.type||e[0],t.start&&-1===r.indexOf(t.start))throw new Error("Vivus [constructor]: "+t.start+" is not an existing `start` option");if(this.start=t.start||r[0],this.isIE=-1!==navigator.userAgent.indexOf("MSIE"),this.duration=o(t.duration,120),this.delay=o(t.delay,null),this.dashGap=o(t.dashGap,2),this.forceRender=t.hasOwnProperty("forceRender")?!!t.forceRender:this.isIE,this.selfDestroy=!!t.selfDestroy,this.delay>=this.duration)throw new Error("Vivus [constructor]: delay must be shorter than duration")},e.prototype.setCallback=function(t){if(t&&t.constructor!==Function)throw new Error('Vivus [constructor]: "callback" parameter must be a function');this.callback=t||function(){}},e.prototype.mapping=function(){var t,e,r,n,a,i,s,h;for(h=i=s=0,e=this.el.querySelectorAll("path"),t=0;t<e.length;t++)r=e[t],a={el:r,length:Math.ceil(r.getTotalLength())},isNaN(a.length)?window.console&&console.warn&&console.warn("Vivus [mapping]: cannot retrieve a path element length",r):(i+=a.length,this.map.push(a),r.style.strokeDasharray=a.length+" "+(a.length+this.dashGap),r.style.strokeDashoffset=a.length,this.isIE&&(a.length+=this.dashGap),this.renderPath(t));for(i=0===i?1:i,this.delay=null===this.delay?this.duration/3:this.delay,this.delayUnit=this.delay/(e.length>1?e.length-1:1),t=0;t<this.map.length;t++){switch(a=this.map[t],this.type){case"delayed":a.startAt=this.delayUnit*t,a.duration=this.duration-this.delay;break;case"oneByOne":a.startAt=s/i*this.duration,a.duration=a.length/i*this.duration;break;case"async":a.startAt=0,a.duration=this.duration;break;case"scenario-sync":r=e[t],n=this.parseAttr(r),a.startAt=h+(o(n["data-delay"],this.delayUnit)||0),a.duration=o(n["data-duration"],this.duration),h=void 0!==n["data-async"]?a.startAt:a.startAt+a.duration,this.frameLength=Math.max(this.frameLength,a.startAt+a.duration);break;case"scenario":r=e[t],n=this.parseAttr(r),a.startAt=o(n["data-start"],this.delayUnit)||0,a.duration=o(n["data-duration"],this.duration),this.frameLength=Math.max(this.frameLength,a.startAt+a.duration)}s+=a.length,this.frameLength=this.frameLength||this.duration}},e.prototype.drawer=function(){var t=this;this.currentFrame+=this.speed,this.currentFrame<=0?(this.stop(),this.reset()):this.currentFrame>=this.frameLength?(this.stop(),this.currentFrame=this.frameLength,this.trace(),this.selfDestroy&&this.destroy(),this.callback(this)):(this.trace(),this.handle=r(function(){t.drawer()}))},e.prototype.trace=function(){var t,e,r;for(t=0;t<this.map.length;t++)r=this.map[t],e=(this.currentFrame-r.startAt)/r.duration,e=Math.max(0,Math.min(1,e)),r.progress!==e&&(r.progress=e,r.el.style.strokeDashoffset=Math.floor(r.length*(1-e)),this.renderPath(t))},e.prototype.renderPath=function(t){if(this.forceRender&&this.map&&this.map[t]){var e=this.map[t],r=e.el.cloneNode(!0);e.el.parentNode.replaceChild(r,e.el),e.el=r}},e.prototype.starter=function(){switch(this.start){case"manual":return;case"autostart":this.play();break;case"inViewport":var t=this,e=function(){t.isInViewport(t.el,1)&&(t.play(),window.removeEventListener("scroll",e))};window.addEventListener("scroll",e),e()}},e.prototype.reset=function(){return this.currentFrame=0,this.trace(),this},e.prototype.play=function(t){if(t&&"number"!=typeof t)throw new Error("Vivus [play]: invalid speed");return this.speed=t||1,this.handle||this.drawer(),this},e.prototype.stop=function(){return this.handle&&(n(this.handle),delete this.handle),this},e.prototype.destroy=function(){var t,e;for(t=0;t<this.map.length;t++)e=this.map[t],e.el.style.strokeDashoffset=null,e.el.style.strokeDasharray=null,this.renderPath(t)},e.prototype.parseAttr=function(t){var e,r={};if(t&&t.attributes)for(var n=0;n<t.attributes.length;n++)e=t.attributes[n],r[e.name]=e.value;return r},e.prototype.isInViewport=function(t,e){var r=this.scrollY(),n=r+this.getViewportH(),o=t.getBoundingClientRect(),a=o.height,i=r+o.top,s=i+a;return e=e||0,n>=i+a*e&&s>=r},e.prototype.docElem=window.document.documentElement,e.prototype.getViewportH=function(){var t=this.docElem.clientHeight,e=window.innerHeight;return e>t?e:t},e.prototype.scrollY=function(){return window.pageYOffset||this.docElem.scrollTop},r=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)}}(),n=function(){return window.cancelAnimationFrame||window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||window.oCancelAnimationFrame||window.msCancelAnimationFrame||function(t){return window.clearTimeout(t)}}(),o=function(t,e){var r=parseInt(t,10);return r>=0?r:e},window.Vivus=e}();
@@ -0,0 +1,3 @@
1
+ module RailsVivus
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,2 @@
1
+ module RailsVivus
2
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :rails_vivus do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails_vivus
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jack A. Huang
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 4.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 4.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec-rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Vivus (SVG animation) assets for Rails
42
+ email:
43
+ - huynhquancam@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - MIT-LICENSE
49
+ - README.rdoc
50
+ - Rakefile
51
+ - app/assets/javascripts/vivus.js
52
+ - app/assets/javascripts/vivus.min.js
53
+ - lib/rails_vivus.rb
54
+ - lib/rails_vivus/version.rb
55
+ - lib/tasks/rails_vivus_tasks.rake
56
+ homepage: http://github.com/huynhquancam/rails_vivus
57
+ licenses:
58
+ - MIT
59
+ metadata: {}
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 2.4.5
77
+ signing_key:
78
+ specification_version: 4
79
+ summary: Vivus (SVG animation) assets for Rails
80
+ test_files: []