├── .gitignore
├── ClassAnimationDemo.framer
├── framer
│ ├── version
│ ├── .bookmark
│ ├── social-80x80.png
│ ├── images
│ │ ├── cursor.png
│ │ ├── icon-76.png
│ │ ├── cursor@2x.png
│ │ ├── icon-120.png
│ │ ├── icon-152.png
│ │ ├── icon-180.png
│ │ ├── icon-192.png
│ │ ├── cursor-active.png
│ │ └── cursor-active@2x.png
│ ├── social-800x600.png
│ ├── config.json
│ ├── framer.generated.js
│ ├── style.css
│ └── framer.init.js
├── images
│ ├── .gitkeep
│ ├── stars.svg
│ └── delivery.svg
├── .gitignore
├── index.html
├── app.coffee
└── modules
│ ├── ClassAnimation.coffee
│ └── velocity.min.js
├── demo.gif
├── thumb.mov
├── module.json
├── example.coffee
├── AnimatableProperties.md
├── README.md
└── ClassAnimation.coffee
/.gitignore:
--------------------------------------------------------------------------------
1 | Demo.sketch
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/framer/version:
--------------------------------------------------------------------------------
1 | 6
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/images/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kysely/framer-class-animation/HEAD/demo.gif
--------------------------------------------------------------------------------
/thumb.mov:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kysely/framer-class-animation/HEAD/thumb.mov
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/framer/.bookmark:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kysely/framer-class-animation/HEAD/ClassAnimationDemo.framer/framer/.bookmark
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/framer/social-80x80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kysely/framer-class-animation/HEAD/ClassAnimationDemo.framer/framer/social-80x80.png
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/framer/images/cursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kysely/framer-class-animation/HEAD/ClassAnimationDemo.framer/framer/images/cursor.png
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/framer/images/icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kysely/framer-class-animation/HEAD/ClassAnimationDemo.framer/framer/images/icon-76.png
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/framer/social-800x600.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kysely/framer-class-animation/HEAD/ClassAnimationDemo.framer/framer/social-800x600.png
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/framer/images/cursor@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kysely/framer-class-animation/HEAD/ClassAnimationDemo.framer/framer/images/cursor@2x.png
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/framer/images/icon-120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kysely/framer-class-animation/HEAD/ClassAnimationDemo.framer/framer/images/icon-120.png
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/framer/images/icon-152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kysely/framer-class-animation/HEAD/ClassAnimationDemo.framer/framer/images/icon-152.png
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/framer/images/icon-180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kysely/framer-class-animation/HEAD/ClassAnimationDemo.framer/framer/images/icon-180.png
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/framer/images/icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kysely/framer-class-animation/HEAD/ClassAnimationDemo.framer/framer/images/icon-192.png
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/framer/images/cursor-active.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kysely/framer-class-animation/HEAD/ClassAnimationDemo.framer/framer/images/cursor-active.png
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/framer/images/cursor-active@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kysely/framer-class-animation/HEAD/ClassAnimationDemo.framer/framer/images/cursor-active@2x.png
--------------------------------------------------------------------------------
/module.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Class Animation",
3 | "description": "Animate individual HTML and SVG elements inside one layer's html property",
4 | "author": "Radek Kysely",
5 |
6 | "require": "{ClassAnimation, Style} = require 'ClassAnimation'",
7 | "install": "ClassAnimation.coffee",
8 | "example": "example.coffee",
9 |
10 | "thumb": "thumb.mov"
11 | }
12 |
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/framer/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "orientation" : 0,
3 | "updateDelay" : 0.3,
4 | "contentScale" : 1,
5 | "fullScreen" : false,
6 | "sharedPrototype" : 0,
7 | "propertyPanelToggleStates" : {
8 |
9 | },
10 | "deviceType" : "apple-iphone-7-black",
11 | "projectId" : "8F1B03B1-0D3B-4115-85A0-2C81C555981B",
12 | "deviceOrientation" : 0,
13 | "selectedHand" : "",
14 | "foldedCodeRanges" : [
15 | "{80, 531}"
16 | ],
17 | "deviceScale" : "fit"
18 | }
--------------------------------------------------------------------------------
/example.coffee:
--------------------------------------------------------------------------------
1 | socialCard = new Layer
2 |
3 | # Create a content in one layer's ›html‹ property
4 | # with some classes
5 | socialCard.html =
6 | """
7 |
Paul Paulson
8 | Montessori Teacher
9 | """
10 |
11 | # Create a new animation for "className" class
12 | animationA = new ClassAnimation "className",
13 | color: "#ff00ff"
14 | fontSize: 60
15 | lineHeight: 70
16 | borderWidth: 5
17 |
18 | # Start the animation
19 | animationA.start()
20 |
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/.gitignore:
--------------------------------------------------------------------------------
1 | # Framer Git Ignore
2 |
3 | # General OSX
4 |
5 | .DS_Store
6 | .AppleDouble
7 | .LSOverride
8 |
9 | # Icon must end with two \r
10 | Icon
11 |
12 | # Thumbnails
13 | ._*
14 |
15 | # Files that might appear in the root of a volume
16 | .DocumentRevisions-V100
17 | .fseventsd
18 | .Spotlight-V100
19 | .TemporaryItems
20 | .Trashes
21 | .VolumeIcon.icns
22 |
23 | # Directories potentially created on remote AFP share
24 | .AppleDB
25 | .AppleDesktop
26 | Network Trash Folder
27 | Temporary Items
28 | .apdisk
29 |
30 | # Framer Specific
31 | .*.html
32 | .app.js
33 | framer/*.old*
34 | framer/.*.hash
35 | framer/backup.coffee
36 | framer/backups/*
37 | framer/manifest.txt
38 | framer/metadata.json
39 | framer/preview.png
40 | framer/social-880x460.png
41 | framer/social-1200x630.png
42 |
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/framer/framer.generated.js:
--------------------------------------------------------------------------------
1 | // This is autogenerated by Framer
2 |
3 |
4 | if (!window.Framer && window._bridge) {window._bridge('runtime.error', {message:'[framer.js] Framer library missing or corrupt. Select File → Update Framer Library.'})}
5 | if (DeviceComponent) {DeviceComponent.Devices["iphone-6-silver"].deviceImageJP2 = false};
6 | if (window.Framer) {window.Framer.Defaults.DeviceView = {"deviceScale":"fit","selectedHand":"","deviceType":"apple-iphone-7-black","contentScale":1,"orientation":0};
7 | }
8 | if (window.Framer) {window.Framer.Defaults.DeviceComponent = {"deviceScale":"fit","selectedHand":"","deviceType":"apple-iphone-7-black","contentScale":1,"orientation":0};
9 | }
10 | window.FramerStudioInfo = {"deviceImagesUrl":"\/_server\/resources\/DeviceImages","documentTitle":"ClassAnimationDemo.framer"};
11 |
12 | Framer.Device = new Framer.DeviceView();
13 | Framer.Device.setupContext();
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/framer/style.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | border: none;
5 | -webkit-user-select: none;
6 | -webkit-tap-highlight-color: rgba(0,0,0,0);
7 | }
8 |
9 | body {
10 | background-color: #fff;
11 | font: 28px/1em "Helvetica";
12 | color: gray;
13 | overflow: hidden;
14 | }
15 |
16 | a {
17 | color: gray;
18 | }
19 |
20 | body {
21 | cursor: url('images/cursor.png') 32 32, auto;
22 | cursor: -webkit-image-set(
23 | url('images/cursor.png') 1x,
24 | url('images/cursor@2x.png') 2x
25 | ) 32 32, auto;
26 | }
27 |
28 | body:active {
29 | cursor: url('images/cursor-active.png') 32 32, auto;
30 | cursor: -webkit-image-set(
31 | url('images/cursor-active.png') 1x,
32 | url('images/cursor-active@2x.png') 2x
33 | ) 32 32, auto;
34 | }
35 |
36 | .framerAlertBackground {
37 | position: absolute; top:0px; left:0px; right:0px; bottom:0px;
38 | z-index: 1000;
39 | background-color: #fff;
40 | }
41 |
42 | .framerAlert {
43 | font:400 14px/1.4 "Helvetica Neue", Helvetica, Arial, sans-serif;
44 | -webkit-font-smoothing:antialiased;
45 | color:#616367; text-align:center;
46 | position: absolute; top:40%; left:50%; width:260px; margin-left:-130px;
47 | }
48 | .framerAlert strong { font-weight:500; color:#000; margin-bottom:8px; display:block; }
49 | .framerAlert a { color:#28AFFA; }
50 | .framerAlert .btn {
51 | font-weight:500; text-decoration:none; line-height:1;
52 | display:inline-block; padding:6px 12px 7px 12px;
53 | border-radius:3px; margin-top:12px;
54 | background:#28AFFA; color:#fff;
55 | }
56 |
57 | ::-webkit-scrollbar {
58 | display: none;
59 | }
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/images/stars.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/framer/framer.init.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | function isFileLoadingAllowed() {
4 | return (window.location.protocol.indexOf("file") == -1)
5 | }
6 |
7 | function isHomeScreened() {
8 | return ("standalone" in window.navigator) && window.navigator.standalone == true
9 | }
10 |
11 | function isCompatibleBrowser() {
12 | return Utils.isWebKit()
13 | }
14 |
15 | var alertNode;
16 |
17 | function dismissAlert() {
18 | alertNode.parentElement.removeChild(alertNode)
19 | loadProject()
20 | }
21 |
22 | function showAlert(html) {
23 |
24 | alertNode = document.createElement("div")
25 |
26 | alertNode.classList.add("framerAlertBackground")
27 | alertNode.innerHTML = html
28 |
29 | document.addEventListener("DOMContentLoaded", function(event) {
30 | document.body.appendChild(alertNode)
31 | })
32 |
33 | window.dismissAlert = dismissAlert;
34 | }
35 |
36 | function showBrowserAlert() {
37 | var html = ""
38 | html += ""
39 | html += "
Error: Not A WebKit Browser "
40 | html += "Your browser is not supported.
Please use Safari or Chrome.
"
41 | html += "
Try anyway "
42 | html += "
"
43 |
44 | showAlert(html)
45 | }
46 |
47 | function showFileLoadingAlert() {
48 | var html = ""
49 | html += ""
50 | html += "
Error: Local File Restrictions "
51 | html += "Preview this prototype with Framer Mirror or learn more about "
52 | html += "
file restrictions .
"
53 | html += "
Try anyway "
54 | html += "
"
55 |
56 | showAlert(html)
57 | }
58 |
59 | function loadProject() {
60 | CoffeeScript.load("app.coffee")
61 | }
62 |
63 | function setDefaultPageTitle() {
64 | // If no title was set we set it to the project folder name so
65 | // you get a nice name on iOS if you bookmark to desktop.
66 | document.addEventListener("DOMContentLoaded", function() {
67 | if (document.title == "") {
68 | if (window.FramerStudioInfo && window.FramerStudioInfo.documentTitle) {
69 | document.title = window.FramerStudioInfo.documentTitle
70 | } else {
71 | document.title = window.location.pathname.replace(/\//g, "")
72 | }
73 | }
74 | })
75 | }
76 |
77 | function init() {
78 |
79 | if (Utils.isFramerStudio()) {
80 | return
81 | }
82 |
83 | setDefaultPageTitle()
84 |
85 | if (!isCompatibleBrowser()) {
86 | return showBrowserAlert()
87 | }
88 |
89 | if (!isFileLoadingAllowed()) {
90 | return showFileLoadingAlert()
91 | }
92 |
93 | loadProject()
94 |
95 | }
96 |
97 | init()
98 |
99 | })()
100 |
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/app.coffee:
--------------------------------------------------------------------------------
1 | {ClassAnimation} = require "ClassAnimation"
2 | {Style} = require "ClassAnimation"
3 |
4 | # DEMO SETUP
5 |
6 | # Import Varela Round from Google Fonts
7 | "@import url('https://fonts.googleapis.com/css?family=Varela+Round'); body {font-family: 'Varela Round', 'Varela', sans-serif}".css()
8 |
9 | Screen.backgroundColor = "#00003C"
10 | Canvas.backgroundColor = "#000000"
11 |
12 | texts = new Layer
13 | width: Screen.width
14 | height: 280
15 | y: 260
16 | backgroundColor: null
17 |
18 | stars = new Layer
19 | x: Screen.width/2 - 326 - 20
20 | y: 30
21 | width: 652
22 | height: 290
23 | backgroundColor: null
24 |
25 | delivery = new Layer
26 | x: 0, y: 720
27 | width: 750
28 | height: 346
29 | backgroundColor: null
30 |
31 |
32 |
33 | # STYLES ——————————————————————————————————————
34 | # —————————————————————————————————————————————
35 | # —————————————————————————————————————————————
36 |
37 | # First, hide all elements in ›second‹ class
38 | Style "second",
39 | display: "none"
40 |
41 | # Set styles for our headlines
42 | Style "headline",
43 | fontSize: 88
44 | lineHeight: 100
45 | color: "#D3D3FF"
46 | fontWeight: 400
47 | textAlign: "center"
48 |
49 | Style "status",
50 | fontSize: 24
51 | lineHeight: 60
52 | letterSpacing: 11
53 | fontWeight: 400
54 | color: "#5856D6"
55 | textTransform: "uppercase"
56 | textAlign: "center"
57 |
58 |
59 |
60 | # LAYERS' CONTENTS ————————————————————————————
61 | # —————————————————————————————————————————————
62 | # —————————————————————————————————————————————
63 |
64 | texts.html = """
65 | Pssst!
66 | Shipping Dreams
67 |
68 | Yasss!
69 | Dreams on the way
70 | """
71 |
72 |
73 | # Import SVGs from files for cleaner code —————
74 | # Individual objects (paths) in Sketch-generated SVGs
75 | # will have ›id‹ attribute equal to the layer name
76 | # in Sketch. You simply need to rewrite them from ›id‹
77 | # to ›class‹ to make them work with the module
78 |
79 | stars.html = Utils.domLoadDataSync("images/stars.svg")
80 | delivery.html = Utils.domLoadDataSync("images/delivery.svg")
81 |
82 |
83 |
84 | # ANIMATIONS SETUP ————————————————————————————
85 | # —————————————————————————————————————————————
86 | # —————————————————————————————————————————————
87 |
88 | # Since both wheel objects in SVG belong to one
89 | # ›wheel‹ class, we can animate both with one animation
90 | toWheel = new ClassAnimation "wheel",
91 | rotateZ: 360
92 | options:
93 | curve: "linear"
94 | repeat: true
95 |
96 | # Add some bouncing effect for Dream Delivery truck
97 | truckRumble = new ClassAnimation "truckBody",
98 | translateY: 5
99 | rotateZ: 0.2
100 | options:
101 | time: 0.3
102 | repeat: true
103 |
104 | # Show off some driving skills
105 | driveSlightly = new ClassAnimation "movingElements",
106 | translateX: 180
107 | options:
108 | time: 5
109 | curve: [0,0,.58,1] # Bezier Curve
110 |
111 | # This will move truck out of screen
112 | driveAway = new ClassAnimation "movingElements",
113 | translateX: 800
114 | options:
115 | time: 0.7
116 |
117 | # Let's animate the stars... cause we can
118 | starsGroupOne = new ClassAnimation "stars1",
119 | scale: 0.97
120 | rotateZ: -1
121 | fill: "#2E2C87"
122 | options:
123 | time: 2
124 | repeat: true
125 |
126 | starsGroupTwo = new ClassAnimation "stars2",
127 | scale: 1.02
128 | rotateZ: 1
129 | options:
130 | time: 2
131 | repeat: true
132 |
133 | # Add unnecessary pulsing effect to ›first‹ headers
134 | pulseHead = new ClassAnimation "first",
135 | opacity: 0.5
136 | options:
137 | time: 0.5
138 | repeat: true
139 |
140 |
141 |
142 | # START THE ANIMATIONS ————————————————————————
143 | # —————————————————————————————————————————————
144 | # —————————————————————————————————————————————
145 |
146 | starsGroupOne.start()
147 | starsGroupTwo.start()
148 |
149 | pulseHead.start()
150 |
151 | truckRumble.start()
152 | toWheel.start()
153 |
154 | driveSlightly.start()
155 |
156 |
157 | # When ›driveSlightly‹ ends, lets ›driveAway‹
158 | # and show the elements in ›second‹ class
159 |
160 | driveSlightly.on Events.AnimationEnd, ->
161 | driveAway.start()
162 | pulseHead.fadeOut()
163 |
164 | pulseHead.onAnimationEnd ->
165 | "second".fadeIn()
166 |
167 |
--------------------------------------------------------------------------------
/AnimatableProperties.md:
--------------------------------------------------------------------------------
1 | # List of Supported Properties in Velocity.js
2 |
3 | `opacity` is unitless *(works for SVG)*
4 |
5 | `width` *(works for SVG)*
6 |
7 | `height` *(works for SVG)*
8 |
9 | `minWidth`
10 |
11 | `minHeight`
12 |
13 | `maxWidth`
14 |
15 | `maxHeight`
16 |
17 |
18 | ## Positioning
19 | `padding`
20 |
21 | `paddingTop`
22 |
23 | `paddingRight`
24 |
25 | `paddingBottom`
26 |
27 | `paddingLeft`
28 |
29 | `top`
30 |
31 | `right`
32 |
33 | `bottom`
34 |
35 | `left`
36 |
37 | `margin`
38 |
39 | `marginTop`
40 |
41 | `marginRight`
42 |
43 | `marginBottom`
44 |
45 | `marginLeft`
46 |
47 | ## Border Dimensions
48 | `borderWidth`
49 |
50 | `borderTopWidth`
51 |
52 | `borderRightWidth`
53 |
54 | `borderBottomWidth`
55 |
56 | `borderLeftWidth`
57 |
58 | `borderRadius`
59 |
60 | `outlineWidth`
61 |
62 | ## Text Properties
63 | `lineHeight`
64 |
65 | `fontSize` *(works for SVG)*
66 |
67 | `letterSpacing` *(works for SVG)*
68 |
69 | `wordSpacing` *(works for SVG)*
70 |
71 | ## Text Colors
72 | `color`
73 |
74 | `colorRed` is unitless
75 |
76 | `colorGreen` is unitless
77 |
78 | `colorBlue` is unitless
79 |
80 | `colorAlpha` is unitless
81 |
82 | ## Background Colors
83 | `backgroundColor`
84 |
85 | `backgroundColorRed` is unitless
86 |
87 | `backgroundColorGreen` is unitless
88 |
89 | `backgroundColorBlue` is unitless
90 |
91 | `backgroundColorAlpha` is unitless
92 |
93 | ## Border Colors
94 | `borderColor`
95 |
96 | `borderTopColor`
97 |
98 | `borderRightColor`
99 |
100 | `borderBottomColor`
101 |
102 | `borderLeftColor`
103 |
104 | ## Red Channel for Borders
105 | `borderColorRed` is unitless
106 |
107 | `borderTopColorRed` is unitless
108 |
109 | `borderRightColorRed` is unitless
110 |
111 | `borderBottomColorRed` is unitless
112 |
113 | `borderLeftColorRed` is unitless
114 |
115 | ### Green
116 | `borderColorGreen` is unitless
117 |
118 | `borderTopColorGreen` is unitless
119 |
120 | `borderRightColorGreen` is unitless
121 |
122 | `borderBottomColorGreen` is unitless
123 |
124 | `borderLeftColorGreen` is unitless
125 |
126 | ### Blue
127 | `borderColorBlue` is unitless
128 |
129 | `borderTopColorBlue` is unitless
130 |
131 | `borderRightColorBlue` is unitless
132 |
133 | `borderBottomColorBlue` is unitless
134 |
135 | `borderLeftColorBlue` is unitless
136 |
137 | ### Alpha
138 | `borderColorAlpha` is unitless
139 |
140 | `borderTopColorAlpha` is unitless
141 |
142 | `borderRightColorAlpha` is unitless
143 |
144 | `borderBottomColorAlpha` is unitless
145 |
146 | `borderLeftColorAlpha` is unitless
147 |
148 | ## Outline Color
149 | `outlineColor`
150 |
151 | `outlineColorRed` is unitless
152 |
153 | `outlineColorGreen` is unitless
154 |
155 | `outlineColorBlue` is unitless
156 |
157 | `outlineColorAlpha` is unitless
158 |
159 | ## Advanced Styles
160 | `backgroundPositionX`
161 |
162 | `backgroundPositionY`
163 |
164 | `textShadowX`
165 |
166 | `textShadowY`
167 |
168 | `textShadowBlur `
169 |
170 | `boxShadowX`
171 |
172 | `boxShadowY`
173 |
174 | `boxShadowBlur`
175 |
176 | `boxShadowSpread`
177 |
178 | ## Transformations
179 | `translateX` *(works for SVG)*
180 |
181 | `translateY` *(works for SVG)*
182 |
183 | `translateZ` *(works for SVG)*
184 |
185 | `scale` is unitless *(works for SVG)*
186 |
187 | `scaleX` is unitless *(works for SVG)*
188 |
189 | `scaleY` is unitless *(works for SVG)*
190 |
191 | `scaleZ` is unitless
192 |
193 | `rotateX` is unitless *(works for SVG)*
194 |
195 | `rotateY` is unitless *(works for SVG)*
196 |
197 | `rotateZ` is unitless *(works for SVG)*
198 |
199 | `skewX` is unitless *(works for SVG)*
200 |
201 | `skewZ` is unitless *(works for SVG)*
202 |
203 | `transformPerspective` *(works for SVG)*
204 |
205 | `perspective`
206 |
207 | `perspectiveOriginX`
208 |
209 | `perspectiveOriginY`
210 |
211 | `transformOriginX` *(works for SVG)*
212 |
213 | `transformOriginY` *(works for SVG)*
214 |
215 | `transformOriginZ` *(works for SVG)*
216 |
217 | `clipTop`
218 |
219 | `clipRight`
220 |
221 | `clipBottom`
222 |
223 | `clipLeft`
224 |
225 | `blur`
226 |
227 | For full CSS support, see [Velocity.js Tester](http://velocityjs.org/#cssSupport)
228 |
229 | # SVG-only Animatables
230 | `x`
231 |
232 | `y`
233 |
234 | `cx`
235 |
236 | `cy`
237 |
238 | `r`
239 |
240 | `rx`
241 |
242 | `ry`
243 |
244 | `x1`
245 |
246 | `x2`
247 |
248 | `y1`
249 |
250 | `y2`
251 |
252 | `strokeDasharray`
253 |
254 | `strokeDashoffset`
255 |
256 | `strokeWidth`
257 |
258 | `strokeMiterlimit` is unitless
259 |
260 | `startOffset`
261 |
262 | `fill`
263 |
264 | `fillRed` is unitless
265 |
266 | `fillGreen` is unitless
267 |
268 | `fillBlue` is unitless
269 |
270 | `fillAlpha` is unitless
271 |
272 | `fillOpacity` is unitless
273 |
274 | `stroke`
275 |
276 | `strokeRed` is unitless
277 |
278 | `strokeGreen` is unitless
279 |
280 | `strokeBlue` is unitless
281 |
282 | `strokeAlpha` is unitless
283 |
284 | `strokeOpacity` is unitless
285 |
286 | `stopColor`
287 |
288 | `stopColorRed` is unitless
289 |
290 | `stopColorGreen` is unitless
291 |
292 | `stopColorBlue` is unitless
293 |
294 | `stopColorAlpha` is unitless
295 |
296 | `stopOpacity` is unitless
297 |
298 | `offset`
299 |
300 | For full SVG support, see [Velocity.js site](http://velocityjs.org/#svg)
301 |
302 | If you find property you know Velocity can animate but ClassAnimation returns
303 | as unsupported, please [send me an e-mail about the issue](mailto:kyselyradek@gmail.com)
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ClassAnimation Module for Framer
2 |
3 | [](https://framer.cloud/aMvYj/)
4 |
5 | A simple module that allows you **animate individual HTML and SVG elements** inside your
6 | layer's **html** property in addition to Framer's core per-layer Animation.
7 |
8 | Shout out to [Velocity.js](http://velocityjs.org) for making great animation library ♥
9 |
10 | ## Install
11 |
12 |
14 |
15 |
16 | or
17 |
18 | 1. [Download the module](ClassAnimation.coffee?raw=true)
19 | 2. Copy the `ClassAnimation.coffee` file to your prototype's `modules` folder.
20 | 3. Call `{ClassAnimation} = require "ClassAnimation"` in your Framer prototype.
21 |
22 | ClassAnimation module depends on Velocity.js library. If you want to use your prototype offline,
23 | please download [velocity.min.js](https://github.com/julianshapiro/velocity/raw/master/velocity.min.js)
24 | and also include it in your
25 | `modules` folder. Otherwise, the module will download the library from web and you don't need to worry about it.
26 |
27 | ## How to Use [(see the live demo)](https://framer.cloud/aMvYj/)
28 |
29 | Import the module and optional handy [Style](#bonus-set-your-styles-like-a-human-being) function for fancy CSS initiation.
30 | ```coffeescript
31 | {ClassAnimation} = require "ClassAnimation"
32 | {Style} = require "ClassAnimation" # Optional
33 | ```
34 |
35 | ### Animate
36 |
37 | Module is designed to mimic Framer's core `Animation` as much as possible
38 | in order to make it easier to work with.
39 |
40 | Thanks to that, it takes similar inputs as the default Framer animation—just
41 | instead of `layer` use `string` with the name of class you want to animate.
42 |
43 |
44 | ```coffeescript
45 | socialCard = new Layer
46 |
47 | socialCard.html =
48 | """
49 | Paul Paulson
50 | Montessori Teacher
51 | """
52 |
53 | # Create a new animation for "className" class
54 | animationA = new ClassAnimation "className",
55 | color: "#ff00ff"
56 | fontSize: 60
57 | lineHeight: 70
58 | borderWidth: 5
59 |
60 |
61 | animationA.start()
62 | ```
63 |
64 | ### Animatable Properties [(see the full list)](AnimatableProperties.md)
65 | Because the module depends on Velocity.js library, it will animate whatever Velocity can animate.
66 |
67 | Although most properties are same as in core animation, there are very few exceptions, such as instead
68 | `rotation` you might want to use `rotateZ`.
69 |
70 | ClassAnimation *will print message if you use unsupported property* and will log the full
71 | list of supported ones to the console.
72 |
73 | If you find property you know Velocity can animate but ClassAnimation returns
74 | as unsupported, please [send me an e-mail about the issue](mailto:kyselyradek@gmail.com)
75 |
76 |
77 | ### Options
78 | Same options as core Animation are available, only `curve` takes different value format
79 | and `repeat` has the option `true` to repeat forever.
80 |
81 | ```coffeescript
82 | animationB = new ClassAnimation "className",
83 | color: "#ff00ff"
84 | fontSize: 60
85 | lineHeight: 70
86 | borderWidth: 5
87 | options:
88 | time: 2
89 | delay: .5
90 | repeat: 2
91 | curve: "spring"
92 |
93 | animationB.start()
94 | ```
95 |
96 | #### Curve Options
97 | `curve: "linear"`
98 |
99 | `curve: "ease"`
100 |
101 | `curve: "ease-in"`
102 |
103 | `curve: "ease-out"`
104 |
105 | `curve: "ease-in-out"` *(default)*
106 |
107 | `curve: [0, 1, 0, 1]` 4 parameters for [Bézier curve](http://cubic-bezier.com/)
108 |
109 | `curve: "spring"`
110 |
111 | `curve: [500, 20]` custom spring [tension, friction]
112 |
113 | For all the available magic and per-property curves, see [Velocity.js docs](http://velocityjs.org/#easing)
114 |
115 |
116 | ### Methods
117 | **`animation.toggle()`** switches between default and animated states
118 |
119 | **`animation.fadeIn()`** / **`animation.fadeOut()`** shows/hides elements targeted by animation
120 |
121 | *plus methods you know from core Animation:*
122 |
123 | **`animation.start()`**
124 |
125 | **`animation.stop()`**
126 |
127 | **`animation.reverse()`**
128 |
129 | **`animation.reset()`**
130 |
131 | **`animation.restart()`**
132 |
133 | ### Events
134 | **`Events.Animation`** returns progress `(0 to 1)` and remaining time `(ms)` **during animation**
135 |
136 | *plus events you know core Animation:*
137 |
138 | **`Events.AnimationStart`** doesn't return any value
139 |
140 | **`Events.AnimationStop`** doesn't return any value
141 |
142 | **`Events.AnimationEnd`** doesn't return any value
143 |
144 | ```coffeescript
145 | # Event Shortcuts
146 |
147 | animationA.onAnimation (prog, remain) ->
148 | print "#{prog*100}% done"
149 | print "#{remain}ms left"
150 |
151 | animationA.onAnimationStart ->
152 | print "Animation started"
153 |
154 | animationA.onAnimationStop ->
155 | print "Animation stopped"
156 |
157 | animationA.onAnimationEnd ->
158 | print "Animation ended"
159 | ```
160 |
161 | ## "But where's my .animate()?"
162 | Right here.
163 |
164 | ```coffeescript
165 | "className".animate
166 | color: "#ff00ff"
167 | fontSize: 60
168 | lineHeight: 70
169 | borderWidth: 5
170 | options:
171 | time: 2
172 | delay: .5
173 | repeat: 2
174 | curve: "spring"
175 |
176 |
177 | # You can also directly fadeIn/Out on any string
178 | "className".fadeOut()
179 | "differentClass".fadeIn()
180 | ```
181 |
182 |
183 | ## Bonus: Set your styles like a human being
184 |
185 | In case you tried writing your styles as CSS strings—or even worse, in-line
186 | inside the layer's html property—see this:
187 |
188 | ```coffeescript
189 | {Style} = require "ClassAnimation"
190 |
191 | # Style(name,props) helps you set your styles
192 | # in a cultivated way (as object, in camelCase)
193 | Style "className",
194 | fontSize: 50
195 | color: "#ff0000"
196 | borderWidth: 1
197 | borderColor: "rgb(255, 255, 0)"
198 |
199 |
200 | socialCard = new Layer
201 | socialCard.html =
202 | """
203 | Paul Paulson
204 | Montessori Teacher
205 | """
206 |
207 |
208 | # PS: If you already have a CSS string, just call .css()
209 | # method on it (this doesn't require {Style} import)
210 | ".differentClass {font-size: 30px; color: #ff00ff}".css()
211 | ```
212 |
--------------------------------------------------------------------------------
/ClassAnimation.coffee:
--------------------------------------------------------------------------------
1 | # VERSION 1.0.1
2 | # HELPER METHODS AND FUNCTIONS ——————————————————————————
3 | String::toCamel = () ->
4 | @replace( /([-_][a-z])/g, ($1) -> return $1.toUpperCase().replace(/[-_]/,'') )
5 |
6 | String::toCss = () ->
7 | @replace( /([A-Z])/g, ($1) -> return "-"+$1.toLowerCase() );
8 |
9 | point = (cl) -> if cl.indexOf(".") is 0 then cl else ".#{cl}"
10 |
11 | getHex = (c) ->
12 | hexa = c.toString(16)
13 | return if hexa.length is 1 then "0#{hexa}" else hexa
14 |
15 | rgb = ([r,g,b] = color) -> "##{getHex(r)}#{getHex(g)}#{getHex(b)}";
16 |
17 | String::checkColor = () ->
18 | if @indexOf("rgb") != 0
19 | return @
20 | else
21 | color = [r,g,b] = @split("(")[1].split(",")
22 | return rgb( (parseInt(value) for value in color) )
23 |
24 |
25 | renameKey = (obj, oldName, newName) ->
26 | if oldName is newName
27 | return obj
28 |
29 | if obj.hasOwnProperty oldName
30 | obj[newName] = obj[oldName]
31 | delete obj[oldName]
32 |
33 | return obj
34 |
35 |
36 | # INCLUDE VELOCITY.JS LIBRARY ———————————————————————————
37 | insertVelocity = (script, webscript, name) ->
38 | try
39 | Utils.domLoadScriptSync(script)
40 | Utils.domComplete ->
41 | console.log "%c#{script} Successfully Included", "background: #DDFFE3; color: #007814"
42 | catch e
43 | console.log "%cCouldn't load '#{script}' locally. Will try downloading from web.", "background: #FFF0DB; color: #D27B00"
44 | try
45 | Utils.domLoadScriptSync(webscript)
46 | Utils.domComplete ->
47 | console.log "%c#{name} Successfully Included from Web", "background: #DDFFE3; color: #007814"
48 | catch e
49 | throw Error("ClassAnimation: Sorry, I don't know how to animate without #{name} library")
50 |
51 |
52 | insertVelocity("modules/velocity.min.js", "//cdn.jsdelivr.net/velocity/1.4/velocity.min.js", "Velocity.js")
53 | #insertVelocity("modules/velocity.ui.min.js", "//cdn.jsdelivr.net/velocity/1.4/velocity.ui.min.js", "Velocity UI")
54 |
55 |
56 |
57 | # OBJECT WITH PROPERTIES AND THEIR OPTIONS ——————————————
58 | # [UNITLESS, ANIMATABLE, SVG]
59 | ANIMATABLES =
60 | fontWeight: [true, false]
61 | fontFamily: [true, false]
62 | textAlign: [false, false]
63 | opacity: [true, true, true]
64 | zIndex: [true, false]
65 |
66 | width: [false, true, true]
67 | height: [false, true, true]
68 | minWidth: [false, true]
69 | minHeight: [false, true]
70 | maxWidth: [false, true]
71 | maxHeight: [false, true]
72 |
73 | "\n— POSITIONING —": [false, true]
74 | padding: [false, true]
75 | paddingTop: [false, true]
76 | paddingRight: [false, true]
77 | paddingBottom: [false, true]
78 | paddingLeft: [false, true]
79 |
80 | top: [false, true]
81 | right: [false, true]
82 | bottom: [false, true]
83 | left: [false, true]
84 |
85 | margin: [false, true]
86 | marginTop: [false, true]
87 | marginRight: [false, true]
88 | marginBottom: [false, true]
89 | marginLeft: [false, true]
90 |
91 | "\n— BORDER DIMENSIONS —": [false, true]
92 | borderWidth: [false, true]
93 | borderTopWidth: [false, true]
94 | borderRightWidth: [false, true]
95 | borderBottomWidth: [false, true]
96 | borderLeftWidth: [false, true]
97 | borderRadius: [false, true]
98 | outlineWidth: [false, true]
99 |
100 | "\n— TEXT PROPERTIES —": [false, true]
101 | lineHeight: [false, true]
102 | fontSize: [false, true, true]
103 | letterSpacing: [false, true, true]
104 | wordSpacing: [false, true, true]
105 |
106 | "\n— TEXT COLORS —": [false, true]
107 | color: [false, true]
108 | colorRed: [true, true]
109 | colorGreen: [true, true]
110 | colorBlue: [true, true]
111 | colorAlpha: [true, true]
112 |
113 | "\n— BACKGROUND COLORS —": [false, true]
114 | backgroundColor: [false, true]
115 | backgroundColorRed: [true, true]
116 | backgroundColorGreen: [true, true]
117 | backgroundColorBlue: [true, true]
118 | backgroundColorAlpha: [true, true]
119 |
120 | "\n— BORDER COLORS —": [false, true]
121 | borderColor: [false, true]
122 | borderTopColor: [false, true]
123 | borderRightColor: [false, true]
124 | borderBottomColor: [false, true]
125 | borderLeftColor: [false, true]
126 |
127 | "\n— RED CHANNEL OF BORDERS —": [false, true]
128 | borderColorRed: [true, true]
129 | borderTopColorRed: [true, true]
130 | borderRightColorRed: [true, true]
131 | borderBottomColorRed: [true, true]
132 | borderLeftColorRed: [true, true]
133 |
134 | "\nGREEN:": [false, true]
135 | borderColorGreen: [true, true]
136 | borderTopColorGreen: [true, true]
137 | borderRightColorGreen: [true, true]
138 | borderBottomColorGreen: [true, true]
139 | borderLeftColorGreen: [true, true]
140 |
141 | "\nBLUE:": [false, true]
142 | borderColorBlue: [true, true]
143 | borderTopColorBlue: [true, true]
144 | borderRightColorBlue: [true, true]
145 | borderBottomColorBlue: [true, true]
146 | borderLeftColorBlue: [true, true]
147 |
148 | "\nALPHA:": [false, true]
149 | borderColorAlpha: [true, true]
150 | borderTopColorAlpha: [true, true]
151 | borderRightColorAlpha: [true, true]
152 | borderBottomColorAlpha: [true, true]
153 | borderLeftColorAlpha: [true, true]
154 |
155 | "\n— OUTLINE COLOR —": [false, true]
156 | outlineColor: [false, true]
157 | outlineColorRed: [true, true]
158 | outlineColorGreen: [true, true]
159 | outlineColorBlue: [true, true]
160 | outlineColorAlpha: [true, true]
161 |
162 | "\n— ADVANCED STYLES —": [false, true]
163 | backgroundPositionX: [false, true]
164 | backgroundPositionY: [false, true]
165 | textShadowX: [false, true]
166 | textShadowY: [false, true]
167 | textShadowBlur: [false, true]
168 | boxShadowX: [false, true]
169 | boxShadowY: [false, true]
170 | boxShadowBlur: [false, true]
171 | boxShadowSpread: [false, true]
172 |
173 | "\n— TRANSFORMATIONS —": [false, true]
174 | translateX: [false, true, true]
175 | translateY: [false, true, true]
176 | translateZ: [false, true, true]
177 |
178 | scale: [true, true, true]
179 | scaleX: [true, true, true]
180 | scaleY: [true, true, true]
181 | scaleZ: [true, true]
182 |
183 | rotateX: [true, true, true]
184 | rotateY: [true, true, true]
185 | rotateZ: [true, true, true]
186 |
187 | skewX: [true, true, true]
188 | skewZ: [true, true, true]
189 |
190 | transformPerspective: [false, true, true]
191 | perspective: [false, true]
192 | perspectiveOriginX: [false, true]
193 | perspectiveOriginY: [false, true]
194 | transformOriginX: [false, true, true]
195 | transformOriginY: [false, true, true]
196 | transformOriginZ: [false, true, true]
197 |
198 | clipTop: [false, true]
199 | clipRight: [false, true]
200 | clipBottom: [false, true]
201 | clipLeft: [false, true]
202 |
203 | blur: [false, true]
204 |
205 | "\n— SVG-only —": [false, true]
206 | x: [false, true]
207 | y: [false, true]
208 | cx: [false, true]
209 | cy: [false, true]
210 | r: [false, true]
211 | rx: [false, true]
212 | ry: [false, true]
213 | x1: [false, true]
214 | x2: [false, true]
215 | y1: [false, true]
216 | y2: [false, true]
217 |
218 | strokeDasharray: [false, true]
219 | strokeDashoffset: [false, true]
220 | strokeWidth: [false, true]
221 | strokeMiterlimit: [true, true]
222 | startOffset: [false, true]
223 |
224 | fill: [false, true]
225 | fillRed: [true, true]
226 | fillGreen: [true, true]
227 | fillBlue: [true, true]
228 | fillAlpha: [true, true]
229 | fillOpacity: [true, true]
230 |
231 | stroke: [false, true]
232 | strokeRed: [true, true]
233 | strokeGreen: [true, true]
234 | strokeBlue: [true, true]
235 | strokeAlpha: [true, true]
236 | strokeOpacity: [true, true]
237 |
238 | stopColor: [false, true]
239 | stopColorRed: [true, true]
240 | stopColorGreen: [true, true]
241 | stopColorBlue: [true, true]
242 | stopColorAlpha: [true, true]
243 | stopOpacity: [true, true]
244 |
245 | offset: [false, true]
246 |
247 | isAnimatable = (property, thr) ->
248 | if typeof ANIMATABLES[property] is 'object' && ANIMATABLES[property][1]
249 | return true
250 | else
251 | moduleHelp() if thr
252 | print "›#{property}‹ is not animatable. Open console (Target icon bottom left) and Cmd+R to see animatable properties." if thr
253 | return false
254 |
255 |
256 |
257 | # GET CSS DECLARATIONS FROM CLASS ———————————————————————
258 | getCSS = (cl, checker) ->
259 | classes = document.getElementsByClassName(cl);
260 | cssDOM = window.getComputedStyle(classes[0], null)
261 |
262 | cssObject = {}
263 |
264 | for val in [0...cssDOM.length]
265 | currentVal = cssDOM[val]
266 | if currentVal.toCamel() of checker
267 | cssObject[currentVal.toCamel()] = cssDOM.getPropertyValue(currentVal)
268 | cssObject[currentVal.toCamel()] = cssDOM.getPropertyValue(currentVal).checkColor() if typeof cssDOM.getPropertyValue(currentVal) is "string"
269 |
270 | return cssObject
271 |
272 |
273 | # MAIN CSSANIMATION CLASS ———————————————————————————————
274 | # ———————————————————————————————————————————————————————
275 | # ———————————————————————————————————————————————————————
276 | class exports.ClassAnimation
277 | constructor: (name, end = {}, init, options = {}, optionsFactor = 1000) ->
278 | @class = name
279 | @target = document.querySelectorAll(".#{@class}");
280 |
281 | @endState = end
282 |
283 | @options = @endState.options || options
284 | @options.time = @options.time*optionsFactor || 1000
285 | @options.delay = @options.delay*optionsFactor || 0
286 | @options.repeat = @options.repeat || false
287 | @options.curve = @options.curve || "ease-in-out"
288 |
289 | delete @endState.options
290 |
291 | for key, val of @endState
292 | @endState[key] = val.checkColor() if typeof val is "string"
293 | renameKey(@endState, key, key.toCamel())
294 | isAnimatable(key.toCamel(), true)
295 |
296 | @initState = init || getCSS(@class, @endState)
297 |
298 | return @
299 |
300 |
301 | # METHODS ———————————————————————————————————————————
302 |
303 | start: () ->
304 | _this = @
305 | Utils.domComplete ->
306 | Velocity _this.target,
307 | _this.endState,
308 | duration: _this.options.time
309 | delay: _this.options.delay
310 | loop: _this.options.repeat
311 | easing: _this.options.curve
312 | begin: -> _this.onStart?()
313 | complete: -> _this.onEnd?()
314 | progress: (elements, complete, remaining, start) ->
315 | _this.onAnim?(complete, remaining)
316 | @notFirst = true
317 | return @
318 |
319 | stop: () ->
320 | @onStop?()
321 | Velocity(@target, "stop")
322 | return @
323 |
324 | reset: () ->
325 | Velocity @target,
326 | @initState,
327 | duration: 0
328 | delay: 0
329 | return @
330 |
331 | restart: () ->
332 | @reset()
333 | @start()
334 | return @
335 |
336 | finish: () ->
337 | Velocity(@target, "finish")
338 | return @
339 |
340 | toggle: () ->
341 | _this = @
342 | if @notFirst
343 | Velocity @target,
344 | "reverse",
345 | begin: -> _this.onStart?()
346 | complete: -> _this.onEnd?()
347 | progress: (elements, complete, remaining, start) ->
348 | _this.onAnim?(complete, remaining)
349 | else
350 | @start()
351 | return @
352 |
353 | reverse: () ->
354 | return new ClassAnimation(@class, @initState, @endState, @options, 1)
355 |
356 | fadeIn: () ->
357 | _this = @
358 | Velocity @target,
359 | "fadeIn",
360 | begin: -> _this.onStart?()
361 | complete: -> _this.onEnd?()
362 | progress: (elements, complete, remaining, start) ->
363 | _this.onAnim?(complete, remaining)
364 | return @
365 |
366 | fadeOut: () ->
367 | _this = @
368 | Velocity @target,
369 | "fadeOut",
370 | begin: -> _this.onStart?()
371 | complete: -> _this.onEnd?()
372 | progress: (elements, complete, remaining, start) ->
373 | _this.onAnim?(complete, remaining)
374 | return @
375 |
376 | help: () -> moduleHelp()
377 |
378 |
379 | # METHOD ALIASES ————————————————————————————————————
380 |
381 | switch: -> @toggle()
382 | stateSwitch: -> @toggle()
383 | revert: -> @reverse()
384 | inverse: -> @reverse()
385 | invert: -> @reverse()
386 |
387 |
388 | # EVENTS ————————————————————————————————————————————
389 |
390 | Events.Animation = "move"
391 |
392 | on: (eventName, cb) ->
393 | switch eventName
394 | when "end" then @onAnimationEnd(cb)
395 | when "stop" then @onAnimationStop(cb)
396 | when "start" then @onAnimationStart(cb)
397 | when "move" then @onAnimation(cb)
398 | else throw Error("Sorry, I'm too stupid to handle this event")
399 |
400 | onAnimationEnd: (cb) -> @onEnd = @eventReturnData(cb)
401 | onAnimationStop: (cb) -> @onStop = @eventReturnData(cb)
402 | onAnimationStart: (cb) -> @onStart = @eventReturnData(cb)
403 | onAnimation: (cb) -> @onAnim = @eventReturnData(cb)
404 |
405 | eventReturnData: (cb) ->
406 | return (prog, rem) -> cb.call?(@, prog, rem)
407 |
408 |
409 |
410 | # .ANIMATE METHOD ———————————————————————————————————————
411 | String::animate = (props) ->
412 | (new exports.ClassAnimation @, props).start()
413 |
414 | String::fadeIn = () -> @animate().fadeIn()
415 |
416 | String::fadeOut = () -> @animate().fadeOut()
417 |
418 |
419 |
420 | # STYLE INIT ————————————————————————————————————————————
421 | String::css = () ->
422 | Utils.insertCSS(@)
423 |
424 | addUnit = (value, key) ->
425 | if typeof value is 'number' && isAnimatable(key, false) && !ANIMATABLES[key][0] then "#{value}px" else value
426 |
427 | exports.Style = (cl, css) ->
428 | insertableCss = "#{point(cl)} {border-style: solid; border-width: 0; "
429 | for key, val of css
430 | css[key] = addUnit( css[key], key.toCamel() )
431 | insertableCss += "#{key.toCss()}:#{css[key]}; "
432 | insertableCss += "}"
433 |
434 | Utils.insertCSS(insertableCss)
435 |
436 | return cl
437 |
438 | exports.style = (cl, css) -> exports.Style(cl, css)
439 |
440 |
441 |
442 | # HELP ——————————————————————————————————————————————————
443 | moduleHelp = () ->
444 | console.log "\n"
445 | console.log "LIST OF ANIMATABLE PROPERTIES"
446 | for key, val of ANIMATABLES
447 | help = if ANIMATABLES[key][0] is true then "%c#{key} %cis unitless" else "%c#{key} %c"
448 | help += if ANIMATABLES[key][2] is true then " %c(works for SVG)" else " %c"
449 | console.log help, "color: #007AFF", "color: #000", "color: #007814" if ANIMATABLES[key][1] is true
450 | console.log "For full support, go to http://velocityjs.org/#cssSupport"
451 |
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/modules/ClassAnimation.coffee:
--------------------------------------------------------------------------------
1 | # VERSION 1.0.0
2 | # HELPER METHODS AND FUNCTIONS ——————————————————————————
3 | String::toCamel = () ->
4 | @replace( /([-_][a-z])/g, ($1) -> return $1.toUpperCase().replace(/[-_]/,'') )
5 |
6 | String::toCss = () ->
7 | @replace( /([A-Z])/g, ($1) -> return "-"+$1.toLowerCase() );
8 |
9 | point = (cl) -> if cl.indexOf(".") is 0 then cl else ".#{cl}"
10 |
11 | getHex = (c) ->
12 | hexa = c.toString(16)
13 | return if hexa.length is 1 then "0#{hexa}" else hexa
14 |
15 | rgb = ([r,g,b] = color) -> "##{getHex(r)}#{getHex(g)}#{getHex(b)}";
16 |
17 | String::checkColor = () ->
18 | if @indexOf("rgb") != 0
19 | return @
20 | else
21 | color = [r,g,b] = @split("(")[1].split(",")
22 | return rgb( (parseInt(value) for value in color) )
23 |
24 |
25 | renameKey = (obj, oldName, newName) ->
26 | if oldName is newName
27 | return obj
28 |
29 | if obj.hasOwnProperty oldName
30 | obj[newName] = obj[oldName]
31 | delete obj[oldName]
32 |
33 | return obj
34 |
35 |
36 | # INCLUDE VELOCITY.JS LIBRARY ———————————————————————————
37 | insertVelocity = (script, webscript, name) ->
38 | try
39 | Utils.domLoadScriptSync(script)
40 | Utils.domComplete ->
41 | console.log "%c#{script} Successfully Included", "background: #DDFFE3; color: #007814"
42 | catch e
43 | console.log "%cCouldn't load '#{script}' locally. Will try downloading from web.", "background: #FFF0DB; color: #D27B00"
44 | try
45 | Utils.domLoadScriptSync(webscript)
46 | Utils.domComplete ->
47 | console.log "%c#{name} Successfully Included from Web", "background: #DDFFE3; color: #007814"
48 | catch e
49 | throw Error("ClassAnimation: Sorry, I don't know how to animate without #{name} library")
50 |
51 |
52 | insertVelocity("modules/velocity.min.js", "//cdn.jsdelivr.net/velocity/1.4/velocity.min.js", "Velocity.js")
53 | #insertVelocity("modules/velocity.ui.min.js", "//cdn.jsdelivr.net/velocity/1.4/velocity.ui.min.js", "Velocity UI")
54 |
55 |
56 |
57 | # OBJECT WITH PROPERTIES AND THEIR OPTIONS ——————————————
58 | # [UNITLESS, ANIMATABLE, SVG]
59 | ANIMATABLES =
60 | fontWeight: [true, false]
61 | fontFamily: [true, false]
62 | textAlign: [false, false]
63 | opacity: [true, true, true]
64 | zIndex: [true, false]
65 |
66 | width: [false, true, true]
67 | height: [false, true, true]
68 | minWidth: [false, true]
69 | minHeight: [false, true]
70 | maxWidth: [false, true]
71 | maxHeight: [false, true]
72 |
73 | "\n— POSITIONING —": [false, true]
74 | padding: [false, true]
75 | paddingTop: [false, true]
76 | paddingRight: [false, true]
77 | paddingBottom: [false, true]
78 | paddingLeft: [false, true]
79 |
80 | top: [false, true]
81 | right: [false, true]
82 | bottom: [false, true]
83 | left: [false, true]
84 |
85 | margin: [false, true]
86 | marginTop: [false, true]
87 | marginRight: [false, true]
88 | marginBottom: [false, true]
89 | marginLeft: [false, true]
90 |
91 | "\n— BORDER DIMENSIONS —": [false, true]
92 | borderWidth: [false, true]
93 | borderTopWidth: [false, true]
94 | borderRightWidth: [false, true]
95 | borderBottomWidth: [false, true]
96 | borderLeftWidth: [false, true]
97 | borderRadius: [false, true]
98 | outlineWidth: [false, true]
99 |
100 | "\n— TEXT PROPERTIES —": [false, true]
101 | lineHeight: [false, true]
102 | fontSize: [false, true, true]
103 | letterSpacing: [false, true, true]
104 | wordSpacing: [false, true, true]
105 |
106 | "\n— TEXT COLORS —": [false, true]
107 | color: [false, true]
108 | colorRed: [true, true]
109 | colorGreen: [true, true]
110 | colorBlue: [true, true]
111 | colorAlpha: [true, true]
112 |
113 | "\n— BACKGROUND COLORS —": [false, true]
114 | backgroundColor: [false, true]
115 | backgroundColorRed: [true, true]
116 | backgroundColorGreen: [true, true]
117 | backgroundColorBlue: [true, true]
118 | backgroundColorAlpha: [true, true]
119 |
120 | "\n— BORDER COLORS —": [false, true]
121 | borderColor: [false, true]
122 | borderTopColor: [false, true]
123 | borderRightColor: [false, true]
124 | borderBottomColor: [false, true]
125 | borderLeftColor: [false, true]
126 |
127 | "\n— RED CHANNEL OF BORDERS —": [false, true]
128 | borderColorRed: [true, true]
129 | borderTopColorRed: [true, true]
130 | borderRightColorRed: [true, true]
131 | borderBottomColorRed: [true, true]
132 | borderLeftColorRed: [true, true]
133 |
134 | "\nGREEN:": [false, true]
135 | borderColorGreen: [true, true]
136 | borderTopColorGreen: [true, true]
137 | borderRightColorGreen: [true, true]
138 | borderBottomColorGreen: [true, true]
139 | borderLeftColorGreen: [true, true]
140 |
141 | "\nBLUE:": [false, true]
142 | borderColorBlue: [true, true]
143 | borderTopColorBlue: [true, true]
144 | borderRightColorBlue: [true, true]
145 | borderBottomColorBlue: [true, true]
146 | borderLeftColorBlue: [true, true]
147 |
148 | "\nALPHA:": [false, true]
149 | borderColorAlpha: [true, true]
150 | borderTopColorAlpha: [true, true]
151 | borderRightColorAlpha: [true, true]
152 | borderBottomColorAlpha: [true, true]
153 | borderLeftColorAlpha: [true, true]
154 |
155 | "\n— OUTLINE COLOR —": [false, true]
156 | outlineColor: [false, true]
157 | outlineColorRed: [true, true]
158 | outlineColorGreen: [true, true]
159 | outlineColorBlue: [true, true]
160 | outlineColorAlpha: [true, true]
161 |
162 | "\n— ADVANCED STYLES —": [false, true]
163 | backgroundPositionX: [false, true]
164 | backgroundPositionY: [false, true]
165 | textShadowX: [false, true]
166 | textShadowY: [false, true]
167 | textShadowBlur: [false, true]
168 | boxShadowX: [false, true]
169 | boxShadowY: [false, true]
170 | boxShadowBlur: [false, true]
171 | boxShadowSpread: [false, true]
172 |
173 | "\n— TRANSFORMATIONS —": [false, true]
174 | translateX: [false, true, true]
175 | translateY: [false, true, true]
176 | translateZ: [false, true, true]
177 |
178 | scale: [true, true, true]
179 | scaleX: [true, true, true]
180 | scaleY: [true, true, true]
181 | scaleZ: [true, true]
182 |
183 | rotateX: [true, true, true]
184 | rotateY: [true, true, true]
185 | rotateZ: [true, true, true]
186 |
187 | skewX: [true, true, true]
188 | skewZ: [true, true, true]
189 |
190 | transformPerspective: [false, true, true]
191 | perspective: [false, true]
192 | perspectiveOriginX: [false, true]
193 | perspectiveOriginY: [false, true]
194 | transformOriginX: [false, true, true]
195 | transformOriginY: [false, true, true]
196 | transformOriginZ: [false, true, true]
197 |
198 | clipTop: [false, true]
199 | clipRight: [false, true]
200 | clipBottom: [false, true]
201 | clipLeft: [false, true]
202 |
203 | blur: [false, true]
204 |
205 | "\n— SVG-only —": [false, true]
206 | x: [false, true]
207 | y: [false, true]
208 | cx: [false, true]
209 | cy: [false, true]
210 | r: [false, true]
211 | rx: [false, true]
212 | ry: [false, true]
213 | x1: [false, true]
214 | x2: [false, true]
215 | y1: [false, true]
216 | y2: [false, true]
217 |
218 | strokeDasharray: [false, true]
219 | strokeDashoffset: [false, true]
220 | strokeWidth: [false, true]
221 | strokeMiterlimit: [true, true]
222 | startOffset: [false, true]
223 |
224 | fill: [false, true]
225 | fillRed: [true, true]
226 | fillGreen: [true, true]
227 | fillBlue: [true, true]
228 | fillAlpha: [true, true]
229 | fillOpacity: [true, true]
230 |
231 | stroke: [false, true]
232 | strokeRed: [true, true]
233 | strokeGreen: [true, true]
234 | strokeBlue: [true, true]
235 | strokeAlpha: [true, true]
236 | strokeOpacity: [true, true]
237 |
238 | stopColor: [false, true]
239 | stopColorRed: [true, true]
240 | stopColorGreen: [true, true]
241 | stopColorBlue: [true, true]
242 | stopColorAlpha: [true, true]
243 | stopOpacity: [true, true]
244 |
245 | offset: [false, true]
246 |
247 | isAnimatable = (property, thr) ->
248 | if typeof ANIMATABLES[property] is 'object' && ANIMATABLES[property][1]
249 | return true
250 | else
251 | moduleHelp() if thr
252 | print "›#{property}‹ is not animatable. Open console (Target icon bottom left) and Cmd+R to see animatable properties." if thr
253 | return false
254 |
255 |
256 |
257 | # GET CSS DECLARATIONS FROM CLASS ———————————————————————
258 | getCSS = (cl, checker) ->
259 | classes = document.getElementsByClassName(cl);
260 | cssDOM = window.getComputedStyle(classes[0], null)
261 |
262 | cssObject = {}
263 |
264 | for val in [0...cssDOM.length]
265 | currentVal = cssDOM[val]
266 | if currentVal.toCamel() of checker
267 | cssObject[currentVal.toCamel()] = cssDOM.getPropertyValue(currentVal)
268 | cssObject[currentVal.toCamel()] = cssDOM.getPropertyValue(currentVal).checkColor() if typeof cssDOM.getPropertyValue(currentVal) is "string"
269 |
270 | return cssObject
271 |
272 |
273 | # MAIN CSSANIMATION CLASS ———————————————————————————————
274 | # ———————————————————————————————————————————————————————
275 | # ———————————————————————————————————————————————————————
276 | class exports.ClassAnimation
277 | constructor: (name, end = {}, init, options = {}, optionsFactor = 1000) ->
278 | @class = name
279 | @target = document.querySelectorAll(".#{@class}");
280 |
281 | @endState = end
282 |
283 | @options = @endState.options || options
284 | @options.time = @options.time*optionsFactor || 1000
285 | @options.delay = @options.delay*optionsFactor || 0
286 | @options.repeat = @options.repeat || false
287 | @options.curve = @options.curve || "ease-in-out"
288 |
289 | delete @endState.options
290 |
291 | for key, val of @endState
292 | @endState[key] = val.checkColor() if typeof val is "string"
293 | renameKey(@endState, key, key.toCamel())
294 | isAnimatable(key.toCamel(), true)
295 |
296 | @initState = init || getCSS(@class, @endState)
297 |
298 | return @
299 |
300 |
301 | # METHODS ———————————————————————————————————————————
302 |
303 | start: () ->
304 | _this = @
305 | Utils.domComplete ->
306 | Velocity _this.target,
307 | _this.endState,
308 | duration: _this.options.time
309 | delay: _this.options.delay
310 | loop: _this.options.repeat
311 | easing: _this.options.curve
312 | begin: -> _this.onStart?()
313 | complete: -> _this.onEnd?()
314 | progress: (elements, complete, remaining, start) ->
315 | _this.onAnim?(complete, remaining)
316 | @notFirst = true
317 | return @
318 |
319 | stop: () ->
320 | @onStop?()
321 | Velocity(@target, "stop")
322 | return @
323 |
324 | reset: () ->
325 | Velocity @target,
326 | @initState,
327 | duration: 0
328 | delay: 0
329 | return @
330 |
331 | restart: () ->
332 | @reset()
333 | @start()
334 | return @
335 |
336 | finish: () ->
337 | Velocity(@target, "finish")
338 | return @
339 |
340 | toggle: () ->
341 | _this = @
342 | if @notFirst
343 | Velocity @target,
344 | "reverse",
345 | begin: -> _this.onStart?()
346 | complete: -> _this.onEnd?()
347 | progress: (elements, complete, remaining, start) ->
348 | _this.onAnim?(complete, remaining)
349 | else
350 | @start()
351 | return @
352 |
353 | reverse: () ->
354 | return new ClassAnimation(@class, @initState, @endState, @options, 1)
355 |
356 | fadeIn: () ->
357 | _this = @
358 | Velocity @target,
359 | "fadeIn",
360 | begin: -> _this.onStart?()
361 | complete: -> _this.onEnd?()
362 | progress: (elements, complete, remaining, start) ->
363 | _this.onAnim?(complete, remaining)
364 | return @
365 |
366 | fadeOut: () ->
367 | _this = @
368 | Velocity @target,
369 | "fadeOut",
370 | begin: -> _this.onStart?()
371 | complete: -> _this.onEnd?()
372 | progress: (elements, complete, remaining, start) ->
373 | _this.onAnim?(complete, remaining)
374 | return @
375 |
376 | help: () -> moduleHelp()
377 |
378 |
379 | # METHOD ALIASES ————————————————————————————————————
380 |
381 | switch: -> @toggle()
382 | stateSwitch: -> @toggle()
383 | revert: -> @reverse()
384 | inverse: -> @reverse()
385 | invert: -> @reverse()
386 |
387 |
388 | # EVENTS ————————————————————————————————————————————
389 |
390 | Events.Animation = "move"
391 |
392 | on: (eventName, cb) ->
393 | switch eventName
394 | when "end" then @onAnimationEnd(cb)
395 | when "stop" then @onAnimationStop(cb)
396 | when "start" then @onAnimationStart(cb)
397 | when "move" then @onAnimation(cb)
398 | else throw Error("Sorry, I'm too stupid to handle this event")
399 |
400 | onAnimationEnd: (cb) -> @onEnd = @eventReturnData(cb)
401 | onAnimationStop: (cb) -> @onStop = @eventReturnData(cb)
402 | onAnimationStart: (cb) -> @onStart = @eventReturnData(cb)
403 | onAnimation: (cb) -> @onAnim = @eventReturnData(cb)
404 |
405 | eventReturnData: (cb) ->
406 | return (prog, rem) -> cb.call?(@, prog, rem)
407 |
408 |
409 |
410 | # .ANIMATE METHOD ———————————————————————————————————————
411 | String::animate = (props) ->
412 | (new exports.ClassAnimation @, props).start()
413 |
414 | String::fadeIn = () -> @animate().fadeIn()
415 |
416 | String::fadeOut = () -> @animate().fadeOut()
417 |
418 |
419 |
420 | # STYLE INIT ————————————————————————————————————————————
421 | String::css = () ->
422 | Utils.insertCSS(@)
423 |
424 | addUnit = (value, key) ->
425 | if typeof value is 'number' && isAnimatable(key, false) && !ANIMATABLES[key][0] then "#{value}px" else value
426 |
427 | exports.Style = (cl, css) ->
428 | insertableCss = "#{point(cl)} {border-style: solid; border-width: 0; "
429 | for key, val of css
430 | css[key] = addUnit( css[key], key.toCamel() )
431 | insertableCss += "#{key.toCss()}:#{css[key]}; "
432 | insertableCss += "}"
433 |
434 | Utils.insertCSS(insertableCss)
435 |
436 | return cl
437 |
438 | exports.style = (cl, css) -> exports.Style(cl, css)
439 |
440 |
441 |
442 | # HELP ——————————————————————————————————————————————————
443 | moduleHelp = () ->
444 | console.log "\n"
445 | console.log "LIST OF ANIMATABLE PROPERTIES"
446 | for key, val of ANIMATABLES
447 | help = if ANIMATABLES[key][0] is true then "%c#{key} %cis unitless" else "%c#{key} %c"
448 | help += if ANIMATABLES[key][2] is true then " %c(works for SVG)" else " %c"
449 | console.log help, "color: #007AFF", "color: #000", "color: #007814" if ANIMATABLES[key][1] is true
450 | console.log "For full support, go to http://velocityjs.org/#cssSupport"
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/images/delivery.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | delivery
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/ClassAnimationDemo.framer/modules/velocity.min.js:
--------------------------------------------------------------------------------
1 | /*! VelocityJS.org (1.4.2). (C) 2014 Julian Shapiro. MIT @license: en.wikipedia.org/wiki/MIT_License */
2 | /*! VelocityJS.org jQuery Shim (1.0.1). (C) 2014 The jQuery Foundation. MIT @license: en.wikipedia.org/wiki/MIT_License. */
3 | !function(a){"use strict";function b(a){var b=a.length,d=c.type(a);return"function"!==d&&!c.isWindow(a)&&(!(1!==a.nodeType||!b)||("array"===d||0===b||"number"==typeof b&&b>0&&b-1 in a))}if(!a.jQuery){var c=function(a,b){return new c.fn.init(a,b)};c.isWindow=function(a){return a&&a===a.window},c.type=function(a){return a?"object"==typeof a||"function"==typeof a?e[g.call(a)]||"object":typeof a:a+""},c.isArray=Array.isArray||function(a){return"array"===c.type(a)},c.isPlainObject=function(a){var b;if(!a||"object"!==c.type(a)||a.nodeType||c.isWindow(a))return!1;try{if(a.constructor&&!f.call(a,"constructor")&&!f.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(d){return!1}for(b in a);return void 0===b||f.call(a,b)},c.each=function(a,c,d){var e,f=0,g=a.length,h=b(a);if(d){if(h)for(;f0?e=g:c=g;while(Math.abs(f)>r&&++h=q?k(b,h):0===i?h:m(b,c,c+u)}function o(){y=!0,a===c&&d===e||l()}var p=4,q=.001,r=1e-7,s=10,t=11,u=1/(t-1),v="Float32Array"in b;if(4!==arguments.length)return!1;for(var w=0;w<4;++w)if("number"!=typeof arguments[w]||isNaN(arguments[w])||!isFinite(arguments[w]))return!1;a=Math.min(a,1),d=Math.min(d,1),a=Math.max(a,0),d=Math.max(d,0);var x=v?new Float32Array(t):new Array(t),y=!1,z=function(b){return y||o(),a===c&&d===e?b:0===b?0:1===b?1:i(n(b),c,e)};z.getControlPoints=function(){return[{x:a,y:c},{x:d,y:e}]};var A="generateBezier("+[a,c,d,e]+")";return z.toString=function(){return A},z}function l(a,b){var c=a;return t.isString(a)?x.Easings[a]||(c=!1):c=t.isArray(a)&&1===a.length?j.apply(null,a):t.isArray(a)&&2===a.length?y.apply(null,a.concat([b])):!(!t.isArray(a)||4!==a.length)&&k.apply(null,a),c===!1&&(c=x.Easings[x.defaults.easing]?x.defaults.easing:w),c}function m(a){if(a){var b=x.timestamp&&a!==!0?a:r.now(),c=x.State.calls.length;c>1e4&&(x.State.calls=e(x.State.calls),c=x.State.calls.length);for(var f=0;f4;a--){var b=c.createElement("div");if(b.innerHTML="",b.getElementsByTagName("span").length)return b=null,a}return d}(),q=function(){var a=0;return b.webkitRequestAnimationFrame||b.mozRequestAnimationFrame||function(b){var c,d=(new Date).getTime();return c=Math.max(0,16-(d-a)),a=d+c,setTimeout(function(){b(d+c)},c)}}(),r=function(){var a=b.performance||{};if(!Object.prototype.hasOwnProperty.call(a,"now")){var c=a.timing&&a.timing.domComplete?a.timing.domComplete:(new Date).getTime();a.now=function(){return(new Date).getTime()-c}}return a}(),s=function(){var a=Array.prototype.slice;try{a.call(c.documentElement)}catch(b){a=function(){for(var a=this.length,b=[];--a>0;)b[a]=this[a];return b}}return a}(),t={isNumber:function(a){return"number"==typeof a},isString:function(a){return"string"==typeof a},isArray:Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)},isFunction:function(a){return"[object Function]"===Object.prototype.toString.call(a)},isNode:function(a){return a&&a.nodeType},isWrapped:function(a){return a&&t.isNumber(a.length)&&!t.isString(a)&&!t.isFunction(a)&&!t.isNode(a)&&(0===a.length||t.isNode(a[0]))},isSVG:function(a){return b.SVGElement&&a instanceof b.SVGElement},isEmptyObject:function(a){for(var b in a)if(a.hasOwnProperty(b))return!1;return!0}},u=!1;if(a.fn&&a.fn.jquery?(o=a,u=!0):o=b.Velocity.Utilities,p<=8&&!u)throw new Error("Velocity: IE8 and below require jQuery to be loaded before Velocity.");if(p<=7)return void(jQuery.fn.velocity=jQuery.fn.animate);var v=400,w="swing",x={State:{isMobile:/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),isAndroid:/Android/i.test(navigator.userAgent),isGingerbread:/Android 2\.3\.[3-7]/i.test(navigator.userAgent),isChrome:b.chrome,isFirefox:/Firefox/i.test(navigator.userAgent),prefixElement:c.createElement("div"),prefixMatches:{},scrollAnchor:null,scrollPropertyLeft:null,scrollPropertyTop:null,isTicking:!1,calls:[],delayedElements:{count:0}},CSS:{},Utilities:o,Redirects:{},Easings:{},Promise:b.Promise,defaults:{queue:"",duration:v,easing:w,begin:d,complete:d,progress:d,display:d,visibility:d,loop:!1,delay:!1,mobileHA:!0,_cacheValues:!0,promiseRejectEmpty:!0},init:function(a){o.data(a,"velocity",{isSVG:t.isSVG(a),isAnimating:!1,computedStyle:null,tweensContainer:null,rootPropertyValueCache:{},transformCache:{}})},hook:null,mock:!1,version:{major:1,minor:4,patch:2},debug:!1,timestamp:!0,pauseAll:function(a){var b=(new Date).getTime();o.each(x.State.calls,function(b,c){if(c){if(a!==d&&(c[2].queue!==a||c[2].queue===!1))return!0;c[5]={resume:!1}}}),o.each(x.State.delayedElements,function(a,c){c&&h(c,b)})},resumeAll:function(a){var b=(new Date).getTime();o.each(x.State.calls,function(b,c){if(c){if(a!==d&&(c[2].queue!==a||c[2].queue===!1))return!0;c[5]&&(c[5].resume=!0)}}),o.each(x.State.delayedElements,function(a,c){c&&i(c,b)})}};b.pageYOffset!==d?(x.State.scrollAnchor=b,x.State.scrollPropertyLeft="pageXOffset",x.State.scrollPropertyTop="pageYOffset"):(x.State.scrollAnchor=c.documentElement||c.body.parentNode||c.body,x.State.scrollPropertyLeft="scrollLeft",x.State.scrollPropertyTop="scrollTop");var y=function(){function a(a){return-a.tension*a.x-a.friction*a.v}function b(b,c,d){var e={x:b.x+d.dx*c,v:b.v+d.dv*c,tension:b.tension,friction:b.friction};return{dx:e.v,dv:a(e)}}function c(c,d){var e={dx:c.v,dv:a(c)},f=b(c,.5*d,e),g=b(c,.5*d,f),h=b(c,d,g),i=1/6*(e.dx+2*(f.dx+g.dx)+h.dx),j=1/6*(e.dv+2*(f.dv+g.dv)+h.dv);return c.x=c.x+i*d,c.v=c.v+j*d,c}return function d(a,b,e){var f,g,h,i={x:-1,v:0,tension:null,friction:null},j=[0],k=0,l=1e-4,m=.016;for(a=parseFloat(a)||500,b=parseFloat(b)||20,e=e||null,i.tension=a,i.friction=b,f=null!==e,f?(k=d(a,b),g=k/e*m):g=m;;)if(h=c(h||i,g),j.push(1+h.x),k+=16,!(Math.abs(h.x)>l&&Math.abs(h.v)>l))break;return f?function(a){return j[a*(j.length-1)|0]}:k}}();x.Easings={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},spring:function(a){return 1-Math.cos(4.5*a*Math.PI)*Math.exp(6*-a)}},o.each([["ease",[.25,.1,.25,1]],["ease-in",[.42,0,1,1]],["ease-out",[0,0,.58,1]],["ease-in-out",[.42,0,.58,1]],["easeInSine",[.47,0,.745,.715]],["easeOutSine",[.39,.575,.565,1]],["easeInOutSine",[.445,.05,.55,.95]],["easeInQuad",[.55,.085,.68,.53]],["easeOutQuad",[.25,.46,.45,.94]],["easeInOutQuad",[.455,.03,.515,.955]],["easeInCubic",[.55,.055,.675,.19]],["easeOutCubic",[.215,.61,.355,1]],["easeInOutCubic",[.645,.045,.355,1]],["easeInQuart",[.895,.03,.685,.22]],["easeOutQuart",[.165,.84,.44,1]],["easeInOutQuart",[.77,0,.175,1]],["easeInQuint",[.755,.05,.855,.06]],["easeOutQuint",[.23,1,.32,1]],["easeInOutQuint",[.86,0,.07,1]],["easeInExpo",[.95,.05,.795,.035]],["easeOutExpo",[.19,1,.22,1]],["easeInOutExpo",[1,0,0,1]],["easeInCirc",[.6,.04,.98,.335]],["easeOutCirc",[.075,.82,.165,1]],["easeInOutCirc",[.785,.135,.15,.86]]],function(a,b){x.Easings[b[0]]=k.apply(null,b[1])});var z=x.CSS={RegEx:{isHex:/^#([A-f\d]{3}){1,2}$/i,valueUnwrap:/^[A-z]+\((.*)\)$/i,wrappedValueAlreadyExtracted:/[0-9.]+ [0-9.]+ [0-9.]+( [0-9.]+)?/,valueSplit:/([A-z]+\(.+\))|(([A-z0-9#-.]+?)(?=\s|$))/gi},Lists:{colors:["fill","stroke","stopColor","color","backgroundColor","borderColor","borderTopColor","borderRightColor","borderBottomColor","borderLeftColor","outlineColor"],transformsBase:["translateX","translateY","scale","scaleX","scaleY","skewX","skewY","rotateZ"],transforms3D:["transformPerspective","translateZ","scaleZ","rotateX","rotateY"],units:["%","em","ex","ch","rem","vw","vh","vmin","vmax","cm","mm","Q","in","pc","pt","px","deg","grad","rad","turn","s","ms"],colorNames:{aliceblue:"240,248,255",antiquewhite:"250,235,215",aquamarine:"127,255,212",aqua:"0,255,255",azure:"240,255,255",beige:"245,245,220",bisque:"255,228,196",black:"0,0,0",blanchedalmond:"255,235,205",blueviolet:"138,43,226",blue:"0,0,255",brown:"165,42,42",burlywood:"222,184,135",cadetblue:"95,158,160",chartreuse:"127,255,0",chocolate:"210,105,30",coral:"255,127,80",cornflowerblue:"100,149,237",cornsilk:"255,248,220",crimson:"220,20,60",cyan:"0,255,255",darkblue:"0,0,139",darkcyan:"0,139,139",darkgoldenrod:"184,134,11",darkgray:"169,169,169",darkgrey:"169,169,169",darkgreen:"0,100,0",darkkhaki:"189,183,107",darkmagenta:"139,0,139",darkolivegreen:"85,107,47",darkorange:"255,140,0",darkorchid:"153,50,204",darkred:"139,0,0",darksalmon:"233,150,122",darkseagreen:"143,188,143",darkslateblue:"72,61,139",darkslategray:"47,79,79",darkturquoise:"0,206,209",darkviolet:"148,0,211",deeppink:"255,20,147",deepskyblue:"0,191,255",dimgray:"105,105,105",dimgrey:"105,105,105",dodgerblue:"30,144,255",firebrick:"178,34,34",floralwhite:"255,250,240",forestgreen:"34,139,34",fuchsia:"255,0,255",gainsboro:"220,220,220",ghostwhite:"248,248,255",gold:"255,215,0",goldenrod:"218,165,32",gray:"128,128,128",grey:"128,128,128",greenyellow:"173,255,47",green:"0,128,0",honeydew:"240,255,240",hotpink:"255,105,180",indianred:"205,92,92",indigo:"75,0,130",ivory:"255,255,240",khaki:"240,230,140",lavenderblush:"255,240,245",lavender:"230,230,250",lawngreen:"124,252,0",lemonchiffon:"255,250,205",lightblue:"173,216,230",lightcoral:"240,128,128",lightcyan:"224,255,255",lightgoldenrodyellow:"250,250,210",lightgray:"211,211,211",lightgrey:"211,211,211",lightgreen:"144,238,144",lightpink:"255,182,193",lightsalmon:"255,160,122",lightseagreen:"32,178,170",lightskyblue:"135,206,250",lightslategray:"119,136,153",lightsteelblue:"176,196,222",lightyellow:"255,255,224",limegreen:"50,205,50",lime:"0,255,0",linen:"250,240,230",magenta:"255,0,255",maroon:"128,0,0",mediumaquamarine:"102,205,170",mediumblue:"0,0,205",mediumorchid:"186,85,211",mediumpurple:"147,112,219",mediumseagreen:"60,179,113",mediumslateblue:"123,104,238",mediumspringgreen:"0,250,154",mediumturquoise:"72,209,204",mediumvioletred:"199,21,133",midnightblue:"25,25,112",mintcream:"245,255,250",mistyrose:"255,228,225",moccasin:"255,228,181",navajowhite:"255,222,173",navy:"0,0,128",oldlace:"253,245,230",olivedrab:"107,142,35",olive:"128,128,0",orangered:"255,69,0",orange:"255,165,0",orchid:"218,112,214",palegoldenrod:"238,232,170",palegreen:"152,251,152",paleturquoise:"175,238,238",palevioletred:"219,112,147",papayawhip:"255,239,213",peachpuff:"255,218,185",peru:"205,133,63",pink:"255,192,203",plum:"221,160,221",powderblue:"176,224,230",purple:"128,0,128",red:"255,0,0",rosybrown:"188,143,143",royalblue:"65,105,225",saddlebrown:"139,69,19",salmon:"250,128,114",sandybrown:"244,164,96",seagreen:"46,139,87",seashell:"255,245,238",sienna:"160,82,45",silver:"192,192,192",skyblue:"135,206,235",slateblue:"106,90,205",slategray:"112,128,144",snow:"255,250,250",springgreen:"0,255,127",steelblue:"70,130,180",tan:"210,180,140",teal:"0,128,128",thistle:"216,191,216",tomato:"255,99,71",turquoise:"64,224,208",violet:"238,130,238",wheat:"245,222,179",whitesmoke:"245,245,245",white:"255,255,255",yellowgreen:"154,205,50",yellow:"255,255,0"}},Hooks:{templates:{textShadow:["Color X Y Blur","black 0px 0px 0px"],boxShadow:["Color X Y Blur Spread","black 0px 0px 0px 0px"],clip:["Top Right Bottom Left","0px 0px 0px 0px"],backgroundPosition:["X Y","0% 0%"],transformOrigin:["X Y Z","50% 50% 0px"],perspectiveOrigin:["X Y","50% 50%"]},registered:{},register:function(){for(var a=0;a=0?c:""},fixColors:function(a){return a.replace(/(rgba?\(\s*)?(\b[a-z]+\b)/g,function(a,b,c){return z.Lists.colorNames.hasOwnProperty(c)?(b?b:"rgba(")+z.Lists.colorNames[c]+(b?"":",1)"):b+c})},cleanRootPropertyValue:function(a,b){return z.RegEx.valueUnwrap.test(b)&&(b=b.match(z.RegEx.valueUnwrap)[1]),z.Values.isCSSNullValue(b)&&(b=z.Hooks.templates[a][1]),b},extractValue:function(a,b){var c=z.Hooks.registered[a];if(c){var d=c[0],e=c[1];return b=z.Hooks.cleanRootPropertyValue(d,b),b.toString().match(z.RegEx.valueSplit)[e]}return b},injectValue:function(a,b,c){var d=z.Hooks.registered[a];if(d){var e,f,g=d[0],h=d[1];return c=z.Hooks.cleanRootPropertyValue(g,c),e=c.toString().match(z.RegEx.valueSplit),e[h]=b,f=e.join(" ")}return c}},Normalizations:{registered:{clip:function(a,b,c){switch(a){case"name":return"clip";case"extract":var d;return z.RegEx.wrappedValueAlreadyExtracted.test(c)?d=c:(d=c.toString().match(z.RegEx.valueUnwrap),d=d?d[1].replace(/,(\s+)?/g," "):c),d;case"inject":return"rect("+c+")"}},blur:function(a,b,c){switch(a){case"name":return x.State.isFirefox?"filter":"-webkit-filter";case"extract":var d=parseFloat(c);if(!d&&0!==d){var e=c.toString().match(/blur\(([0-9]+[A-z]+)\)/i);d=e?e[1]:0}return d;case"inject":return parseFloat(c)?"blur("+c+")":"none"}},opacity:function(a,b,c){if(p<=8)switch(a){case"name":return"filter";case"extract":var d=c.toString().match(/alpha\(opacity=(.*)\)/i);return c=d?d[1]/100:1;case"inject":return b.style.zoom=1,parseFloat(c)>=1?"":"alpha(opacity="+parseInt(100*parseFloat(c),10)+")"}else switch(a){case"name":return"opacity";case"extract":return c;case"inject":return c}}},register:function(){function a(a,b,c){var d="border-box"===z.getPropertyValue(b,"boxSizing").toString().toLowerCase();if(d===(c||!1)){var e,f,g=0,h="width"===a?["Left","Right"]:["Top","Bottom"],i=["padding"+h[0],"padding"+h[1],"border"+h[0]+"Width","border"+h[1]+"Width"];for(e=0;e9)||x.State.isGingerbread||(z.Lists.transformsBase=z.Lists.transformsBase.concat(z.Lists.transforms3D));for(var c=0;c8)&&3===f.split(" ").length&&(f+=" 1"),f;case"inject":return/^rgb/.test(e)?e:(p<=8?4===e.split(" ").length&&(e=e.split(/\s+/).slice(0,3).join(" ")):3===e.split(" ").length&&(e+=" 1"),(p<=8?"rgb":"rgba")+"("+e.replace(/\s+/g,",").replace(/\.(\d)+(?=,)/g,"")+")")}}}();z.Normalizations.registered.innerWidth=b("width",!0),z.Normalizations.registered.innerHeight=b("height",!0),z.Normalizations.registered.outerWidth=b("width"),z.Normalizations.registered.outerHeight=b("height")}},Names:{camelCase:function(a){return a.replace(/-(\w)/g,function(a,b){return b.toUpperCase()})},SVGAttribute:function(a){var b="width|height|x|y|cx|cy|r|rx|ry|x1|x2|y1|y2";return(p||x.State.isAndroid&&!x.State.isChrome)&&(b+="|transform"),new RegExp("^("+b+")$","i").test(a)},prefixCheck:function(a){if(x.State.prefixMatches[a])return[x.State.prefixMatches[a],!0];for(var b=["","Webkit","Moz","ms","O"],c=0,d=b.length;c=2&&console.log("Get "+c+": "+i),i},setPropertyValue:function(a,c,d,e,f){var h=c;if("scroll"===c)f.container?f.container["scroll"+f.direction]=d:"Left"===f.direction?b.scrollTo(d,f.alternateValue):b.scrollTo(f.alternateValue,d);else if(z.Normalizations.registered[c]&&"transform"===z.Normalizations.registered[c]("name",a))z.Normalizations.registered[c]("inject",a,d),h="transform",d=g(a).transformCache[c];else{if(z.Hooks.registered[c]){var i=c,j=z.Hooks.getRoot(c);e=e||z.getPropertyValue(a,j),d=z.Hooks.injectValue(i,d,e),c=j}if(z.Normalizations.registered[c]&&(d=z.Normalizations.registered[c]("inject",a,d),c=z.Normalizations.registered[c]("name",a)),h=z.Names.prefixCheck(c)[0],p<=8)try{a.style[h]=d}catch(k){x.debug&&console.log("Browser does not support ["+d+"] for ["+h+"]")}else{var l=g(a);l&&l.isSVG&&z.Names.SVGAttribute(c)?a.setAttribute(c,d):a.style[h]=d}x.debug>=2&&console.log("Set "+c+" ("+h+"): "+d)}return[h,d]},flushTransformCache:function(a){var b="",c=g(a);if((p||x.State.isAndroid&&!x.State.isChrome)&&c&&c.isSVG){var d=function(b){return parseFloat(z.getPropertyValue(a,b))},e={translate:[d("translateX"),d("translateY")],skewX:[d("skewX")],skewY:[d("skewY")],scale:1!==d("scale")?[d("scale"),d("scale")]:[d("scaleX"),d("scaleY")],rotate:[d("rotateZ"),0,0]};o.each(g(a).transformCache,function(a){/^translate/i.test(a)?a="translate":/^scale/i.test(a)?a="scale":/^rotate/i.test(a)&&(a="rotate"),e[a]&&(b+=a+"("+e[a].join(" ")+") ",delete e[a])})}else{var f,h;o.each(g(a).transformCache,function(c){return f=g(a).transformCache[c],"transformPerspective"===c?(h=f,!0):(9===p&&"rotateZ"===c&&(c="rotate"),void(b+=c+f+" "))}),h&&(b="perspective"+h+" "+b)}z.setPropertyValue(a,"transform",b)}};z.Hooks.register(),z.Normalizations.register(),x.hook=function(a,b,c){var e;return a=f(a),o.each(a,function(a,f){if(g(f)===d&&x.init(f),c===d)e===d&&(e=z.getPropertyValue(f,b));else{var h=z.setPropertyValue(f,b,c);"transform"===h[0]&&x.CSS.flushTransformCache(f),e=h}}),e};var A=function(){function a(){return k?y.promise||null:p}function e(a,e){function f(f){var k,n;if(i.begin&&0===C)try{i.begin.call(r,r)}catch(p){setTimeout(function(){throw p},1)}if("scroll"===F){var q,v,w,A=/^x$/i.test(i.axis)?"Left":"Top",D=parseFloat(i.offset)||0;i.container?t.isWrapped(i.container)||t.isNode(i.container)?(i.container=i.container[0]||i.container,q=i.container["scroll"+A],w=q+o(a).position()[A.toLowerCase()]+D):i.container=null:(q=x.State.scrollAnchor[x.State["scrollProperty"+A]],v=x.State.scrollAnchor[x.State["scrollProperty"+("Left"===A?"Top":"Left")]],w=o(a).offset()[A.toLowerCase()]+D),j={scroll:{rootPropertyValue:!1,startValue:q,currentValue:q,endValue:w,unitType:"",easing:i.easing,scrollData:{container:i.container,direction:A,alternateValue:v}},element:a},x.debug&&console.log("tweensContainer (scroll): ",j.scroll,a)}else if("reverse"===F){if(k=g(a),!k)return;if(!k.tweensContainer)return void o.dequeue(a,i.queue);"none"===k.opts.display&&(k.opts.display="auto"),"hidden"===k.opts.visibility&&(k.opts.visibility="visible"),k.opts.loop=!1,k.opts.begin=null,k.opts.complete=null,u.easing||delete i.easing,u.duration||delete i.duration,i=o.extend({},k.opts,i),n=o.extend(!0,{},k?k.tweensContainer:null);for(var E in n)if(n.hasOwnProperty(E)&&"element"!==E){var G=n[E].startValue;n[E].startValue=n[E].currentValue=n[E].endValue,n[E].endValue=G,t.isEmptyObject(u)||(n[E].easing=i.easing),x.debug&&console.log("reverse tweensContainer ("+E+"): "+JSON.stringify(n[E]),a)}j=n}else if("start"===F){k=g(a),k&&k.tweensContainer&&k.isAnimating===!0&&(n=k.tweensContainer);var H=function(b,c){var d,f,g;return t.isFunction(b)&&(b=b.call(a,e,B)),t.isArray(b)?(d=b[0],!t.isArray(b[1])&&/^[\d-]/.test(b[1])||t.isFunction(b[1])||z.RegEx.isHex.test(b[1])?g=b[1]:t.isString(b[1])&&!z.RegEx.isHex.test(b[1])&&x.Easings[b[1]]||t.isArray(b[1])?(f=c?b[1]:l(b[1],i.duration),g=b[2]):g=b[1]||b[2]):d=b,c||(f=f||i.easing),t.isFunction(d)&&(d=d.call(a,e,B)),t.isFunction(g)&&(g=g.call(a,e,B)),[d||0,f,g]},I=function(e,f){var g,l=z.Hooks.getRoot(e),m=!1,p=f[0],q=f[1],r=f[2];
4 | if(!(k&&k.isSVG||"tween"===l||z.Names.prefixCheck(l)[1]!==!1||z.Normalizations.registered[l]!==d))return void(x.debug&&console.log("Skipping ["+l+"] due to a lack of browser support."));(i.display!==d&&null!==i.display&&"none"!==i.display||i.visibility!==d&&"hidden"!==i.visibility)&&/opacity|filter/.test(e)&&!r&&0!==p&&(r=0),i._cacheValues&&n&&n[e]?(r===d&&(r=n[e].endValue+n[e].unitType),m=k.rootPropertyValueCache[l]):z.Hooks.registered[e]?r===d?(m=z.getPropertyValue(a,l),r=z.getPropertyValue(a,e,m)):m=z.Hooks.templates[l][1]:r===d&&(r=z.getPropertyValue(a,e));var s,u,v,w=!1,y=function(a,b){var c,d;return d=(b||"0").toString().toLowerCase().replace(/[%A-z]+$/,function(a){return c=a,""}),c||(c=z.Values.getUnitType(a)),[d,c]};if(r!==p&&t.isString(r)&&t.isString(p)){g="";var A=0,B=0,C=[],D=[],E=0,F=0,G=0;for(r=z.Hooks.fixColors(r),p=z.Hooks.fixColors(p);A=4&&"("===H?E++:(E&&E<5||E>=4&&")"===H&&--E<5)&&(E=0),0===F&&"r"===H||1===F&&"g"===H||2===F&&"b"===H||3===F&&"a"===H||F>=3&&"("===H?(3===F&&"a"===H&&(G=1),F++):G&&","===H?++G>3&&(F=G=0):(G&&F<(G?5:4)||F>=(G?4:3)&&")"===H&&--F<(G?5:4))&&(F=G=0)}}A===r.length&&B===p.length||(x.debug&&console.error('Trying to pattern match mis-matched strings ["'+p+'", "'+r+'"]'),g=d),g&&(C.length?(x.debug&&console.log('Pattern found "'+g+'" -> ',C,D,"["+r+","+p+"]"),r=C,p=D,u=v=""):g=d)}g||(s=y(e,r),r=s[0],v=s[1],s=y(e,p),p=s[0].replace(/^([+-\/*])=/,function(a,b){return w=b,""}),u=s[1],r=parseFloat(r)||0,p=parseFloat(p)||0,"%"===u&&(/^(fontSize|lineHeight)$/.test(e)?(p/=100,u="em"):/^scale/.test(e)?(p/=100,u=""):/(Red|Green|Blue)$/i.test(e)&&(p=p/100*255,u="")));var S=function(){var d={myParent:a.parentNode||c.body,position:z.getPropertyValue(a,"position"),fontSize:z.getPropertyValue(a,"fontSize")},e=d.position===L.lastPosition&&d.myParent===L.lastParent,f=d.fontSize===L.lastFontSize;L.lastParent=d.myParent,L.lastPosition=d.position,L.lastFontSize=d.fontSize;var g=100,h={};if(f&&e)h.emToPx=L.lastEmToPx,h.percentToPxWidth=L.lastPercentToPxWidth,h.percentToPxHeight=L.lastPercentToPxHeight;else{var i=k&&k.isSVG?c.createElementNS("http://www.w3.org/2000/svg","rect"):c.createElement("div");x.init(i),d.myParent.appendChild(i),o.each(["overflow","overflowX","overflowY"],function(a,b){x.CSS.setPropertyValue(i,b,"hidden")}),x.CSS.setPropertyValue(i,"position",d.position),x.CSS.setPropertyValue(i,"fontSize",d.fontSize),x.CSS.setPropertyValue(i,"boxSizing","content-box"),o.each(["minWidth","maxWidth","width","minHeight","maxHeight","height"],function(a,b){x.CSS.setPropertyValue(i,b,g+"%")}),x.CSS.setPropertyValue(i,"paddingLeft",g+"em"),h.percentToPxWidth=L.lastPercentToPxWidth=(parseFloat(z.getPropertyValue(i,"width",null,!0))||1)/g,h.percentToPxHeight=L.lastPercentToPxHeight=(parseFloat(z.getPropertyValue(i,"height",null,!0))||1)/g,h.emToPx=L.lastEmToPx=(parseFloat(z.getPropertyValue(i,"paddingLeft"))||1)/g,d.myParent.removeChild(i)}return null===L.remToPx&&(L.remToPx=parseFloat(z.getPropertyValue(c.body,"fontSize"))||16),null===L.vwToPx&&(L.vwToPx=parseFloat(b.innerWidth)/100,L.vhToPx=parseFloat(b.innerHeight)/100),h.remToPx=L.remToPx,h.vwToPx=L.vwToPx,h.vhToPx=L.vhToPx,x.debug>=1&&console.log("Unit ratios: "+JSON.stringify(h),a),h};if(/[\/*]/.test(w))u=v;else if(v!==u&&0!==r)if(0===p)u=v;else{h=h||S();var T=/margin|padding|left|right|width|text|word|letter/i.test(e)||/X$/.test(e)||"x"===e?"x":"y";switch(v){case"%":r*="x"===T?h.percentToPxWidth:h.percentToPxHeight;break;case"px":break;default:r*=h[v+"ToPx"]}switch(u){case"%":r*=1/("x"===T?h.percentToPxWidth:h.percentToPxHeight);break;case"px":break;default:r*=1/h[u+"ToPx"]}}switch(w){case"+":p=r+p;break;case"-":p=r-p;break;case"*":p*=r;break;case"/":p=r/p}j[e]={rootPropertyValue:m,startValue:r,currentValue:r,endValue:p,unitType:u,easing:q},g&&(j[e].pattern=g),x.debug&&console.log("tweensContainer ("+e+"): "+JSON.stringify(j[e]),a)};for(var J in s)if(s.hasOwnProperty(J)){var K=z.Names.camelCase(J),N=H(s[J]);if(z.Lists.colors.indexOf(K)>=0){var O=N[0],P=N[1],Q=N[2];if(z.RegEx.isHex.test(O)){for(var R=["Red","Green","Blue"],S=z.Values.hexToRgb(O),T=Q?z.Values.hexToRgb(Q):d,U=0;U