요약
이제 CSS에는 JavaScript에서 값을 사용하기 위한 적절한 객체 기반 API가 있습니다.
el.attributeStyleMap.set('padding', CSS.px(42));
const padding = el.attributeStyleMap.get('padding');
console.log(padding.value, padding.unit); // 42, 'px'
문자열을 연결하는 시대와 미묘한 버그는 이제 끝났습니다.
소개
이전 CSSOM
CSS는 수년 동안 객체 모델 (CSSOM)을 사용해 왔습니다. 실제로 JavaScript에서 .style
를 읽거나 설정할 때마다 이를 사용합니다.
// Element styles.
el.style.opacity = 0.3;
typeof el.style.opacity === 'string' // Ugh. A string!?
// Stylesheet rules.
document.styleSheets[0].cssRules[0].style.opacity = 0.3;
새 CSS 유형 OM
Houdini 노력의 일환으로 새로운 CSS 유형이 지정된 객체 모델 (Typed OM)으로 CSS 값에 유형, 메서드, 적절한 객체 모델을 추가하여 이 세계관을 확장합니다. 문자열 대신 값이 자바스크립트 객체로 노출되어 성능이 우수하고 합리적인 CSS 조작을 용이하게 합니다.
element.style
를 사용하는 대신 요소의 새로운 .attributeStyleMap
속성 및 스타일시트 규칙의 경우 .styleMap
속성을 통해 스타일에 액세스합니다. 둘 다 StylePropertyMap
객체를 반환합니다.
// Element styles.
el.attributeStyleMap.set('opacity', 0.3);
typeof el.attributeStyleMap.get('opacity').value === 'number' // Yay, a number!
// Stylesheet rules.
const stylesheet = document.styleSheets[0];
stylesheet.cssRules[0].styleMap.set('background', 'blue');
StylePropertyMap
는 맵과 같은 객체이므로 일반적인 모든 문제 (get/set/keys/values/entries)를 지원하므로 다음과 같이 유연하게 작업할 수 있습니다.
// All 3 of these are equivalent:
el.attributeStyleMap.set('opacity', 0.3);
el.attributeStyleMap.set('opacity', '0.3');
el.attributeStyleMap.set('opacity', CSS.number(0.3)); // see next section
// el.attributeStyleMap.get('opacity').value === 0.3
// StylePropertyMaps are iterable.
for (const [prop, val] of el.attributeStyleMap) {
console.log(prop, val.value);
}
// → opacity, 0.3
el.attributeStyleMap.has('opacity') // true
el.attributeStyleMap.delete('opacity') // remove opacity.
el.attributeStyleMap.clear(); // remove all styles.
두 번째 예에서 opacity
는 문자열 ('0.3'
)로 설정되었지만 나중에 속성을 다시 읽을 때 숫자가 다시 나옵니다.
이점
그렇다면 CSS Typed OM은 어떤 문제를 해결하려고 할까요? 위의 예와 이 문서의 나머지 부분을 살펴보면 CSS Typed OM이 이전 객체 모델보다 훨씬 더 상세하다고 주장할 수 있습니다. 동의하겠습니다.
Typed OM을 삭제하기 전에 테이블에 제공하는 몇 가지 주요 기능을 고려하세요.
버그가 적습니다. 예를 들어 숫자 값은 항상 문자열이 아닌 숫자로 반환됩니다.
el.style.opacity += 0.1; el.style.opacity === '0.30.1' // dragons!
산술 연산 및 단위 변환. 절대 길이 단위 (예:
px
->cm
) 간을 변환하고 기본적인 계산을 실행합니다.값 고정 및 반올림. 입력한 OM 값이 속성에 허용되는 범위 내에 있도록 반올림 또는 고정합니다.
성능 개선. 브라우저에서 문자열 값을 직렬화 및 역직렬화하는 작업을 줄일 수 있습니다. 이제 엔진은 JS 및 C++에서 CSS 값에 관한 유사한 이해를 사용합니다. Tab Akins는 이전 CSSOM 및 문자열을 사용할 때보다 Typed OM이 초당 약 30% 더 빠른 작업 속도를 제공하는 몇 가지 초기 성능 벤치마크를 보여주었습니다. 이는
requestionAnimationFrame()
를 사용하는 빠른 CSS 애니메이션에 중요할 수 있습니다. crbug.com/808933은 Blink에서 추가 성능 작업을 추적합니다."카멜 표기법의 CSS 이름이나 문자열을 사용해야 하나요?" 이름이 카멜 표기법 또는 문자열인지 더 이상 추측할 필요가 없습니다 (예:
el.style.backgroundColor
또는el.style['background-color']
). 유형이 지정된 OM의 CSS 속성 이름은 항상 문자열로, 실제로 CSS에 작성하는 이름과 일치합니다.
브라우저 지원 및 기능 감지
입력한 OM은 Chrome 66에 출시되어 Firefox에서 구현 중입니다. Edge는 지원 신호를 표시했지만 아직 플랫폼 대시보드에 추가하지 않았습니다.
특성 감지의 경우 CSS.*
숫자 팩토리 중 하나가 정의되어 있는지 확인할 수 있습니다.
if (window.CSS && CSS.number) {
// Supports CSS Typed OM.
}
API 기본사항
스타일 액세스
값은 CSS 유형 OM의 단위와는 별개입니다. 스타일을 가져오면 value
및 unit
가 포함된 CSSUnitValue
이 반환됩니다.
el.attributeStyleMap.set('margin-top', CSS.px(10));
// el.attributeStyleMap.set('margin-top', '10px'); // string arg also works.
el.attributeStyleMap.get('margin-top').value // 10
el.attributeStyleMap.get('margin-top').unit // 'px'
// Use CSSKeyWorldValue for plain text values:
el.attributeStyleMap.set('display', new CSSKeywordValue('initial'));
el.attributeStyleMap.get('display').value // 'initial'
el.attributeStyleMap.get('display').unit // undefined
계산된 스타일
계산된 스타일이 window
의 API에서 HTMLElement
, computedStyleMap()
의 새 메서드로 이동했습니다.
이전 CSSOM
el.style.opacity = 0.5;
window.getComputedStyle(el).opacity === "0.5" // Ugh, more strings!
새 유형의 OM
el.attributeStyleMap.set('opacity', 0.5);
el.computedStyleMap().get('opacity').value // 0.5
값 고정 / 반올림
새 객체 모델의 좋은 기능 중 하나는 계산된 스타일 값의 자동 클램핑 또는 반올림입니다. 예를 들어 opacity
를 허용되는 범위([0, 1])를 벗어난 값으로 설정하려 한다고 가정해 보겠습니다. 입력된 OM은 스타일을 계산할 때 값을 1
로 고정합니다.
el.attributeStyleMap.set('opacity', 3);
el.attributeStyleMap.get('opacity').value === 3 // val not clamped.
el.computedStyleMap().get('opacity').value === 1 // computed style clamps value.
마찬가지로 z-index:15.4
를 설정하면 15
로 반올림되므로 값이 정수로 유지됩니다.
el.attributeStyleMap.set('z-index', CSS.number(15.4));
el.attributeStyleMap.get('z-index').value === 15.4 // val not rounded.
el.computedStyleMap().get('z-index').value === 15 // computed style is rounded.
CSS 숫자 값
숫자는 유형이 있는 OM의 두 가지 유형의 CSSNumericValue
객체로 표현됩니다.
CSSUnitValue
- 단일 단위 유형이 포함된 값 (예:"42px"
).CSSMathValue
- 수학 표현식과 같이 값/단위를 2개 이상 포함하는 값입니다 (예:"calc(56em + 10%)"
).
단위 값
단순한 숫자 값 ("50%"
)은 CSSUnitValue
객체로 표현됩니다.
이러한 객체를 직접 만들 수 있지만 (new CSSUnitValue(10, 'px')
) 대부분의 경우 CSS.*
팩토리 메서드를 사용합니다.
const {value, unit} = CSS.number('10');
// value === 10, unit === 'number'
const {value, unit} = CSS.px(42);
// value === 42, unit === 'px'
const {value, unit} = CSS.vw('100');
// value === 100, unit === 'vw'
const {value, unit} = CSS.percent('10');
// value === 10, unit === 'percent'
const {value, unit} = CSS.deg(45);
// value === 45, unit === 'deg'
const {value, unit} = CSS.ms(300);
// value === 300, unit === 'ms'
CSS.*
메서드의 전체 목록은 사양을 참고하세요.
수학 값
CSSMathValue
객체는 수학 표현식을 나타내며 일반적으로 값/단위를 2개 이상 포함합니다. 일반적인 예는 CSS calc()
표현식을 만드는 것이지만 calc()
, min()
, max()
등 모든 CSS 함수를 위한 메서드가 있습니다.
new CSSMathSum(CSS.vw(100), CSS.px(-10)).toString(); // "calc(100vw + -10px)"
new CSSMathNegate(CSS.px(42)).toString() // "calc(-42px)"
new CSSMathInvert(CSS.s(10)).toString() // "calc(1 / 10s)"
new CSSMathProduct(CSS.deg(90), CSS.number(Math.PI/180)).toString();
// "calc(90deg * 0.0174533)"
new CSSMathMin(CSS.percent(80), CSS.px(12)).toString(); // "min(80%, 12px)"
new CSSMathMax(CSS.percent(80), CSS.px(12)).toString(); // "max(80%, 12px)"
중첩된 표현식
수학 함수를 사용하여 더 복잡한 값을 만드는 것은 약간 혼란스러울 수 있습니다. 다음은 시작하는 데 도움이 되는 몇 가지 예입니다. 읽기 쉽도록 들여쓰기를 추가했습니다.
calc(1px - 2 * 3em)
는 다음과 같이 구성됩니다.
new CSSMathSum(
CSS.px(1),
new CSSMathNegate(
new CSSMathProduct(2, CSS.em(3))
)
);
calc(1px + 2px + 3px)
는 다음과 같이 구성됩니다.
new CSSMathSum(CSS.px(1), CSS.px(2), CSS.px(3));
calc(calc(1px + 2px) + 3px)
는 다음과 같이 구성됩니다.
new CSSMathSum(
new CSSMathSum(CSS.px(1), CSS.px(2)),
CSS.px(3)
);
산술 연산
CSS Typed OM의 가장 유용한 기능 중 하나는 CSSUnitValue
객체에서 수학 연산을 실행할 수 있다는 것입니다.
기본 작업
기본 작업 (add
/sub
/mul
/div
/min
/max
)이 지원됩니다.
CSS.deg(45).mul(2) // {value: 90, unit: "deg"}
CSS.percent(50).max(CSS.vw(50)).toString() // "max(50%, 50vw)"
// Can Pass CSSUnitValue:
CSS.px(1).add(CSS.px(2)) // {value: 3, unit: "px"}
// multiple values:
CSS.s(1).sub(CSS.ms(200), CSS.ms(300)).toString() // "calc(1s + -200ms + -300ms)"
// or pass a `CSSMathSum`:
const sum = new CSSMathSum(CSS.percent(100), CSS.px(20)));
CSS.vw(100).add(sum).toString() // "calc(100vw + (100% + 20px))"
전환
절대 길이 단위는 다른 단위 길이로 변환할 수 있습니다.
// Convert px to other absolute/physical lengths.
el.attributeStyleMap.set('width', '500px');
const width = el.attributeStyleMap.get('width');
width.to('mm'); // CSSUnitValue {value: 132.29166666666669, unit: "mm"}
width.to('cm'); // CSSUnitValue {value: 13.229166666666668, unit: "cm"}
width.to('in'); // CSSUnitValue {value: 5.208333333333333, unit: "in"}
CSS.deg(200).to('rad').value // 3.49066...
CSS.s(2).to('ms').value // 2000
형평성
const width = CSS.px(200);
CSS.px(200).equals(width) // true
const rads = CSS.deg(180).to('rad');
CSS.deg(180).equals(rads.to('deg')) // true
CSS 변환 값
CSS 변환은 CSSTransformValue
를 사용하여 생성되고 변환 값의 배열 (예: CSSRotate
, CSScale
, CSSSkew
, CSSSkewX
, CSSSkewY
)을 전달합니다. 예를 들어 다음 CSS를 다시 만들고 싶다고 가정해 보겠습니다.
transform: rotateZ(45deg) scale(0.5) translate3d(10px,10px,10px);
유형 OM으로 변환:
const transform = new CSSTransformValue([
new CSSRotate(CSS.deg(45)),
new CSSScale(CSS.number(0.5), CSS.number(0.5)),
new CSSTranslate(CSS.px(10), CSS.px(10), CSS.px(10))
]);
설명이 자세함 (ᄏᄏᄏ! ) 외에도 CSSTransformValue
에는 멋진 기능이 있습니다. 여기에는 2D 및 3D 변환을 구분하는 불리언 속성과 변환의 DOMMatrix
표현을 반환하는 .toMatrix()
메서드가 있습니다.
new CSSTranslate(CSS.px(10), CSS.px(10)).is2D // true
new CSSTranslate(CSS.px(10), CSS.px(10), CSS.px(10)).is2D // false
new CSSTranslate(CSS.px(10), CSS.px(10)).toMatrix() // DOMMatrix
예: 큐브에 애니메이션 적용
변환 사용의 실제 예를 살펴보겠습니다. JavaScript 및 CSS 변환을 사용하여 큐브에 애니메이션을 적용할 것입니다.
const rotate = new CSSRotate(0, 0, 1, CSS.deg(0));
const transform = new CSSTransformValue([rotate]);
const box = document.querySelector('#box');
box.attributeStyleMap.set('transform', transform);
(function draw() {
requestAnimationFrame(draw);
transform[0].angle.value += 5; // Update the transform's angle.
// rotate.angle.value += 5; // Or, update the CSSRotate object directly.
box.attributeStyleMap.set('transform', transform); // commit it.
})();
짚고 넘어갈 사항:
- 숫자 값은 수학을 사용하여 각도를 직접 증가시킬 수 있음을 의미합니다.
- 애니메이션은 DOM을 터치하거나 모든 프레임에서 값을 다시 읽는 대신 (예:
box.style.transform=`rotate(0,0,1,${newAngle}deg)`
없음) 기본CSSTransformValue
데이터 객체를 업데이트하여 성능을 개선함으로써 구동됩니다.
데모
브라우저가 Typed OM을 지원하는 경우 아래와 같이 빨간색 큐브가 표시됩니다. 정육면체 위로 마우스를 가져가면 정육면체가 회전하기 시작합니다. 애니메이션은 CSS Typed OM을 사용합니다. 🤘
CSS 맞춤 속성 값
CSS var()
는 유형이 있는 OM에서 CSSVariableReferenceValue
객체가 됩니다.
이러한 값은 모든 유형 (px, %, em, rgba() 등)을 사용할 수 있으므로 CSSUnparsedValue
로 파싱됩니다.
const foo = new CSSVariableReferenceValue('--foo');
// foo.variable === '--foo'
// Fallback values:
const padding = new CSSVariableReferenceValue(
'--default-padding', new CSSUnparsedValue(['8px']));
// padding.variable === '--default-padding'
// padding.fallback instanceof CSSUnparsedValue === true
// padding.fallback[0] === '8px'
맞춤 속성의 값을 가져오려면 다음과 같이 해야 합니다.
<style>
body {
--foo: 10px;
}
</style>
<script>
const styles = document.querySelector('style');
const foo = styles.sheet.cssRules[0].styleMap.get('--foo').trim();
console.log(CSSNumericValue.parse(foo).value); // 10
</script>
게재순위 값
object-position
와 같이 공백으로 구분된 x/y 위치를 사용하는 CSS 속성은 CSSPositionValue
객체로 표시됩니다.
const position = new CSSPositionValue(CSS.px(5), CSS.px(10));
el.attributeStyleMap.set('object-position', position);
console.log(position.x.value, position.y.value);
// → 5, 10
값 파싱
유형이 있는 OM은 웹 플랫폼에 파싱 메서드를 도입합니다. 즉, CSS 값을 사용하기 전에 프로그래매틱 방식으로 CSS 값을 파싱할 수 있습니다. 이 새로운 기능은 조기 버그와 잘못된 형식의 CSS를 포착하는 데 도움을 줍니다.
전체 스타일 파싱:
const css = CSSStyleValue.parse(
'transform', 'translate3d(10px,10px,0) scale(0.5)');
// → css instanceof CSSTransformValue === true
// → css.toString() === 'translate3d(10px, 10px, 0) scale(0.5)'
값을 CSSUnitValue
로 파싱합니다.
CSSNumericValue.parse('42.0px') // {value: 42, unit: 'px'}
// But it's easier to use the factory functions:
CSS.px(42.0) // '42px'
오류 처리
예 - CSS 파서가 이 transform
값에 만족하는지 확인:
try {
const css = CSSStyleValue.parse('transform', 'translate4d(bogus value)');
// use css
} catch (err) {
console.err(err);
}
결론
마침내 CSS를 위해 객체 모델을 업데이트한 것은 좋습니다. 현을 다루는 일은 저에게는 절대로 적합하지 않았어요. CSS Typed OM API는 약간 장황하지만 앞으로 버그가 줄고 코드의 성능이 향상되기를 바랍니다.