最近在研究 $.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") 来获取到。
家里电脑坏了,不开森,不开森,不开森...