├── .gitignore
├── package.json
├── LICENSE
├── README.md
├── index.html
├── css3-animation.js
└── src
└── j2ckf.js
/.gitignore:
--------------------------------------------------------------------------------
1 | *.un~
2 | /.DS_Store
3 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "js2css3",
3 | "version": "1.0.2",
4 | "description": "Create CSS3 keyframes animation from javascript. 使用JS动态创建管理CSS3 Animations。",
5 | "main": "src/j2ckf.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://qiqiboy@github.com/qiqiboy/JS2CSSKeyframes.git"
12 | },
13 | "author": "",
14 | "license": "ISC",
15 | "bugs": {
16 | "url": "https://github.com/qiqiboy/JS2CSSKeyframes/issues"
17 | },
18 | "homepage": "https://github.com/qiqiboy/JS2CSSKeyframes#readme"
19 | }
20 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 qiqiboy
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JS2CSSKeyframes
2 | Create CSS3 keyframes animation from javascript.
3 | 使用JS动态创建管理CSS3 Animations。
4 |
5 | ## 为何使用
6 | 随着支持HTML5的普及,页面中使用各种动画的地方越来越多,尤其是一个小型的动画,使用css3来做无疑是既方便又快捷的一个选择。
7 |
8 | 但是由于浏览器间差异(主要是 webkit 和 moz),我们目前使用css3动画还必须写上各种浏览器前缀,这样无疑给开发工作增添许多麻烦,添加 修改都是无尽的麻烦。在css中预先定义各种动画,每个都要写三遍,无疑又臭又长。
9 |
10 | JS2CSSKeyframes就是解决这个问题的,它可以自动根据不同浏览器生成其支持的css3动画@keyframes,省去各种前缀。动画中的css3属性也会自动加入前缀,无需单独书写。
11 |
12 | 使用 JS2CSSKeyframes 来代替在css中定义css3动画,添加、修改、删除都会变得轻松无比。而且还可以定义复杂无比的css3动画,这是用css无法想象的!!
13 |
14 | 例如:new JS2CSSKeyframes("test", {from:{transform:'translate(0,0);'},to:{transform:'translate(100px,100px);'}});
15 |
16 | 在不同浏览器下将会生成以下三种之一
17 | * @-webkit-keyframes test { from { -webkit-transform:translate(0,0) } to { -webkit-transform:translate(100px,100px) } }
18 | * @-moz-keyframes test { from { -moz-transform:translate(0,0) } to { -moz-transform:translate(100px,100px) } }
19 | * @keyframes test { from { transform:translate(0,0) } to { transform:translate(100px,100px) } }
20 |
21 | ## 安装
22 | ```
23 | npm install js2css3 --save
24 | ```
25 |
26 | ## 开始使用
27 |
28 | 有两种情况:
29 | #### 使用JS2CSSKeyframes创建自定义动画
30 | ```js
31 | import JS2CSSKeyframes from 'js2css3';
32 |
33 | new JS2CSSKeyframes(...);
34 | ```
35 |
36 | #### 使用自带的动画插件
37 | ```js
38 | import 'js2css3/css3-animation';
39 | ```
40 |
41 | ## 使用示例
42 | ```javascript
43 |
44 | /* @description 创建css3动画
45 | * @Class JS2CSSKeyframes
46 | * @param String name 名称,可省略该参数,name将随机生成
47 | * @param String|Object|Array config 动画帧设定
48 | *
49 | * @example-1 new JS2CSSKeyframes("test", {
50 | * "from":"width:300px;height:100px;",
51 | * "50%":"width:30px;height:10px;",
52 | * "to":{width:"300px",height:"10px"}
53 | * });
54 | *
55 | * @example-2 new JS2CSSKeyframes("test1", ["width:300px;height:100px;","width:30px;height:10px;"]);
56 | *
57 | * @example-3 new JS2CSSKeyframes("test2", "from {width:300px;height:100px;} to {width:300px;height:100px;}");
58 | *
59 | * 注:也可以使用 JS2CSSKeyframes.add(name,config) 来创建动画
60 | */
61 |
62 | //用法一
63 | ani=new JS2CSSKeyframes('ani_1',{
64 | '0%':{width:0,height:0},
65 | '50%':{width:'100px',height:'100%'},
66 | '100%':{width:0,height:0}
67 | });
68 |
69 | //用法二
70 | ani=new JS2CSSKeyframes('ani_1',{
71 | '0%':'width:0;height:0',
72 | '50%':'width:100px;height:100%;',
73 | '100%':'width:0;height:0;'
74 | });
75 |
76 | //用法三
77 | //均等比例帧可以使用数组省略比例
78 | ani=new JS2CSSKeyframes('ani_1',[
79 | 'width:0;height:0', //0%
80 | 'width:100px;height:100%;', //50%
81 | 'width:0;height:0;' //100%
82 | ]);
83 |
84 | //用法四
85 | //可以省略名称,name将随机生成,可以通过 .name 来查看生成的名称
86 | ani=new JS2CSSKeyframes([
87 | 'width:0;height:0', //0%
88 | 'width:100px;height:100%;', //50%
89 | 'width:0;height:0;' //100%
90 | ]);
91 | console.log(nai.name); //输出 css3Ani_999997712
92 |
93 |
94 |
95 | //JS2CSSKeyframes实例对象的其它属性及方法说明
96 | //@prop String name 动画名称
97 | console.log(ani.name);
98 |
99 | //@prop String cssText 动画内容
100 | console.log(ani.cssText);
101 |
102 | //@prop CSSRuleList cssRules 动画帧信息集合
103 | console.dir(ani.cssRules);
104 |
105 | //@prop Object keyframes 动画帧对象,键值为比例百分比,0% 50% 等
106 | console.dir(ani.keyframes);
107 |
108 | //@method get(key) 获取指定进度的CSSKeyframeRule帧
109 | ani.get('50%')
110 |
111 | //@method add(key,value) 增加进度为key,样式为value的帧
112 | ani.add('70%',{opacity:.5,height:'100px'})
113 |
114 | //@method remove(key) 删除进度为key的帧
115 | ani.remove('50%');
116 |
117 | //@method clear() 删除所有的帧
118 | ani.clear();
119 |
120 |
121 |
122 | JS2CSSKeyframes.CSSKeyframes //Object 获取页面上所有的css3动画
123 |
124 | //JS2CSSKeyframes.get(name) //获取页面上名为name的动画
125 | JS2CSSKeyframes.get('ani_1');
126 |
127 | //JS2CSSKeyframes.remove(name) //删除页面上名为name的动画
128 | JS2CSSKeyframes.remove'ani_1');
129 |
130 | //JS2CSSKeyframes.add(name,config) //增加动画,同 new JS2CSSKeyframes(name,config);
131 | JS2CSSKeyframes.add("test", {
132 | "from":"width:300px;height:100px;",
133 | "50%":"width:30px;height:10px;",
134 | "to":{width:"300px",height:"10px"}
135 | });
136 |
137 | JS2CSSKeyframes.vendor //String 当前浏览器前缀 -webkit -moz- 或空字符串
138 |
139 | JS2CSSKeyframes.support //Boolean 是否支持css动画,不支持css3动画的浏览器中调用JS2CSSKeyframes其它方法将会报错
140 |
141 | JS2CSSKeyframes["animation-css"] //-webkit-animation
142 | JS2CSSKeyframes["animation"] //webkitAnimation
143 |
144 | JS2CSSKeyframes["animation-timing-function"] //-webkit-animation-timing-function
145 | JS2CSSKeyframes["animation-timing-function"] //webkitAnimationTimingFunction
146 |
147 | ...... //同理支持其他animation的子属性
148 |
149 | ````
150 |
151 | ## css3-animation.js
152 | 基于JS2CSSKeyframes生成常用css3动画类
153 | 具体见css3-animation.js内容
154 | 将css3-animation.js引入页面中,即可在css中使用 flyTop flyLeft fadeIn scaleIn 等简单动画类
155 |
156 | ## DEMO
157 | http://u.boy.im/css3keyframes
158 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | JS2CSSKeyframes
6 |
7 |
8 |
57 |
58 |
59 |
60 |
61 |
62 |
JS2CSSKeyframes
63 |
64 |
示例一 普通用法,js生成动画,css通过animation属性调用
65 |
66 |
67 | 代码部分:
68 | css: #dot1{
69 | -webkit-animation:fly 1s linear alternate infinite;
70 | -moz-animation:fly 1s linear alternate infinite;
71 | animation:fly 1s linear alternate infinite;
72 | }
73 |
74 | js: new JS2CSSKeyframes('fly',{
75 | from:'transform:translate(0,0)',
76 | to:'transform:translate(100px,0)'
77 | });
78 |
79 | 等同于 new JS2CSSKeyframes('fly',['transform:translate(0,0)','transform:translate(100px,0)']);
80 |
81 |
82 |
示例二 通过js添加匿名动画
83 |
84 |
85 | 代码部分:document.querySelector('#dot1').style.cssText=JS2CSSKeyframes.vendor+'animation:'+JS2CSSKeyframes([
86 | 'transform:translate(0,0)',
87 | 'transform:translate(100px,0)'
88 | ]).name+' 1s linear alternate infinite';
89 |
90 | 或者:document.querySelector('#dot1').style[JS2CSSKeyframes.animation]=JS2CSSKeyframes([
91 | 'transform:translate(0,0)',
92 | 'transform:translate(100px,0)'
93 | ]).name+' 1s linear alternate infinite';
94 |
95 |
96 |
示例三 通过JS2CSSKeyframes生成复杂多帧复杂动画
97 |
98 |
99 | 代码部分:var kfs=[],num=100,rand=function(f,t){return Math.random()*t+f}
100 | while(num){
101 | kfs.push({transform:'scale('+rand(0,2)+')',background:'rgba('+parseInt(255*Math.random())+','+parseInt(255*Math.random())+','+parseInt(255*Math.random())+','+Math.random()+')'});
102 | num--;
103 | }
104 | document.querySelector('#dot3').style[JS2CSSKeyframes.animation]=JS2CSSKeyframes(kfs).name+' 60s linear alternate infinite';
105 |
106 |
107 |
108 |
109 |
137 |
138 |
139 |
--------------------------------------------------------------------------------
/css3-animation.js:
--------------------------------------------------------------------------------
1 | /* 定义常用css3动画css类
2 | * @author qiqiboy
3 | * @github https://github.com/qiqiboy/JS2CSSKeyframes
4 | */
5 | (function(ROOT, struct, undefined) {
6 | if (typeof window === 'undefined') return;
7 |
8 | if (typeof module == 'object') {
9 | module.exports = struct(require('./src/j2ckf'));
10 | } else if (typeof define == 'function' && define.amd) {
11 | require(['./src/j2ckf'], function(js2ck) {
12 | if (js2ck.support) {
13 | return struct(js2ck);
14 | }
15 | });
16 | } else if (JS2CSSKeyframes.support) {
17 | ROOT.css3Ani = struct(JS2CSSKeyframes);
18 | }
19 | })(typeof window === 'undefined' ? global : window, function(JS2KF) {
20 | var styleSheet = JS2KF.getSheet();
21 | var css3Ani = {};
22 | var CONFIG = {
23 | bounce: {
24 | '20,50,80,100': 'transform:translate(0)',
25 | '40': 'transform:translateY(-30px)',
26 | '60': 'transform:translateY(-15px)'
27 | },
28 | bounceIn: {
29 | '0': 'transform:scale(0.3);opacity:0',
30 | '50': 'transform:scale(1.05);opacity:1',
31 | '75': 'transform:scale(0.9)',
32 | '100': 'transform:scale(1)'
33 | },
34 | bounceOut: {
35 | '100': 'transform:scale(0.3);opacity:0',
36 | '50': 'transform:scale(1.1);opacity:1',
37 | '25': 'transform:scale(0.96)'
38 | },
39 | flash: {
40 | '50,100': 'opacity:1;',
41 | '25,75': 'opacity:0;'
42 | },
43 | shake: {
44 | '10,30,50,70,90': 'transform:translateX(-10px)',
45 | '20,40,60,80': 'transform:translateX(10px)',
46 | '100': 'transform:translateX(0)'
47 | },
48 | rubberBand: {
49 | '30': 'transform:scale(1.25,0.75)',
50 | '40': 'transform:scale(0.75,1.25)',
51 | '50': 'transform:scale(1.15,0.85)',
52 | '65': 'transform:scale(.95,1.05)',
53 | '75': 'transform:scale(1.05,.95)',
54 | '100': 'transform:scale(1,1)'
55 | },
56 | tada: {
57 | '10,20': 'transform:scale(.9) rotate(-3deg)',
58 | '30,50,70,90': 'transform:scale(1.1) rotate(3deg)',
59 | '40,60,80': 'transform:scale(1.1) rotate(-3deg)',
60 | '100': 'transform:scale(1) rotate(0)'
61 | },
62 | hinge: {
63 | '0': 'transform-origin:0 0',
64 | '20,60': 'transform-origin:0 0;transform:rotate(30deg)',
65 | '40,80': 'transform-origin:0 0;transform:rotate(60deg);opacity:1',
66 | '100': 'transform-origin:0 0;transform:translateY(200%);opacity:0'
67 | },
68 | pulse: {
69 | '50': 'transform:scale(1.05)',
70 | '100': 'transform:scale(1)'
71 | },
72 | wiggle: [
73 | 'transform:skewX(-10deg)',
74 | 'transform:skewX(9deg)',
75 | 'transform:skewX(-8deg)',
76 | 'transform:skewX(7deg)',
77 | 'transform:skewX(-6deg)',
78 | 'transform:skewX(5deg)',
79 | 'transform:skewX(-4deg)',
80 | 'transform:skewX(3deg)',
81 | 'transform:skewX(-2deg)',
82 | 'transform:skewX(1deg)',
83 | 'transform:skewX(0)'
84 | ],
85 | swing: [
86 | '',
87 | 'transform:rotate(15deg)',
88 | 'transform:rotate(-10deg)',
89 | 'transform:rotate(5deg)',
90 | 'transform:rotate(-5deg)',
91 | 'transform:rotate(0)'
92 | ],
93 | wobble: [
94 | '',
95 | 'transform:translateX(-100px) rotate(-5deg)',
96 | 'transform:translateX(80px) rotate(3deg)',
97 | 'transform:translateX(-65px) rotate(-3deg)',
98 | 'transform:translateX(40px) rotate(2deg)',
99 | 'transform:translateX(-20px) rotate(-1deg)',
100 | 'transform:translateX(0) rotate(0)'
101 | ],
102 | ring: {
103 | '10,20': 'transform:scale(0.9) rotate(-3deg)',
104 | '30,50,70,90': 'transform:scale(1.1) rotate(3deg)',
105 | '40,60,80': 'transform:scale(1.1) rotate(-3deg)',
106 | '100': 'transform:scale(1) rotate(0)'
107 | },
108 | rotate360: ['', 'transform:rotate(360deg)']
109 | };
110 |
111 | 'X Y'.split(' ').forEach(function(prop) {
112 | var dir = prop == 'X' ? 'Y' : 'X';
113 |
114 | CONFIG['flip' + prop] = {
115 | '50': 'transform:perspective(400px) translateZ(150px) rotate' + dir + '(170deg)',
116 | '60': 'transform:perspective(400px) translateZ(150px) rotate' + dir + '(190deg)',
117 | '100': 'transform:perspective(400px) rotate' + dir + '(360deg)'
118 | };
119 |
120 | CONFIG['flipin' + prop] = {
121 | '0': 'transform:perspective(400px) rotate' + prop + '(90deg);opacity:0',
122 | '40': 'transform:perspective(400px) rotate' + prop + '(-10deg);opacity:1',
123 | '70': 'transform:perspective(400px) rotate' + prop + '(10deg)',
124 | '100': 'transform:perspective(400px) rotate' + prop + '(0)'
125 | };
126 |
127 | CONFIG['flipout' + prop] = {
128 | '50': 'opacity:1',
129 | '100': 'transform:perspective(400px) rotate' + prop + '(90deg);opacity:0'
130 | };
131 | });
132 |
133 | 'In Out '.split(' ').forEach(function(prop, i) {
134 | var call = i % 2 ? 'reverse' : 'slice',
135 | out = prop ? 'opacity:0;' : '';
136 |
137 | prop && (CONFIG['fade' + prop] = ['opacity:0', ''][call]());
138 |
139 | CONFIG['roll' + prop] = [
140 | out + 'transform:translateX(-100px) rotate(-120deg)',
141 | 'transform:translateX(0) rotate(0)'
142 | ][call]();
143 |
144 | CONFIG['scale' + prop] = [out + 'transform:scale(0)', 'transform:scale(1)'][call]();
145 | CONFIG['zoom' + prop] = [out + 'transform:scale(2)', 'transform:scale(1)'][call]();
146 | CONFIG['flyTop' + prop] = [out + 'transform:translateY(-50px)', 'transform:translateY(0)'][call]();
147 | CONFIG['flyRight' + prop] = [out + 'transform:translateX(50px)', 'transform:translateX(0)'][call]();
148 | CONFIG['flyBottom' + prop] = [out + 'transform:translateY(50px)', 'transform:translateY(0)'][call]();
149 | CONFIG['flyLeft' + prop] = [out + 'transform:translateX(-50px)', 'transform:translateX(0)'][call]();
150 | CONFIG['rotate' + prop] = [out + 'transform:rotate(-200deg)', 'transform:rotate(0)'][call]();
151 |
152 | CONFIG['lightSpeed' + prop] = [
153 | out + 'transform:translateX(100%) skewX(-30deg)',
154 | 'transform:translateX(0) skewX(0)'
155 | ][call]();
156 |
157 | CONFIG['slideX' + prop] = [out + 'width:0;overflow:hidden', 'overflow:hidden'][call]();
158 | CONFIG['slideY' + prop] = [out + 'height:0;overflow:hidden', 'overflow:hidden'][call]();
159 | });
160 |
161 | Object.keys(CONFIG).forEach(function(name) {
162 | css3Ani[name] = JS2KF(name, CONFIG[name]);
163 |
164 | //生成1s ease曲线执行的css类,如 .a-flyTopIn { -webkit-animation: flyTopIn 1s ease }
165 | styleSheet.insertRule(
166 | '.a-' + name + ' { ' + JS2KF['animation-css'] + ': ' + name + ' 1s ease both }',
167 | styleSheet.cssRules.length
168 | );
169 | });
170 |
171 | var delay = 100;
172 |
173 | while (delay < 10000) {
174 | styleSheet.insertRule(
175 | '.delay' +
176 | delay +
177 | '{ ' +
178 | JS2KF['animation-delay'] +
179 | ': ' +
180 | delay +
181 | 'ms !important; ' +
182 | JS2KF['animation-fill-mode'] +
183 | ': both !important }',
184 | styleSheet.cssRules.length
185 | );
186 |
187 | delay += delay < 3000 ? 100 : 1000;
188 | }
189 |
190 | return css3Ani;
191 | });
192 |
--------------------------------------------------------------------------------
/src/j2ckf.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @author qiqiboy
3 | * @github https://github.com/qiqiboy/JS2CSSKeyframes
4 | */
5 | (function(ROOT, struct, undefined) {
6 | if (typeof exports === 'object') {
7 | module.exports = struct;
8 | } else if (typeof define == 'function' && define.amd) {
9 | define('JS2CSSKeyframes', function() {
10 | return struct;
11 | });
12 | } else ROOT.JS2CSSKeyframes = struct;
13 |
14 | if (typeof window === 'undefined') {
15 | return;
16 | }
17 |
18 | var doc = document,
19 | slice = [].slice,
20 | CSSRule = ROOT.CSSRule || {},
21 | IMPORT_RULE = CSSRule.IMPORT_RULE,
22 | MEDIA_RULE = CSSRule.MEDIA_RULE,
23 | KEYFRAMES_RULE =
24 | CSSRule.KEYFRAMES_RULE ||
25 | CSSRule.WEBKIT_KEYFRAMES_RULE ||
26 | CSSRule.MOZ_KEYFRAMES_RULE ||
27 | CSSRule.O_KEYFRAMES_RULE,
28 | divstyle = document.documentElement.style,
29 | cssVendor = (function() {
30 | var tests = '-webkit- -moz- -o- -ms-'.split(' '),
31 | prop;
32 |
33 | while ((prop = tests.shift())) {
34 | if (camelCase(prop + 'animation') in divstyle) {
35 | return prop;
36 | }
37 | }
38 |
39 | return '';
40 | })(),
41 | KEY_REG = new RegExp('@(?:' + cssVendor + ')?keyframes', 'i'),
42 | animation = fixCSS3('animation'),
43 | keyframes = '@' + animation.replace('animation', 'keyframes');
44 |
45 | function getSheet() {
46 | //获取可以用的样式以用来插入css3 keyframes
47 | var n = 0,
48 | sheet,
49 | style;
50 |
51 | while ((sheet = doc.styleSheets.item(n++))) {
52 | try {
53 | if (sheet.cssRules) {
54 | return sheet;
55 | }
56 | } catch (e) {}
57 | }
58 |
59 | style = doc.createElement('style');
60 | style.type = 'text/css';
61 | doc.getElementsByTagName('head')[0].appendChild(style);
62 |
63 | return style.sheet;
64 | }
65 |
66 | function camelCase(str) {
67 | return (str + '').replace(/^-ms-/, 'ms-').replace(/-([a-z]|[0-9])/gi, function(all, letter) {
68 | return (letter + '').toUpperCase();
69 | });
70 | }
71 |
72 | function iterateSheet(callback) {
73 | slice.call(doc.styleSheets).forEach(function getRule(sheet) {
74 | slice.call(sheet.cssRules).forEach(function(rule, i) {
75 | if (rule.type == IMPORT_RULE) {
76 | //imported css
77 | getRule(rule.styleSheet || rule.sheet);
78 | } else if (rule.type == MEDIA_RULE) {
79 | getRule(rule);
80 | } else if (rule.type == KEYFRAMES_RULE) {
81 | callback(rule, i, sheet);
82 | }
83 | });
84 | });
85 | }
86 |
87 | function fixCSS3(name) {
88 | var prop = camelCase(name),
89 | _prop = camelCase(cssVendor + prop);
90 |
91 | return (prop in divstyle && name) || (_prop in divstyle && cssVendor + name) || name;
92 | }
93 |
94 | function fixKey(key) {
95 | return { from: '0%', to: '100%' }[key] || (key + '').replace('%', '') + '%';
96 | }
97 |
98 | function getKeyframesStyle(keys, name) {
99 | var cssText = '';
100 |
101 | if (typeof keys == 'string') {
102 | if (KEY_REG.test(keys)) {
103 | return keys.replace(KEY_REG, keyframes);
104 | }
105 |
106 | cssText = keys.replace(/^\s*{\s*(?={)|}\s*(?=}\s*$)/gi, '');
107 | } else if (Array.isArray(keys)) {
108 | cssText = keys
109 | .map(function(rule, i) {
110 | return ((i / (keys.length - 1)) * 100 || 0) + '%' + ' { ' + getKeyframesRule(rule) + ' }';
111 | })
112 | .join(' ');
113 | } else if (keys) {
114 | cssText = Object.keys(keys)
115 | .map(function(key) {
116 | var ruleText = getKeyframesRule(keys[key]);
117 |
118 | return splitKey(key)
119 | .map(function(k) {
120 | return fixKey(k) + ' { ' + ruleText + ' }';
121 | })
122 | .join(' ');
123 | })
124 | .join(' ');
125 | }
126 |
127 | return keyframes + ' ' + name + ' { ' + cssText + ' }';
128 | }
129 |
130 | function getKeyframesRule(rule) {
131 | var ruleText = '';
132 |
133 | if (typeof rule == 'string') {
134 | ruleText = getKeyframesRule(rule.split(/\s*;\s*/g));
135 | } else if (Array.isArray(rule)) {
136 | ruleText = getKeyframesRule(
137 | rule.reduce(function(obj, text) {
138 | var ret = text.split(/\s*:\s*/);
139 |
140 | if (ret.length > 1) {
141 | obj[ret[0]] = ret[1];
142 | }
143 |
144 | return obj;
145 | }, {})
146 | );
147 | } else if (rule) {
148 | ruleText = Object.keys(rule)
149 | .map(function(key) {
150 | return fixCSS3(key) + ': ' + rule[key] + ';';
151 | })
152 | .join(' ');
153 | }
154 |
155 | return ruleText;
156 | }
157 |
158 | function splitKey(key) {
159 | return (key + '').trim().split(/\s*[\s,]\s*/);
160 | }
161 |
162 | struct.prototype = {
163 | constructor: struct,
164 | init: function(name, keys) {
165 | var ckf; //CSSKeyframes
166 |
167 | if (!struct.support) return this;
168 |
169 | if (typeof name == 'object' || KEY_REG.test(name)) {
170 | if (name.cssRules && name.type == KEYFRAMES_RULE) {
171 | ckf = name;
172 | } else {
173 | keys = name;
174 | name = null;
175 | }
176 | }
177 |
178 | if (!name) {
179 | name = 'js2keyframes-' + parseInt(Math.random() * 1e10);
180 | }
181 |
182 | if (!ckf) {
183 | var sheet = getSheet(),
184 | id = sheet.insertRule(getKeyframesStyle(keys, name), sheet.cssRules.length);
185 |
186 | ckf = sheet.cssRules[id];
187 | }
188 |
189 | this.keyframesRule = ckf || {};
190 |
191 | return this.extract();
192 | },
193 | extract: function() {
194 | this.keyframes = slice.call(this.cssRules).reduce(function(obj, rule) {
195 | obj[rule.keyText] = rule;
196 |
197 | return obj;
198 | }, {});
199 |
200 | return this;
201 | },
202 | get: function(name) {
203 | return this.keyframes[fixKey(splitKey(name)[0])];
204 | },
205 | add: function(name, value) {
206 | var frameRule = this.keyframesRule,
207 | insert = 'appendRule' in frameRule ? 'appendRule' : 'insertRule',
208 | ruleText;
209 |
210 | if (typeof name == 'object') {
211 | for (var key in name) {
212 | this.add(key, name[key]);
213 | }
214 | } else {
215 | ruleText = getKeyframesRule(value);
216 |
217 | splitKey(name).forEach(
218 | function(k) {
219 | this.remove(k);
220 | frameRule[insert](fixKey(k) + ' { ' + ruleText + ' }');
221 | }.bind(this)
222 | );
223 | }
224 |
225 | return this.extract();
226 | },
227 | remove: function(name) {
228 | var frameRule = this.keyframesRule;
229 |
230 | splitKey(name).forEach(function(k) {
231 | frameRule.deleteRule(fixKey(k));
232 | });
233 |
234 | return this.extract();
235 | },
236 | clear: function() {
237 | slice.call(this.cssRules).forEach(
238 | function(rule) {
239 | this.remove(rule.keyText);
240 | }.bind(this)
241 | );
242 |
243 | return this.extract();
244 | }
245 | };
246 |
247 | var extend = {
248 | vendor: cssVendor,
249 | get: function(name) {
250 | return this.CSSKeyframes[name];
251 | },
252 | add: function() {
253 | return this.apply(null, arguments);
254 | },
255 | remove: function(name) {
256 | iterateSheet(function(rule, i, sheet) {
257 | if (rule.name == (name.name || name)) sheet.deleteRule(i);
258 | });
259 |
260 | return true;
261 | },
262 | getSheet: getSheet,
263 | 'animation-css': animation,
264 | animation: camelCase(animation),
265 | support: camelCase(animation) in divstyle
266 | };
267 |
268 | if (typeof Object.defineProperties == 'function') {
269 | 'name duration timing-function delay iteration-count direction play-state fill-mode'
270 | .split(' ')
271 | .forEach(function(prop) {
272 | var name = 'animation-' + prop,
273 | caseName = camelCase(name);
274 |
275 | struct[caseName] = camelCase((struct[name] = fixCSS3(name)));
276 | });
277 |
278 | Object.keys(extend).forEach(function(name) {
279 | struct[name] = extend[name];
280 | });
281 |
282 | 'name cssText cssRules'.split(' ').forEach(function(prop) {
283 | Object.defineProperty(struct.prototype, prop, {
284 | get: function() {
285 | return (this.keyframesRule || {})[prop];
286 | },
287 | enumerable: true
288 | });
289 | });
290 |
291 | Object.defineProperty(struct, 'CSSKeyframes', {
292 | get: function() {
293 | var ret = {};
294 |
295 | iterateSheet(
296 | function(rule) {
297 | ret[rule.name] = this(rule);
298 | }.bind(this)
299 | );
300 |
301 | return ret;
302 | },
303 | enumerable: true
304 | });
305 | }
306 |
307 | if (typeof exports === 'object') {
308 | module.exports = struct;
309 | } else if (typeof define == 'function' && define.amd) {
310 | define('JS2CSSKeyframes', function() {
311 | return struct;
312 | });
313 | } else ROOT.JS2CSSKeyframes = struct;
314 | })(typeof window === 'undefined' ? global : window, function(name, keys) {
315 | if (!(this instanceof arguments.callee)) {
316 | return new arguments.callee(name, keys);
317 | }
318 |
319 | arguments.callee.support && this.init(name, keys);
320 | });
321 |
--------------------------------------------------------------------------------