![](/img/trans.png)
[英]Convert css styles to inline styles with javascript keeping the style units
[英]convert css units
我正在嘗試取回所有有效“長度”和“百分比”單位的樣式屬性,從為該屬性設置的原始值轉換而來。
例如,如果我有一個 style.width 設置為 20% 的 div,我想要返回一個 object,該值以百分比(當然是 20%)、像素(無論實際像素寬度是多少)、em、pt、前等
我意識到“百分比”不是“長度”值,並且並非所有接受長度值的屬性都接受百分比,但也希望包括該值。
當然,某些值將具體取決於元素,可能是 DOM 中的 position(例如,獲取 em 值還需要該元素的父計算字體大小)。
我可以假設樣式是為元素明確設置的——我知道如何檢索元素的當前計算樣式——我只是希望不要重復別人可能已經完成的工作。 我也知道http://www.galasoft.ch/myjavascript/WebControls/css-length.html ,但它依賴於 style.pixelWidth 或 node.clientWidth,並且在 Chrome 中失敗(我假設它在Safari 以及……可能還有其他人)。
我已經計算出顏色值(rgb、rgba、十六進制、名稱)——這當然要簡單得多。 我正在處理數學上可變的值,所以實際上只需要“長度”和“百分比”值(如果在具有非長度、非百分比值的屬性集上調用——比如“字體大小:更大”—— function 可能會失敗,或引發錯誤)。
如果按程序編寫,這樣的事情將是理想的:
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;
}
我不是要有人為我編寫代碼,但我希望有人以前已經這樣做過,並且可以在某些開源庫、框架、博客文章、嘖嘖等等中找到它。 如果做不到這一點,如果有人有一個聰明的想法如何簡化流程,那也很好(上面鏈接的作者創建了一個臨時 div 並計算了一個值來確定其他單位的比率 - 一個方便的想法但是不是我完全相信的,而且絕對是需要補充邏輯來處理我希望接受的一切的人)。
提前感謝您的任何見解或建議。
編輯:更新以允許用戶選擇要返回的單個單位(例如,以 % 的形式存在,以 px 返回)-當足夠時性能有很大改進-可能最終將其更改為僅接受單個單位進行轉換,並擺脫循環。 感謝無眼的幫助。 /編輯
這就是我想出的 - 經過初步測試,它似乎有效。 我從原始問題中提到的鏈接中借用了臨時 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;
})();
蒂亞
查看Units ,這是一個執行這些轉換的 JavaScript 庫。
派對遲到了,我認為這不一定能完全回答問題,因為我沒有包括百分比轉換。 但是,我確實認為這是一個很好的開始,可以根據您的特定用途輕松修改。
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
、 mm
、 q
、 in
、 pc
、 pt
、 rem
、 em
、 vw
、 vh
、 s
、 ms
, deg
, rad
, grad
, turn
例如:
10rem
10.2em
-0.34cm
+10.567s
您可以在此處查看完整的格式組合: https ://jsfiddle.net/thelevicole/k7yt4naw/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
屬性(值的單位,如果需要)。
這並不能完全回答問題,但這可能是一個開始。
在深入研究 SVG 規范時,我發現SVGLength
提供了一個有趣的 DOM API 用於內置單位轉換。 這是一個使用它的 function:
/** Convert a value to a different unit
* @param {number} val - value to convert
* @param {string} from - unit `val`; can be one of: %, em, ex, px, cm, mm, in, pt, pc
* @param {string} to - unit to convert to, same as `from`
* @returns {object} - {number, string} with the number/string forms for the converted value
*/
const convert_units = (() => {
const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
const len = rect.width.baseVal;
const modes = {
"%": len.SVG_LENGTHTYPE_PERCENTAGE,
"em": len.SVG_LENGTHTYPE_EMS,
"ex": len.SVG_LENGTHTYPE_EXS,
"px": len.SVG_LENGTHTYPE_PX,
"cm": len.SVG_LENGTHTYPE_CM,
"mm": len.SVG_LENGTHTYPE_MM,
"in": len.SVG_LENGTHTYPE_IN,
"pt": len.SVG_LENGTHTYPE_PT,
"pc": len.SVG_LENGTHTYPE_PC,
};
return (val, from, to, context) => {
if (context)
context.appendChild(rect);
len.newValueSpecifiedUnits(modes[from], val);
len.convertToSpecifiedUnits(modes[to]);
const out = {
number: len.valueInSpecifiedUnits,
string: len.valueAsString
};
if (context)
context.removeChild(rect);
return out;
};
})();
使用示例:
convert_units(1, "in", "mm");
// output: {"number": 25.399999618530273, "string": "25.4mm"}
有些單位是相對的,因此需要暫時放在父 DOM 元素中才能解析單位的絕對值。 在這些情況下,為父元素提供第四個參數:
convert_units(1, "em", "px", document.body);
// output: {"number": 16, "string": "16px"}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.