我试图以所有有效'长度'和'百分比'为单位取回一个样式属性,从该属性的原始值转换而来。
例如,如果我有一个div,style.width设置为20%,我想要一个具有该值的对象,以百分比(当然,20%),像素(无论实际像素宽度是多少),em ,pt,ex等。
我意识到'百分比'不是'长度'值,并且并非所有接受长度值的属性都接受百分比,但也希望包括它。
当然,一些值将特别依赖于元素,并且可能是它在DOM中的位置(例如,获取em值也需要该元素的父计算字体大小)。
我可以假设为该元素明确设置了样式 - 我知道如何检索元素的当前计算样式 - 我只是希望不重复其他人可能已经完成的工作。我也知道http://www.galasoft.ch/myjavascript/WebControls/css-length.html,但它依赖于style.pixelWidth或node.clientWidth,并且在Chrome中失败(我认为它在Safari中也失败了......还有其他人)。
我已经得到了颜色值(rgb,rgba,hex,name) - 这当然要简单得多。我正在使用数学上可变的值,因此实际上只需要'length'和'percent'值(如果在具有非长度,非百分比值的属性集上调用 - 如'font-size:larger' - 该函数可能会失败,或抛出错误。)
如果以程序方式编写,这样的事情将是理想的:
function getUnits(target, prop){
var value = // get target's computed style property value
// figure out what unit is being used natively, and it's values - for this e.g., 100px
var units = {};
units.pixel = 100;
units.percent = 50; // e.g., if the prop was height and the parent was 200px tall
units.inch = 1.39; // presumably units.pixel / 72 would work, but i'm not positive
units.point = units.inch / 72;
units.pica = units.point * 12;
// etc...
return units;
}
我不是要求有人为我编写代码,但我希望有人之前已经完成了这项工作,并且它可以在一些开源库,框架,博客文章,tut等等中使用。失败的是,如果有人聪明地想要如何简化流程,那也会很棒(上面链接的作者创建了一个临时div并计算了一个单独的值来确定其他单位的比率 - 一个方便的想法但是不是一个我完全卖掉了,绝对是一个需要补充逻辑来处理我希望接受的一切的东西。
提前感谢任何见解或建议。
答案 0 :(得分:7)
编辑:更新以允许用户选择要返回的单个单元(例如,以%存在,以px返回) - 当性能足够大时性能的大幅提升 - 可能最终将其更改为仅接受单个单元转换,并摆脱循环。感谢无眼界的帮助。 / EDIT
这就是我想出来的 - 经过初步测试后似乎有效。我从原始问题中提到的链接中借用了临时div的想法,但这是关于从其他类中获取的所有内容。
如果有人有任何意见或改进,我很乐意听到。
(function(){
// pass to string.replace for camel to hyphen
var hyphenate = function(a, b, c){
return b + "-" + c.toLowerCase();
}
// get computed style property
var getStyle = function(target, prop){
if(prop in target.style){ // if it's explicitly assigned, just grab that
if(!!(target.style[prop]) || target.style[prop] === 0){
return target.style[prop];
}
}
if(window.getComputedStyle){ // gecko and webkit
prop = prop.replace(/([a-z])([A-Z])/, hyphenate); // requires hyphenated, not camel
return window.getComputedStyle(target, null).getPropertyValue(prop);
}
if(target.currentStyle){ // ie
return target.currentStyle[prop];
}
return null;
}
// get object with units
var getUnits = function(target, prop, returnUnit){
var baseline = 100; // any number serves
var item; // generic iterator
var map = { // list of all units and their identifying string
pixel : "px",
percent : "%",
inch : "in",
cm : "cm",
mm : "mm",
point : "pt",
pica : "pc",
em : "em",
ex : "ex"
};
var factors = {}; // holds ratios
var units = {}; // holds calculated values
var value = getStyle(target, prop); // get the computed style value
var numeric = value.match(/\d+/); // get the numeric component
if(numeric === null) { // if match returns null, throw error... use === so 0 values are accepted
throw "Invalid property value returned";
}
numeric = numeric[0]; // get the string
var unit = value.match(/\D+$/); // get the existing unit
unit = (unit == null) ? "px" : unit[0]; // if its not set, assume px - otherwise grab string
var activeMap; // a reference to the map key for the existing unit
for(item in map){
if(map[item] == unit){
activeMap = item;
break;
}
}
if(!activeMap) { // if existing unit isn't in the map, throw an error
throw "Unit not found in map";
}
var singleUnit = false; // return object (all units) or string (one unit)?
if(returnUnit && (typeof returnUnit == "string")) { // if user wants only one unit returned, delete other maps
for(item in map){
if(map[item] == returnUnit){
singleUnit = item;
continue;
}
delete map[item];
}
}
var temp = document.createElement("div"); // create temporary element
temp.style.overflow = "hidden"; // in case baseline is set too low
temp.style.visibility = "hidden"; // no need to show it
target.parentNode.appendChild(temp); // insert it into the parent for em and ex
for(item in map){ // set the style for each unit, then calculate it's relative value against the baseline
temp.style.width = baseline + map[item];
factors[item] = baseline / temp.offsetWidth;
}
for(item in map){ // use the ratios figured in the above loop to determine converted values
units[item] = (numeric * (factors[item] * factors[activeMap])) + map[item];
}
target.parentNode.removeChild(temp); // clean up
if(singleUnit !== false){ // if they just want one unit back
return units[singleUnit];
}
return units; // returns the object with converted unit values...
}
// expose
window.getUnits = this.getUnits = getUnits;
})();
tyia
答案 1 :(得分:2)
查看Units,这是一个执行这些转换的JavaScript库。
Here's a blog post由作者描述代码。
答案 2 :(得分:1)
Émile有点这样做,特别是在parse
函数中:
function parse(prop){
var p = parseFloat(prop), q = prop.replace(/^[\-\d\.]+/,'');
return isNaN(p) ? { v: q, f: color, u: ''} : { v: p, f: interpolate, u: q };
}
prop
参数是某个元素的computedStyle。返回的对象具有v
属性(值),稍后仅用于动画的f
方法和u
属性(如果需要,则为值的单位) )。
这并不完全回答这个问题,但它可能是一个开始。
答案 3 :(得分:0)
聚会迟到了,我认为这不一定能完全回答问题,因为我没有包括百分比转换。不过,我确实认为这是一个好的开始,可以根据您的特定用途轻松修改。
Javascript 函数
/**
* Convert absolute CSS numerical values to pixels.
*
* @link https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units#numbers_lengths_and_percentages
*
* @param {string} cssValue
* @param {null|HTMLElement} target Used for relative units.
* @return {*}
*/
window.convertCssUnit = function( cssValue, target ) {
target = target || document.body;
const supportedUnits = {
// Absolute sizes
'px': value => value,
'cm': value => value * 38,
'mm': value => value * 3.8,
'q': value => value * 0.95,
'in': value => value * 96,
'pc': value => value * 16,
'pt': value => value * 1.333333,
// Relative sizes
'rem': value => value * parseFloat( getComputedStyle( document.documentElement ).fontSize ),
'em': value => value * parseFloat( getComputedStyle( target ).fontSize ),
'vw': value => value / 100 * window.innerWidth,
'vh': value => value / 100 * window.innerHeight,
// Times
'ms': value => value,
's': value => value * 1000,
// Angles
'deg': value => value,
'rad': value => value * ( 180 / Math.PI ),
'grad': value => value * ( 180 / 200 ),
'turn': value => value * 360
};
// Match positive and negative numbers including decimals with following unit
const pattern = new RegExp( `^([\-\+]?(?:\\d+(?:\\.\\d+)?))(${ Object.keys( supportedUnits ).join( '|' ) })$`, 'i' );
// If is a match, return example: [ "-2.75rem", "-2.75", "rem" ]
const matches = String.prototype.toString.apply( cssValue ).trim().match( pattern );
if ( matches ) {
const value = Number( matches[ 1 ] );
const unit = matches[ 2 ].toLocaleLowerCase();
// Sanity check, make sure unit conversion function exists
if ( unit in supportedUnits ) {
return supportedUnits[ unit ]( value );
}
}
return cssValue;
};
示例用法
// Convert rem value to pixels
const remExample = convertCssUnit( '2.5rem' );
// Convert time unit (seconds) to milliseconds
const speedExample = convertCssUnit( '2s' );
// Convert angle unit (grad) to degrees
const emExample = convertCssUnit( '200grad' );
// Convert vw value to pixels
const vwExample = convertCssUnit( '80vw' );
// Convert the css variable to pixels
const varExample = convertCssUnit( getComputedStyle( document.body ).getPropertyValue( '--container-width' ) );
// Convert `em` value relative to page element
const emExample = convertCssUnit( '2em', document.getElementById( '#my-element' ) );
当前支持的格式
任何带有加号 (+
) 或减号 (-
) 的格式以及以下任何单位都是有效的:px
、cm
、{{ 1}}、mm
、q
、in
、pc
、pt
、rem
、em
、{{1} }、vw
、vh
、s
、ms
、deg
、rad
例如:
grad
您可以在此处查看完整的格式组合:https://jsfiddle.net/thelevicole/k7yt4naw/1/