├── .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 |

效果演示,生成常用css3动画组件,代码见 css3-animation.js

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 | --------------------------------------------------------------------------------