├── .gitattributes
├── .gitignore
├── README.md
├── demos
├── 1
│ ├── demo1.html
│ ├── demo1.js
│ ├── maze.png
│ ├── path.svg
│ └── styles.css
├── 2
│ ├── demo2.html
│ └── fire.png
└── 3
│ ├── demo3.html
│ ├── demo3.js
│ ├── maze.png
│ ├── path.svg
│ └── styles.css
├── index.html
└── pathAnimator.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 | *.sln merge=union
7 | *.csproj merge=union
8 | *.vbproj merge=union
9 | *.fsproj merge=union
10 | *.dbproj merge=union
11 |
12 | # Standard to msysgit
13 | *.doc diff=astextplain
14 | *.DOC diff=astextplain
15 | *.docx diff=astextplain
16 | *.DOCX diff=astextplain
17 | *.dot diff=astextplain
18 | *.DOT diff=astextplain
19 | *.pdf diff=astextplain
20 | *.PDF diff=astextplain
21 | *.rtf diff=astextplain
22 | *.RTF diff=astextplain
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #################
2 | ## Eclipse
3 | #################
4 |
5 | *.pydevproject
6 | .project
7 | .metadata
8 | bin/
9 | tmp/
10 | *.tmp
11 | *.bak
12 | *.swp
13 | *~.nib
14 | local.properties
15 | .classpath
16 | .settings/
17 | .loadpath
18 |
19 | # External tool builders
20 | .externalToolBuilders/
21 |
22 | # Locally stored "Eclipse launch configurations"
23 | *.launch
24 |
25 | # CDT-specific
26 | .cproject
27 |
28 | # PDT-specific
29 | .buildpath
30 |
31 |
32 | #################
33 | ## Visual Studio
34 | #################
35 |
36 | ## Ignore Visual Studio temporary files, build results, and
37 | ## files generated by popular Visual Studio add-ons.
38 |
39 | # User-specific files
40 | *.suo
41 | *.user
42 | *.sln.docstates
43 |
44 | # Build results
45 | [Dd]ebug/
46 | [Rr]elease/
47 | *_i.c
48 | *_p.c
49 | *.ilk
50 | *.meta
51 | *.obj
52 | *.pch
53 | *.pdb
54 | *.pgc
55 | *.pgd
56 | *.rsp
57 | *.sbr
58 | *.tlb
59 | *.tli
60 | *.tlh
61 | *.tmp
62 | *.vspscc
63 | .builds
64 | *.dotCover
65 |
66 | ## TODO: If you have NuGet Package Restore enabled, uncomment this
67 | #packages/
68 |
69 | # Visual C++ cache files
70 | ipch/
71 | *.aps
72 | *.ncb
73 | *.opensdf
74 | *.sdf
75 |
76 | # Visual Studio profiler
77 | *.psess
78 | *.vsp
79 |
80 | # ReSharper is a .NET coding add-in
81 | _ReSharper*
82 |
83 | # Installshield output folder
84 | [Ee]xpress
85 |
86 | # DocProject is a documentation generator add-in
87 | DocProject/buildhelp/
88 | DocProject/Help/*.HxT
89 | DocProject/Help/*.HxC
90 | DocProject/Help/*.hhc
91 | DocProject/Help/*.hhk
92 | DocProject/Help/*.hhp
93 | DocProject/Help/Html2
94 | DocProject/Help/html
95 |
96 | # Click-Once directory
97 | publish
98 |
99 | # Others
100 | [Bb]in
101 | [Oo]bj
102 | sql
103 | TestResults
104 | *.Cache
105 | ClientBin
106 | stylecop.*
107 | ~$*
108 | *.dbmdl
109 | Generated_Code #added for RIA/Silverlight projects
110 |
111 | # Backup & report files from converting an old project file to a newer
112 | # Visual Studio version. Backup files are not needed, because we have git ;-)
113 | _UpgradeReport_Files/
114 | Backup*/
115 | UpgradeLog*.XML
116 |
117 |
118 |
119 | ############
120 | ## Windows
121 | ############
122 |
123 | # Windows image file caches
124 | Thumbs.db
125 |
126 | # Folder config file
127 | Desktop.ini
128 |
129 |
130 | #############
131 | ## Python
132 | #############
133 |
134 | *.py[co]
135 |
136 | # Packages
137 | *.egg
138 | *.egg-info
139 | dist
140 | build
141 | eggs
142 | parts
143 | bin
144 | var
145 | sdist
146 | develop-eggs
147 | .installed.cfg
148 |
149 | # Installer logs
150 | pip-log.txt
151 |
152 | # Unit test / coverage reports
153 | .coverage
154 | .tox
155 |
156 | #Translations
157 | *.mo
158 |
159 | #Mr Developer
160 | .mr.developer.cfg
161 |
162 | # Mac crap
163 | .DS_Store
164 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Path Animator
2 | =============
3 | Moves a DOM element along an SVG path (or do whatever along a path...)
4 |
5 | # [DEMO PAGE](http://yaireo.github.io/pathAnimator/)
6 |
7 | ## Basic use example:
8 | ```javascript
9 |
10 | var path = "M150 0 L75 200 L225 200 Z"; // an SVG path
11 | pathAnimator,
12 | startFromPercent = 10, // start from 10% of the path
13 | stopAtPercent = 70; // stop at 70% of the path (which will then call the onDone function callback)
14 |
15 |
16 | // initiate a new pathAnimator object
17 | pathAnimator = new PathAnimator( path, {
18 | duration : 4, // seconds that will take going through the whole path
19 | step : step,
20 | easing : function(t){ return t*(2-t) },
21 | onDone : finish(this)
22 | });
23 |
24 | pathAnimator.start( startFromPercent, stopAtPercent );
25 |
26 | function step( point, angle ){
27 | // do something every "frame" with: point.x, point.y & angle
28 | }
29 |
30 | function finish(){
31 | // do something when animation is done
32 | }
33 | ```
34 |
35 | ## Settings
36 |
37 | Name | Type | Default | Info
38 | ------------------- | ---------- | ----------- | --------------------------------------------------------------------------
39 | duration | Number | undefined | (in seconds) the duration of going through the path
40 | step | Function | undefined | a callback function which is called on every frame
41 | onDone | Function | undefined | (optional) a callback function which will be called at the end
42 | reverse | Boolean | false | go back or forward along the path
43 | startPercent | Number | 0 | Where to start on the path, between 0% to 100%
44 | easing | Function | 1000/60 | (optional) mathematical function for easing
45 | fps | Number | undefined | (optional) frames per second
46 |
47 |
--------------------------------------------------------------------------------
/demos/1/demo1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | PathAnimator
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | By – Yair Even Or
17 |
18 |
19 |
20 |
51 |
52 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/demos/1/demo1.js:
--------------------------------------------------------------------------------
1 | /*----------------------------------------------------------
2 | Page Configuration
3 | -----------------------------------------------------------*/
4 | (function(){
5 | var path = "M6.426,79.957c0,11.458,1.996,19,14.175,19s14.513,3.233,14.513,13.992 c0,6.656-0.397,14.008-13.284,14.008c-9.987,0-15.716,7.74-15.716,13.369c0,21.011,0,61.556,0,82.832 c0,5.765,4.383,13.8,15.54,13.8c8.249,0,18.337,0,26.498,0c7.93,0,14.962,6.735,14.962,13.752c0,21.643,0,63.35,0,84.411 c0,6.236,6.625,12.837,13.29,12.837c26.493,0,85.584,0,111.78,0c8.115,0,12.93-6.952,12.93-13.364c0-21.212,0-62.342,0-83.445 c0-8.199,3.521-13.191,13.918-13.191c10.265,0,14.082,6.601,14.082,13.103c0,26.36,0,85.56,0,112.314 c0,5.3-3.583,13.583-12.941,13.583c-21.146,0-62.868,0-84.055,0c-5.23,0-13.004,4.118-13.004,12.652 c0,8.372,3.064,16.348,13.18,16.348c8.367,0,19.188,0,27.726,0c8.527,0,14.095,4.659,14.095,11.292 c0,7.924,5.049,13.708,14.07,13.708c31.402,0,106.853,0,137.575,0c9.209,0,13.355,8.605,13.355,13.932c0,26.855,0,85.454,0,111.787 c0,7.003,5.738,13.281,15.16,13.281c27.004,0,83.071,0,109.484,0c8.844,0,14.355,6.448,14.355,14.589 c0,10.933-5.415,16.411-13.775,16.411c-8.578,0-14.225,5.688-14.225,10.997c-1,21.253,16.501,34.67,39.834,32.67 s59.72-19.334,63.333-77.334s-38.419-58.724-2.876-143.362c35.543-84.637-31.851-132.554-23.66-194.708 c9.375-71.142,49.203-73.929,66.536-149.263c0-54.333-59.469-55.664-95.734-48.665s-48.265-18.999-91.599-20s-30,31-99.667,31 s-44.05-26.665-103.191-31c-59.142-4.335-38.976,40.167-121.642,41.832c-23.568,0-54.487-17.882-63.333-10.333 C12.211,47.827,7.582,70.632,6.53,77.041C6.376,77.978,6.426,79.895,6.426,79.957z",
6 | firstWalkerObj = $('.maze > .walker')[0],
7 | walkers = [];
8 |
9 | // handles whatever moves along the path
10 | function AnimateWalker(walker){
11 | this.pathAnimator = new PathAnimator( path, {
12 | duration : 30,
13 | step : this.step.bind(this),
14 | reverse : false,
15 | onDone : this.finish.bind(this)
16 | });
17 |
18 | this.walker = walker;
19 | this.color = 'deeppink'; // visually separate different walkers easily
20 | }
21 |
22 | AnimateWalker.prototype = {
23 | start : function(){
24 | //this.walker.style.cssText = "";
25 | this.startOffset = (this.reverse || this.speed < 0) ? 100 : 0; // if in reversed mode, then animation should start from the end, I.E 100%
26 | this.pathAnimator.start();
27 | },
28 |
29 | // Execute every "frame"
30 | step : function(point, angle){
31 | this.walker.style.cssText = "top:" + point.y + "px;" +
32 | "left:" + point.x + "px;" +
33 | "transform:rotate(" + angle + "deg);" +
34 | "color:" + this.color;
35 | },
36 |
37 | // Restart animation once it was finished
38 | finish : function(){
39 | this.start();
40 | },
41 |
42 | // Resume animation from the last completed percentage (also updates the animation with new settings' values)
43 | resume : function( settings ){
44 | settings = settings || {};
45 | $.extend(this.pathAnimator.settings, settings);
46 |
47 | this.pathAnimator.start( this.pathAnimator.percent );
48 | }
49 | }
50 |
51 | function generateWalker(walkerObj){
52 | var newAnimatedWalker = new AnimateWalker( walkerObj );
53 | walkers.push(newAnimatedWalker);
54 | return newAnimatedWalker;
55 | }
56 |
57 | // start "animating" the first Walker on the page
58 | generateWalker(firstWalkerObj).start();
59 | // bind the first Controller to the first Walker
60 | var firstController = $('menu > div:first');
61 | resetController( firstController );
62 | firstController.data( 'walker', walkers[0] );
63 |
64 | /*-----------------------------------------------------------
65 | User Controls
66 | ------------------------------------------------------------*/
67 | $('#showPath').on('change', togglePath);
68 | $('#addWalker').on('click', addWalker);
69 | $('menu')
70 | .on('click', '.delete', removeInstance)
71 | .on('change', '.stopPlay input', stopPlay)
72 | .on('change', '.reverse input', switchDirection)
73 | .on('change', '.speed', changeSpeed)
74 | .on('change', 'select', changeEasing);
75 |
76 | $('.speed').trigger('change');
77 |
78 | // show / hide the path of the animated object
79 | function togglePath(){
80 | $('#svgPath').toggleClass('show');
81 | }
82 |
83 | // add a new instance Walker and his controller box
84 | function addWalker(){
85 | var newWalker = firstWalkerObj.cloneNode(true),
86 | controllerTemplate = $('menu > div:last'),
87 | controllerClone = controllerTemplate.clone(),
88 | newAnimatedWalker = generateWalker(newWalker),
89 | color = '#'+(Math.random()*0xFFFFFF<<0).toString(16);
90 |
91 | resetController( controllerClone );
92 | controllerTemplate.after( controllerClone.css('borderColor', color) );
93 |
94 | $(firstWalkerObj).after(newWalker);
95 |
96 | controllerClone.data('walker', newAnimatedWalker); // keep track which controller controls which walker
97 | newAnimatedWalker.color = color;
98 | newAnimatedWalker.start();
99 | }
100 | // reset the controller box for new "walker" instances
101 | function resetController(obj){
102 | var speed = 30;
103 | obj.find('.speed').val(speed).next().text(speed + 's');
104 | obj.find(':checkbox').removeAttr('checked');
105 | }
106 |
107 | // pause or place the animated object along the path
108 | function stopPlay(){
109 | var thisAnimatedWalker = $(this.parentNode.parentNode).data('walker');
110 |
111 | thisAnimatedWalker.pathAnimator.running ? thisAnimatedWalker.pathAnimator.stop() : thisAnimatedWalker.resume.apply(thisAnimatedWalker);
112 | }
113 |
114 | // switch direction of the animated object
115 | function switchDirection(){
116 | var thisAnimatedWalker = $(this.parentNode.parentNode).data('walker'),
117 | settings = {
118 | reverse : !thisAnimatedWalker.pathAnimator.settings.reverse
119 | };
120 |
121 | if( thisAnimatedWalker.pathAnimator.running )
122 | thisAnimatedWalker.resume.call(thisAnimatedWalker, settings);
123 | }
124 |
125 | function changeSpeed(){
126 | var thisAnimatedWalker = $(this.parentNode).data('walker');
127 | this.nextElementSibling.innerHTML = this.value + 's';
128 |
129 | thisAnimatedWalker.resume.call(thisAnimatedWalker, { duration:this.value });
130 | }
131 |
132 | function removeInstance(){
133 | var parent = $(this.parentNode),
134 | thisAnimatedWalker = parent.data('walker');
135 |
136 | // make sure at least one Walker stays
137 | if( walkers.length > 1 ){
138 | parent.remove();
139 | thisAnimatedWalker.pathAnimator.stop();
140 | $(thisAnimatedWalker.walker).remove();
141 | walkers.splice(walkers.indexOf(thisAnimatedWalker), 1);
142 | }
143 | }
144 |
145 | function changeEasing(){
146 | var thisAnimatedWalker = $(this.parentNode).data('walker'),
147 | formula = this.value;
148 |
149 | if( this.value ){
150 | thisAnimatedWalker.resume.call(thisAnimatedWalker, {
151 | easing : function(t){ return eval(formula) }
152 | });
153 | }
154 | }
155 |
156 | // reset checkboxes
157 | $(':checkbox').removeAttr('checked');
158 | $('select').prop('selectedIndex', 0);
159 | })();
--------------------------------------------------------------------------------
/demos/1/maze.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yairEO/pathAnimator/84fb6ddc58d72aecccd6854d4595a0472bcbb78d/demos/1/maze.png
--------------------------------------------------------------------------------
/demos/1/path.svg:
--------------------------------------------------------------------------------
1 |
2 |
21 |
--------------------------------------------------------------------------------
/demos/1/styles.css:
--------------------------------------------------------------------------------
1 | /* YUI 3.8.1 (build 5795) Copyright 2013 Yahoo! Inc. */
2 | html{color:#000;background:#FFF}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0}table{border-collapse:collapse;border-spacing:0}fieldset,img{border:0}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal}ol,ul{list-style:none}caption,th{text-align:left}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal}q:before,q:after{content:''}abbr,acronym{border:0;font-variant:normal}sup{vertical-align:text-top}sub{vertical-align:text-bottom}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit}input,textarea,select{*font-size:100%}legend{color:#000}#yui3-css-stamp.cssreset{display:none}
3 | .cf:before, .cf:after{content:""; display:table;} .cf:after {clear:both;} .cf{zoom:1;}
4 |
5 | html{ height:100%; }
6 |
7 | body{ color:#666; font:12px/1.5 arial; text-align:center; min-height:100%; }
8 |
9 | label{ cursor:pointer; }
10 | input[type=checkbox]{ margin:-1px 5px 0 0; vertical-align:text-top; }
11 |
12 | menu{ position:absolute; top:45px; left:0; padding:10px; margin:0; text-align:left; }
13 | .walkerController{ background:#EEE; padding:6px; margin:8px 0; width:300px; border-left:8px solid deeppink; }
14 | .walkerController input[type=checkbox]{ display:none; }{ }
15 | menu button{ cursor:pointer; margin:0 0 5px 0; font-size:inherit; }
16 |
17 | .walkerController label > span{ background:#E1E1E1; border:1px solid #AAA; padding:1px 6px; border-radius:2px; }
18 | .walkerController label > span:hover{ background:#EEE; }
19 | .walkerController input[type=range]{ width:230px; }
20 | .walkerController input[type=range] + span{ float:right; line-height:2; }
21 | .stopPlay span::after{ content:'Playing'; }
22 | .walkerController .stopPlay input:checked + span::after{ content:'Stopped'; color:red; }
23 |
24 | .reverse span::after{ content:'Going Forward'; }
25 | .walkerController .reverse input:checked + span::after{ content:'Going Reverse'; }
26 |
27 | menu > div:first-of-type .delete{ display:none; }
28 | menu select option:first-child{ border-bottom:1px solid #CCC; font-weight:bold; }
29 | menu .delete{ background:#FFF; border:1px solid #CCC; border-radius:30px; color:#666; float:right; font-size:20px; height:20px; line-height:8px; padding:0 0 1px; width:20px; }
30 | menu .delete:hover{ background:#666; color:#fff; border-color:transparent; }
31 | .walkerController input{ padding:4px; margin-right:6px; width:70px; vertical-align:middle; }
32 | menu #addWalker{ padding:5px 20px; }
33 |
34 | header{ background:#333; width:100%; height:40px; line-height:36px; position:fixed; top:0; left:0; font-size:14px; }
35 | a.git, a.by{ position:absolute; top:2px; right:5px; opacity:0.3; -webkit-transition:0.3s cubic-bezier(0.055, 0.6, 0.2, 1); transition:0.3s cubic-bezier(0.055, 0.6, 0.2, 1); }
36 | a.git{ -webkit-transform:scale(0.6); transform:scale(0.6); -webkit-transform-origin:100% 0 0; transform-origin:100% 0 0; }
37 | a.git:hover, a.by:hover{ opacity:1; }
38 | a.git img{ }
39 |
40 | a.by{ text-transform:capitalize; right:auto; left:5px; font-weight:bold; color:#FFF; text-decoration:none; font-family:'Fjalla One', sans-serif; -webkit-transform-origin:0%; transform-origin:0%; }
41 | .social{ position:absolute; top:-15px; left:5px; -webkit-filter:blur(20px); opacity:0; -webkit-transition:1s ease-out; transition:.6s ease-out; }
42 | .social.show{ top:5px; opacity:1; -webkit-filter:none; }
43 | .fbLike{ height:21px; width:100px; border:none; }
44 | .twitter-share-button{ }
45 | .social > *{ opacity:.5; -webkit-transition:.2s ease-out; transition:.2s ease-out; }
46 | .social > *:hover{ opacity:1; }
47 |
48 | /* graphics */
49 | .maze{ display:inline-block; vertical-align:middle; height:537px; width:536px; margin:100px auto; background:url('maze.png') 0 0 no-repeat; position:relative; }
50 | /* Walker */
51 | .walker{ position:absolute; z-index:1; font-size:25px; color:deeppink; height:25px; width:25px; text-align:center; line-height:25px; margin:-93px 0 0 28px; text-shadow:-6px 0px 2px rgba(0,0,0,0.3), 7px 0; }
52 |
53 | #svgPath{ opacity:0; margin:-80px 0 0 39px; -webkit-transition:0.2s; transition:0.2s; }
54 | #svgPath.show{ opacity:1; }
--------------------------------------------------------------------------------
/demos/2/demo2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | PathAnimator - demo 2
7 |
8 |
9 |
10 |
11 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/demos/2/fire.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yairEO/pathAnimator/84fb6ddc58d72aecccd6854d4595a0472bcbb78d/demos/2/fire.png
--------------------------------------------------------------------------------
/demos/3/demo3.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | PathAnimator
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | By – Yair Even Or
16 |
17 |
18 |
19 |
29 |
30 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/demos/3/demo3.js:
--------------------------------------------------------------------------------
1 | /*----------------------------------------------------------
2 | Page Configuration
3 | -----------------------------------------------------------*/
4 | (function(){
5 | var path = "M6.426,79.957c0,11.458,1.996,19,14.175,19s14.513,3.233,14.513,13.992 c0,6.656-0.397,14.008-13.284,14.008c-9.987,0-15.716,7.74-15.716,13.369c0,21.011,0,61.556,0,82.832 c0,5.765,4.383,13.8,15.54,13.8c8.249,0,18.337,0,26.498,0c7.93,0,14.962,6.735,14.962,13.752c0,21.643,0,63.35,0,84.411 c0,6.236,6.625,12.837,13.29,12.837c26.493,0,85.584,0,111.78,0c8.115,0,12.93-6.952,12.93-13.364c0-21.212,0-62.342,0-83.445 c0-8.199,3.521-13.191,13.918-13.191c10.265,0,14.082,6.601,14.082,13.103c0,26.36,0,85.56,0,112.314 c0,5.3-3.583,13.583-12.941,13.583c-21.146,0-62.868,0-84.055,0c-5.23,0-13.004,4.118-13.004,12.652 c0,8.372,3.064,16.348,13.18,16.348c8.367,0,19.188,0,27.726,0c8.527,0,14.095,4.659,14.095,11.292 c0,7.924,5.049,13.708,14.07,13.708c31.402,0,106.853,0,137.575,0c9.209,0,13.355,8.605,13.355,13.932c0,26.855,0,85.454,0,111.787 c0,7.003,5.738,13.281,15.16,13.281c27.004,0,83.071,0,109.484,0c8.844,0,14.355,6.448,14.355,14.589 c0,10.933-5.415,16.411-13.775,16.411c-8.578,0-14.225,5.688-14.225,10.997c-1,21.253,16.501,34.67,39.834,32.67 s59.72-19.334,63.333-77.334s-38.419-58.724-2.876-143.362c35.543-84.637-31.851-132.554-23.66-194.708 c9.375-71.142,49.203-73.929,66.536-149.263c0-54.333-59.469-55.664-95.734-48.665s-48.265-18.999-91.599-20s-30,31-99.667,31 s-44.05-26.665-103.191-31c-59.142-4.335-38.976,40.167-121.642,41.832c-23.568,0-54.487-17.882-63.333-10.333 C12.211,47.827,7.582,70.632,6.53,77.041C6.376,77.978,6.426,79.895,6.426,79.957z",
6 | firstWalkerObj = $('.maze > .walker')[0],
7 | walkers = [],
8 | totalPercentageElm = document.querySelector('.totalPercentage'),
9 | stopPointElm = document.querySelector('.stopPoint');
10 |
11 | // handles whatever moves along the path
12 | function AnimateWalker(walker){
13 | this.pathAnimator = new PathAnimator( path, {
14 | duration : 4,
15 | step : this.step.bind(this),
16 | reverse : false,
17 | easing : function(t){ return t<.5 ? 8*t*t*t*t : 1-8*(--t)*t*t*t },
18 | onDone : this.onDone.bind(this)
19 | });
20 |
21 | this.walker = walker;
22 | this.color = 'deeppink'; // visually separate different walkers easily
23 | }
24 |
25 | AnimateWalker.prototype = {
26 | init : function(){
27 | this.stops = [20, 40, 80, 100];
28 | this.stopIdx = 0;
29 | this.goToStop( 0, this.stops[0] );
30 | },
31 |
32 | /**
33 | * the percentage value of the overall path where the stop should occur
34 | * @param {Number} startFromPercent
35 | * @param {Number} stopAtPercent
36 | */
37 | goToStop( startFromPercent, stopAtPercent ){
38 | this.pathAnimator.start( startFromPercent, stopAtPercent );
39 | },
40 |
41 | // Execute every "frame"
42 | step : function(point, angle){
43 | totalPercentageElm.innerHTML = this.pathAnimator.percent.toFixed(0);
44 |
45 | this.walker.style.cssText = "top:" + point.y + "px;" +
46 | "left:" + point.x + "px;" +
47 | "transform:rotate(" + angle + "deg);" +
48 | "color:" + this.color;
49 | },
50 |
51 | // Restart animation once it was finished
52 | onDone : function(){
53 | var that = this;
54 | stopPointElm.innerHTML = this.pathAnimator.percent == 100 ? 0 : this.stopIdx + 1;
55 |
56 | if( that.stopIdx < that.stops.length - 1 ){
57 | setTimeout(function(){
58 | that.goToStop( that.stops[that.stopIdx], that.stops[++that.stopIdx] );
59 | }, 500)
60 | }
61 | else
62 | this.init();
63 | },
64 |
65 | // Resume animation from the last completed percentage (also updates the animation with new settings' values)
66 | resume : function( settings ){
67 | settings = settings || {};
68 | $.extend(this.pathAnimator.settings, settings);
69 |
70 | this.pathAnimator.start( this.pathAnimator.percent );
71 | }
72 | }
73 |
74 |
75 |
76 | function generateWalker(walkerObj){
77 | var newAnimatedWalker = new AnimateWalker( walkerObj );
78 | walkers.push(newAnimatedWalker);
79 | return newAnimatedWalker;
80 | }
81 |
82 | // start "animating" the first Walker on the page
83 | generateWalker(firstWalkerObj).init();
84 |
85 | // bind the first Controller to the first Walker
86 | var firstController = $('menu > div:first');
87 | firstController.data( 'walker', walkers[0] );
88 | })();
--------------------------------------------------------------------------------
/demos/3/maze.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yairEO/pathAnimator/84fb6ddc58d72aecccd6854d4595a0472bcbb78d/demos/3/maze.png
--------------------------------------------------------------------------------
/demos/3/path.svg:
--------------------------------------------------------------------------------
1 |
2 |
21 |
--------------------------------------------------------------------------------
/demos/3/styles.css:
--------------------------------------------------------------------------------
1 | /* YUI 3.8.1 (build 5795) Copyright 2013 Yahoo! Inc. */
2 | html{color:#000;background:#FFF}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0}table{border-collapse:collapse;border-spacing:0}fieldset,img{border:0}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal}ol,ul{list-style:none}caption,th{text-align:left}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal}q:before,q:after{content:''}abbr,acronym{border:0;font-variant:normal}sup{vertical-align:text-top}sub{vertical-align:text-bottom}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit}input,textarea,select{*font-size:100%}legend{color:#000}#yui3-css-stamp.cssreset{display:none}
3 | .cf:before, .cf:after{content:""; display:table;} .cf:after {clear:both;} .cf{zoom:1;}
4 |
5 | html{ height:100%; }
6 |
7 | body{ color:#666; font:12px/1.5 arial; text-align:center; min-height:100%; }
8 |
9 | label{ cursor:pointer; }
10 | input[type=checkbox]{ margin:-1px 5px 0 0; vertical-align:text-top; }
11 |
12 | menu{ position:absolute; top:45px; left:0; padding:10px; margin:0; text-align:left; }
13 | .walkerController{ background:#EEE; padding:6px; margin:8px 0; width:282px; border-left:8px solid deeppink; }
14 | .walkerController input[type=checkbox]{ display:none; }{ }
15 | menu button{ cursor:pointer; margin:0 0 5px 0; font-size:inherit; }
16 |
17 | .walkerController label > span{ background:#E1E1E1; border:1px solid #AAA; padding:1px 6px; border-radius:2px; }
18 | .walkerController label > span:hover{ background:#EEE; }
19 | .walkerController input[type=range]{ width:230px; }
20 | .walkerController input[type=range] + span{ float:right; line-height:2; }
21 | .stopPlay span::after{ content:'Playing'; }
22 | .walkerController .stopPlay input:checked + span::after{ content:'Stopped'; color:red; }
23 |
24 | .reverse span::after{ content:'Going Forward'; }
25 | .walkerController .reverse input:checked + span::after{ content:'Going Reverse'; }
26 |
27 | menu > div:first-of-type .delete{ display:none; }
28 | menu select option:first-child{ border-bottom:1px solid #CCC; font-weight:bold; }
29 | menu .delete{ background:#FFF; border:1px solid #CCC; border-radius:30px; color:#666; float:right; font-size:20px; height:20px; line-height:8px; padding:0 0 1px; width:20px; }
30 | menu .delete:hover{ background:#666; color:#fff; border-color:transparent; }
31 | .walkerController input{ padding:4px; margin-right:6px; width:70px; vertical-align:middle; }
32 | menu #addWalker{ padding:5px 20px; }
33 |
34 | header{ background:#333; width:100%; height:40px; line-height:36px; position:fixed; top:0; left:0; font-size:14px; }
35 | a.git, a.by{ position:absolute; top:2px; right:5px; opacity:0.3; -webkit-transition:0.3s cubic-bezier(0.055, 0.6, 0.2, 1); transition:0.3s cubic-bezier(0.055, 0.6, 0.2, 1); }
36 | a.git{ -webkit-transform:scale(0.6); transform:scale(0.6); -webkit-transform-origin:100% 0 0; transform-origin:100% 0 0; }
37 | a.git:hover, a.by:hover{ opacity:1; }
38 | a.git img{ }
39 |
40 | a.by{ text-transform:capitalize; right:auto; left:5px; font-weight:bold; color:#FFF; text-decoration:none; font-family:'Fjalla One', sans-serif; -webkit-transform-origin:0%; transform-origin:0%; }
41 | .social{ position:absolute; top:-15px; left:5px; -webkit-filter:blur(20px); opacity:0; -webkit-transition:1s ease-out; transition:.6s ease-out; }
42 | .social.show{ top:5px; opacity:1; -webkit-filter:none; }
43 | .fbLike{ height:21px; width:100px; border:none; }
44 | .twitter-share-button{ }
45 | .social > *{ opacity:.5; -webkit-transition:.2s ease-out; transition:.2s ease-out; }
46 | .social > *:hover{ opacity:1; }
47 |
48 | /* graphics */
49 | .maze{ display:inline-block; vertical-align:middle; height:537px; width:536px; margin:100px auto; background:url('maze.png') 0 0 no-repeat; position:relative; }
50 | /* Walker */
51 | .walker{ position:absolute; z-index:1; font-size:25px; color:deeppink; height:25px; width:25px; text-align:center; line-height:25px; margin:-93px 0 0 28px; text-shadow:-6px 0px 2px rgba(0,0,0,0.3), 7px 0; }
52 |
53 | #svgPath{ opacity:0; margin:-80px 0 0 39px; -webkit-transition:0.2s; transition:0.2s; }
54 | #svgPath.show{ opacity:1; }
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | PathAnimator
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | By – Yair Even Or
17 |
18 |
19 |
20 |
51 |
52 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/pathAnimator.js:
--------------------------------------------------------------------------------
1 | // Path Animator v1.5.0
2 | // (c) 2013 Yair Even Or (https://github.com/yairEO/pathAnimator)
3 | // MIT-style license.
4 |
5 | function PathAnimator( path, settings ){
6 | if( !path ) return;
7 |
8 | this.len = this.updatePath( path );
9 | this.timer = null;
10 |
11 | this.settings = {
12 | duration : settings.duration,
13 | step : settings.step,
14 | reverse : !!settings.reverse,
15 | startPercent : settings.startPercent || 0,
16 | onDone : settings.onDone || function(){},
17 | easing : settings.easing,
18 | fps : 1000/60, // frames-per-second
19 | }
20 | }
21 |
22 | PathAnimator.prototype = {
23 | start : function( startFromPercent, stopAtPercent ){
24 | this.stop();
25 | startFromPercent = startFromPercent || this.settings.startPercent || 0;
26 | this.percent = startFromPercent;
27 | if( this.settings.duration == 0 ) return false;
28 |
29 | var that = this,
30 | startTime = new Date(),
31 | stopAtPercent = stopAtPercent || 100;
32 |
33 | (function calc(){
34 | var p = [], angle,
35 | now = new Date(),
36 | elapsed = (now-startTime)/1000,
37 | t = (elapsed/that.settings.duration),
38 | newPercent;
39 |
40 | // easing functions: https://gist.github.com/gre/1650294
41 | if( typeof that.settings.easing == 'function' ){
42 | t = that.settings.easing(t);
43 | }
44 |
45 | newPercent = startFromPercent + t * (stopAtPercent - startFromPercent);
46 |
47 | if( that.settings.reverse ){
48 | newPercent = startFromPercent - t * (stopAtPercent - startFromPercent)
49 | if( newPercent < 0 )
50 | newPercent = stopAtPercent + newPercent;
51 | }
52 |
53 | that.running = true;
54 | that.percent = newPercent;
55 |
56 | // On animation end (from '0%' to '100%' or '100%' to '0%')
57 | if( t > 0.999 ){
58 | that.stop();
59 | that.percent = stopAtPercent;
60 | return that.settings.onDone();
61 | }
62 |
63 | // angle calculations
64 | p[0] = that.pointAt( that.percent - 1 );
65 | p[1] = that.pointAt( that.percent + 1 );
66 | angle = Math.atan2(p[1].y-p[0].y,p[1].x-p[0].x)*180 / Math.PI;
67 |
68 | // advance to the next point on the path
69 | that.timer = setTimeout( calc, that.settings.fps );
70 | // do one step ("frame")
71 | that.settings.step( that.pointAt(that.percent), angle );
72 | })();
73 | },
74 |
75 | stop : function(){
76 | clearTimeout( this.timer );
77 | this.timer = null;
78 | this.running = false;
79 | },
80 |
81 | stopAt : function(percent){
82 |
83 | },
84 |
85 | pointAt : function(percent){
86 | return this.path.getPointAtLength( this.len * percent/100 );
87 | },
88 |
89 | updatePath : function( path ){
90 | if( !this.path && path ){
91 | this.path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
92 | this.path.setAttribute('d', path);
93 | }
94 | return this.path.getTotalLength();
95 | }
96 | };
--------------------------------------------------------------------------------