$.cssHooks 扩展 jquery 的属性操作
最近在研究 $.transit 然后发现了 $.cssHooks 这个方法,试了一下官方的 demo 表示好像并不是那么回事,所以决定深入的测试一下。
$.cssHooks 的作用在于拓展属性(自己意淫的),比如用 "rotate" 代替 "transform: rotate()" 之类的,酱紫,$(".box1").css({"rotate":"10"});
而且还可以用 "rotate" 代替 "transform:rotate()" 和 "-webkit-transform:rotate()",这样是不是很爽呢...
那么,我们就分三个阶段好了,一获取当前浏览器支持的属性,二拆分成特殊属性,三添加到属性拓展。
获取兼容的属性这个很容易找到啦,但对我这种懒人来说,代码优美最重要咯,行数越少越好了。
var div = document.createElement('div'); var support = {}; function getVendorPropertyName(prop) { if (prop in div.style) return prop; var prefixes = ['Moz', 'Webkit', 'O', 'ms']; var prop_ = prop.charAt(0).toUpperCase() + prop.substr(1); for (var i=0; i<prefixes.length; ++i) { var vendorProp = prefixes[i] + prop_; if (vendorProp in div.style) { return vendorProp; } } } function checkTransition3dSupport() { div.style[support.transform] = ''; div.style[support.transform] = 'rotateY(90deg)'; return div.style[support.transform] !== ''; } support.perspective = getVendorPropertyName('perspective'); support.transition = getVendorPropertyName('transition'); support.transitionDelay = getVendorPropertyName('transitionDelay'); support.transform = getVendorPropertyName('transform'); support.transformOrigin = getVendorPropertyName('transformOrigin'); support.filter = getVendorPropertyName('Filter');
这也是看了多家源码后总结出来最简短的了,不要嫌弃。
拆分特殊属性这块,其实没有太多时间重写,先写上 $.transit 的代码好了,之后我会再写一个关于 filter 特殊属性的拆分。
var Transform = function( str ) { if (typeof str === 'string') { this.parse(str); } return this; }; // 将属性的值(如 rotate())转换为数字 Transform.prototype.parse = function( str ) { var self = this; str.replace(/([a-zA-Z0-9]+)\((.*?)\)/g, function(x, prop, val) { self.divideString(prop, val); }); } // 将多值字符串拆分 Transform.prototype.divideString = function( prop, val ) { var args = (typeof val === 'string') ? val.split(',') : (val.constructor === Array) ? val : [ val ]; args.unshift(prop); Transform.prototype.set.apply(this, args); } // 设置 transform 的值,用于 $.cssHooks 方法 Transform.prototype.set = function( prop ) { var args = Array.prototype.slice.apply(arguments, [1]); if (this.setter[prop]) { this.setter[prop].apply(this, args); } else { this[prop] = args.join(','); } } // transform 拆分值设置 Transform.prototype.setter = { rotate: function(theta) { this.rotate = unit(theta, 'deg'); }, rotateX: function(theta) { this.rotateX = unit(theta, 'deg'); }, rotateY: function(theta) { this.rotateY = unit(theta, 'deg'); }, scale: function(x, y) { if (y === undefined) { y = x; } this.scale = x + "," + y; }, skewX: function(x) { this.skewX = unit(x, 'deg'); }, skewY: function(y) { this.skewY = unit(y, 'deg'); }, perspective: function(dist) { this.perspective = unit(dist, 'px'); }, x: function(x) { this.set('translate', x, null); }, y: function(y) { this.set('translate', null, y); }, translate: function(x, y) { if (this._translateX === undefined) { this._translateX = 0; } if (this._translateY === undefined) { this._translateY = 0; } if (x !== null && x !== undefined) { this._translateX = unit(x, 'px'); } if (y !== null && y !== undefined) { this._translateY = unit(y, 'px'); } this.translate = this._translateX + "," + this._translateY; }, } // 获取已有属性及其值,用于 $.cssHooks 的方法 Transform.prototype.get = function( prop ) { if (this.getter[prop]) { return this.getter[prop].apply(this); } else { return this[prop] || 0; } } // 获取 Transform.prototype.getter = { x: function() { return this._translateX || 0; }, y: function() { return this._translateY || 0; }, scale: function() { var s = (this.scale || "1,1").split(','); if (s[0]) { s[0] = parseFloat(s[0]); } if (s[1]) { s[1] = parseFloat(s[1]); } return (s[0] === s[1]) ? s[0] : s; }, rotate3d: function() { var s = (this.rotate3d || "0,0,0,0deg").split(','); for (var i=0; i<=3; ++i) { if (s[i]) { s[i] = parseFloat(s[i]); } } if (s[3]) { s[3] = unit(s[3], 'deg'); } return s; } } // 将多值合并为 3d 状态 Transform.prototype.toString = function( use3d ) { var re = []; for (var i in this) { if (this.hasOwnProperty(i)) { var sure = (!support.transform3d) && ( (i === 'rotateX') || (i === 'rotateY') || (i === 'perspective') || (i === 'transformOrigin')) if (sure) { continue; } if (i[0] !== '_') { if (use3d && (i === 'scale')) { re.push(i + "3d(" + this[i] + ",1)"); } else if (use3d && (i === 'translate')) { re.push(i + "3d(" + this[i] + ",0)"); } else { re.push(i + "(" + this[i] + ")"); } } } } return re.join(" "); }
最后一步,添加到 $.cssHooks 中,具体 $.cssHooks 怎么用,搜文档比看我写过程其实要更精确
// 添加特殊兼容性 $.cssHooks['transit:transform'] = { get: function(elem) { return $(elem).data('transform') || new Transform(); }, set: function(elem, value) { if (!(value instanceof Transform)) { value = new Transform(value); } elem.style[support.transform] = value.toString(); $(elem).data('transform', value); } }; // 兼容 transform 属性 $.cssHooks.transform = { set: $.cssHooks['transit:transform'].set }; // 让特殊值可拥有过渡效果 function registerCssHook(prop, isPixels) { if (!isPixels) { $.cssNumber[prop] = true; } // $.transit.propertyMap[prop] = support.transform; $.cssHooks[prop] = { get: function(elem) { var t = $(elem).css('transit:transform'); return t.get(prop); }, set: function(elem, value) { var t = $(elem).css('transit:transform'); t.divideString(prop, value); $(elem).css({ 'transit:transform': t }); } }; } registerCssHook('scale'); registerCssHook('scaleX'); registerCssHook('scaleY'); registerCssHook('translate'); registerCssHook('rotate'); registerCssHook('rotateX'); registerCssHook('rotateY'); registerCssHook('rotate3d'); registerCssHook('perspective'); registerCssHook('skewX'); registerCssHook('skewY'); registerCssHook('x', true); registerCssHook('y', true);
ok,加上了上面这三串之后,你会发觉,$(".box1").css({"rotate":"10"}); 成为了现实,当初还要用插件和写多个 transform 已经不需要了。而且 $.animate 也可以哟。
然而,$.cssHook 并不完善,所以会出现 rotateX / perspective 等无效的情况,
而 css 本身也有不足,比如 scaleX 的默认值为 0,本身没有 translate 那么添加 translate 动画也是无效的。
还有一个问题在于,未用自己设的特殊属性赋值的话,当然也是取不到它的值的,也就是说你用 css 写的 translateX 并不能用 $.fn.css("x") 来获取到。
家里电脑坏了,不开森,不开森,不开森...