1. 소개
CSS의 페인트 단계는 레이아웃 단계에서 생성된 박스의 크기와 계산된 스타일을 기반으로 박스의 배경, 콘텐츠, 하이라이트를 그리는 역할을 합니다.
이 명세서는 개발자가 크기 또는 계산된 스타일 변경에 따라 박스의 일부를 추가 <image> 함수로 그릴 수 있도록 하는 API를 설명합니다.
참고: 향후 명세 버전에서는 박스의 일부(예: 배경 레이어)에 대해 clip, global alpha, filter를 정의하는 기능이 추가될 수 있습니다.
2. 페인트 워클릿
오직 한 개의 엔진에서만 지원됩니다.
Opera52+Edge79+
Edge (Legacy)미지원IE미지원
Firefox for Android미지원iOS Safari미지원Chrome for Android65+Android WebView65+Samsung Internet9.0+Opera Mobile47+
paintWorklet
속성을 통해 페인트와 관련된 모든 클래스에 대한 Worklet
에 접근할 수 있습니다.
paintWorklet
의
worklet global scope type은 PaintWorkletGlobalScope
입니다.
paintWorklet
의
worklet destination type은 "paintworklet"
입니다.
partial namespace CSS { [SameObject ]readonly attribute Worklet ; };
paintWorklet
PaintWorkletGlobalScope
는 paintWorklet
의 글로벌 실행 컨텍스트입니다.
오직 한 개의 엔진에서만 지원됩니다.
Opera?Edge79+
Edge (Legacy)미지원IE미지원
Firefox for Android미지원iOS Safari미지원Chrome for Android65+Android WebView65+Samsung Internet9.0+Opera Mobile?
PaintWorkletGlobalScope
는 devicePixelRatio
속성을 가지며,
이는 Window.devicePixelRatio
속성과 동일합니다.
오직 한 개의 엔진에서만 지원됩니다.
Opera?Edge79+
Edge (Legacy)미지원IE미지원
Firefox for Android미지원iOS Safari미지원Chrome for Android65+Android WebView65+Samsung Internet9.0+Opera Mobile?
[Global =(Worklet ,PaintWorklet ),Exposed =PaintWorklet ]interface :
PaintWorkletGlobalScope WorkletGlobalScope {undefined registerPaint (DOMString ,
name VoidFunction );
paintCtor readonly attribute unrestricted double ; };
devicePixelRatio
PaintRenderingContext2DSettings
는 페인트 캔버스와 연결된 렌더링 컨텍스트의 설정을 포함합니다. PaintRenderingContext2DSettings
는 캔버스 렌더링 컨텍스트 2D 설정의 지원되는 부분 집합을 제공합니다. 향후에는 페인트 캔버스에서 색상 관리 지원이 추가될 수 있습니다.
dictionary {
PaintRenderingContext2DSettings boolean =
alpha true ; };
class MyPaint{ static get inputProperties() { return [ '--foo' ]; } static get inputArguments() { return [ '<color>' ]; } static get contextOptions() { return { alpha: true }; } paint( ctx, size, styleMap) { // 페인트 코드가 여기에 들어갑니다. } }
3. 개념
페인트 정의는
구조체로,
저자 정의 PaintWorkletGlobalScope
에 필요한 정보를 설명합니다. 이 정보는 <image>
(paint() 함수로 참조 가능)와 관련 있습니다. 구성 요소는 다음과 같습니다:
-
클래스 생성자 (class 생성자)
-
생성자 유효 플래그
-
입력 속성 (DOMString 리스트)
-
PaintRenderingContext2DSettings 객체
문서 페인트 정의는 구조체로, 저자 정의 문서에 필요한 정보를 설명합니다. 정보는 <image> 함수(paint 함수에서 참조 가능)와 관련됩니다. 구성 요소는 다음과 같습니다:
-
입력 속성 (DOMString 리스트)
-
PaintRenderingContext2DSettings 객체
4. 사용자 정의 페인트 등록
document에는 map 형태의 문서 페인트 정의가
있습니다. 처음에는
이 map이 비어 있으며, registerPaint(name, paintCtor)
가 호출될 때 채워집니다.
PaintWorkletGlobalScope
에는 map 형태의 페인트 정의가 있습니다. 처음에는 이 map이 비어 있으며, registerPaint(name, paintCtor)
가 호출될 때 채워집니다.
PaintWorkletGlobalScope
에는 map 형태의 페인트 클래스 인스턴스가 있습니다. 처음에는
이 map이 비어 있으며, 페인트 이미지를
그리기가 사용자 에이전트에 의해 호출될 때 채워집니다.
페인트 클래스 인스턴스 map에 있는 클래스 인스턴스들은 사용자 에이전트가 언제든지 해제하고 map에서 제거할 수 있습니다. 이는 <paint()> 함수가 더 이상 사용되지 않거나, 사용자 에이전트가 메모리를 회수해야 하는 경우에 발생할 수 있습니다.
오직 한 개의 엔진에서만 지원됩니다.
Opera?Edge79+
Edge (Legacy)미지원IE미지원
Firefox for Android미지원iOS Safari미지원Chrome for Android65+Android WebView65+Samsung Internet9.0+Opera Mobile?
registerPaint(name, paintCtor)
메서드가 호출되면, 사용자 에이전트는 다음 단계를 반드시 실행해야 합니다:
-
paintDefinitionMap을
PaintWorkletGlobalScope
의 페인트 정의 map으로 설정합니다. -
paintDefinitionMap[name] 존재하면, throw "
InvalidModificationError
"DOMException
을 발생시키고 모든 단계를 중단합니다. -
inputProperties를 빈
sequence<DOMString>
으로 설정합니다. -
inputPropertiesIterable을 Get(paintCtor, "inputProperties")의 결과로 설정합니다.
-
inputPropertiesIterable이 undefined가 아니면, inputProperties를 변환 결과로 설정합니다. inputPropertiesIterable을
sequence<DOMString>
으로 변환합니다. 예외가 발생하면 예외를 다시 발생시키고 모든 단계를 중단합니다. -
inputProperties를 지원되는 CSS 속성과 커스텀 속성만 포함하도록 필터링합니다.
참고: input properties getter로 제공되는 CSS 속성 목록은 커스텀 또는 기본 CSS 속성 모두 가능합니다.
참고: CSS 속성 목록에는 shorthand가 포함될 수 있습니다.
참고: paint 이미지 클래스가 미래 호환성을 위해, CSS 속성 목록은 현재 사용자 에이전트에 유효하지 않은 속성도
포함할 수 있습니다. 예시:
margin-bikeshed-property
.
-
inputArguments를 빈
sequence<DOMString>
으로 설정합니다. -
inputArgumentsIterable을 Get(paintCtor, "inputArguments")의 결과로 설정합니다.
-
inputArgumentsIterable이 undefined가 아니면, inputArguments를 변환 결과로 설정합니다. inputArgumentsIterable을
sequence<DOMString>
으로 변환합니다. 예외가 발생하면 예외를 다시 발생시키고 모든 단계를 중단합니다. -
inputArguments의 각 item에 대해 다음 하위 단계를 수행합니다:
-
consume a syntax definition을 item에서 시도합니다. 실패하면 throw TypeError를 발생시키고 모든 단계를 중단합니다. 성공하면 parsedSyntax를 반환된 syntax definition으로 설정합니다.
-
parsedSyntax를 inputArgumentSyntaxes에 추가합니다.
-
-
contextOptionsValue를 Get(paintCtor, "contextOptions")의 결과로 설정합니다.
-
paintRenderingContext2DSettings를 변환 결과로 설정합니다. contextOptionsValue를
PaintRenderingContext2DSettings
로 변환합니다. 예외가 발생하면 예외를 다시 발생시키고 모든 단계를 중단합니다.참고:
paintRenderingContext2DSettings.alpha
를false
로 설정하면 사용자 에이전트가 텍스트를 안티앨리어싱하는 것 외에도 "visibility" 최적화(예: paint 이미지가 불투명할 때 paint 이미지 뒤의 이미지를 그리지 않음)를 수행할 수 있습니다. -
IsConstructor(paintCtor)의 결과가 false이면, throw TypeError를 발생시키고 모든 단계를 중단합니다.
-
prototype을 Get(paintCtor, "prototype")의 결과로 설정합니다.
-
Type(prototype)의 결과가 Object가 아니면, throw TypeError를 발생시키고 모든 단계를 중단합니다.
-
paintValue를 Get(prototype, "paint")의 결과로 설정합니다.
-
paint를 변환 결과로 설정합니다. paintValue를 Function 콜백 함수 타입으로 변환합니다. 변환 중 발생하는 예외는 다시 발생시킵니다.
-
definition을 새로운 페인트 정의로 생성하며, 다음과 같이 구성됩니다:
-
클래스 생성자: paintCtor
-
paint 함수: paint
-
생성자 유효 플래그: true
-
입력 속성: inputProperties
-
PaintRenderingContext2DSettings 객체: paintRenderingContext2DSettings
-
-
paintDefinitionMap[name]을 definition으로 설정합니다.
-
테스크를 큐에 추가하여 다음 단계를 실행합니다:
-
documentDefinition을 새로운 문서 페인트 정의로 생성하며, 다음과 같이 구성됩니다:
-
입력 속성: inputProperties
-
입력 인자 문법: inputArgumentSyntaxes
-
PaintRenderingContext2DSettings 객체: paintRenderingContext2DSettings
-
-
documentPaintDefinitionMap[name] 존재하면, 다음 단계를 실행합니다:
-
existingDocumentDefinition을 get documentPaintDefinitionMap[name]의 결과로 설정합니다.
-
existingDocumentDefinition이
"invalid"
이면 모든 단계를 중단합니다. -
existingDocumentDefinition과 documentDefinition이 동등하지 않으면 (입력 속성, 입력 인자 문법, PaintRenderingContext2DSettings 객체가 다르면), 다음을 실행합니다:
documentPaintDefinitionMap[name]을
"invalid"
로 설정합니다.동일한 클래스가 다른
inputProperties
,inputArguments
,paintRenderingContext2DSettings
와 함께 등록되었다는 오류를 디버깅 콘솔에 기록합니다.
-
-
그렇지 않으면 documentPaintDefinitionMap[name]을 documentDefinition으로 설정합니다.
참고: 입력 속성 목록은 반드시 한 번만 조회되어야 하며, 클래스는 입력 속성을 동적으로 변경할 기회가 없습니다.
참고: 향후 명세 버전에서는 저자가 다른 타입의 RenderingContext를 받을 수 있는 기능이 추가될 수
있습니다. 저자가 3D 효과를 렌더링하기 위해 WebGL 렌더링 컨텍스트를 원할 수도 있습니다. WebGL 렌더링 컨텍스트를 PaintSize
와
StylePropertyMap
을
입력으로 받도록 설정하는 데는 복잡성이 있습니다.
5. 페인트 표기법
오직 한 개의 엔진에서만 지원됩니다.
Opera52+Edge79+
Edge (Legacy)미지원IE미지원
Firefox for Android미지원iOS Safari미지원Chrome for Android65+Android WebView65+Samsung Internet9.2+Opera Mobile47+
paint() = paint( <ident>, <declaration-value>? )
<paint()> 함수는 <image> 타입에서 추가로 지원되는 표기법입니다.
< style > . logo { background-image : paint ( company -logo ); } . chat-bubble { background-image : paint ( chat -bubble , blue ); } </ style >
cursor 속성에 대해서, <paint()> 함수는 잘못된 이미지로 취급되어 다음으로 지원되는 <image>로 대체됩니다.
computed value 시점에서는 <paint()> 함수가 registerPaint()
로
등록된 문법과 일치할 필요는 없습니다.
대신, 파싱이 페인트 이미지를 그리기
내부에서 발생할 때 잘못된 이미지가 됩니다.
6. 2D 렌더링 컨텍스트
[Exposed =PaintWorklet ]interface { };
PaintRenderingContext2D PaintRenderingContext2D includes CanvasState ;PaintRenderingContext2D includes CanvasTransform ;PaintRenderingContext2D includes CanvasCompositing ;PaintRenderingContext2D includes CanvasImageSmoothing ;PaintRenderingContext2D includes CanvasFillStrokeStyles ;PaintRenderingContext2D includes CanvasShadowStyles ;PaintRenderingContext2D includes CanvasRect ;PaintRenderingContext2D includes CanvasDrawPath ;PaintRenderingContext2D includes CanvasDrawImage ;PaintRenderingContext2D includes CanvasPathDrawingStyles ;PaintRenderingContext2D includes CanvasPath ;
참고: PaintRenderingContext2D
는 CanvasRenderingContext2D
API의 부분 집합만 구현합니다.
특히 CanvasImageData
,
CanvasUserInterface
,
CanvasText
,
CanvasTextDrawingStyles
API는 구현하지 않습니다.
PaintRenderingContext2D
객체는 출력 비트맵을 가집니다.
이 객체가 생성될 때 초기화됩니다.
출력 비트맵의 크기는 렌더링되는 객체의 구체적 객체
크기입니다.
PaintRenderingContext2D
객체는 alpha 플래그도 가지며,
true 또는 false로 설정할 수 있습니다.
컨텍스트가 생성될 때 기본적으로 alpha 플래그는 true로 설정되어야 합니다.
PaintRenderingContext2D
객체의 alpha 플래그가 false로 설정되면,
모든 픽셀의 alpha 채널은 1.0(완전히 불투명)으로 고정되고,
픽셀의 alpha 성분을 변경하는 시도는 조용히 무시되어야 합니다.
출력 비트맵의 크기는 반드시 내부적으로 또는 렌더링 시 실제 사용자 에이전트가 사용할 비트맵의 크기를 의미하지는 않습니다. 예를 들어, 시각적 뷰포트가 확대된 경우 사용자 에이전트는 좌표 공간의 디바이스 픽셀 수에 해당하는 비트맵을 내부적으로 사용할 수 있으므로 결과 렌더링 품질이 높아집니다.
또한 사용자 에이전트는 출력 비트맵에 적용된 그리기 작업의 순서를 기록하여, 이후 디바이스 비트맵에 올바른 해상도로 그릴 수 있게 할 수 있습니다. 이는 예를 들어 시각적 뷰포트가 확대되는 동안 출력 비트맵의 동일한 결과물을 반복적으로 재사용할 수 있게 합니다.
"currentColor"
가 PaintRenderingContext2D
API에서 색상으로 사용될 때,
불투명한 검정색으로 처리됩니다.
registerPaint( 'currentcolor' , class { paint( ctx, size) { ctx. fillStyle= 'currentColor' ; ctx. fillRect( 0 , 0 , size. width, size. height); } });
-
새
PaintRenderingContext2D
를 생성합니다. -
PaintRenderingContext2D
의 alpha 플래그를 paintRenderingContext2DSettings의alpha
값으로 설정합니다. -
새
PaintRenderingContext2D
를 반환합니다.
참고: 렌더링 컨텍스트의 초기 상태는 비트맵 크기 설정 알고리즘에서 설정되며, 여기서 렌더링 컨텍스트를 초기 상태로 리셋하고 출력 비트맵을 지웁니다.
6.1. CSSImageValue 그리기
CanvasImageSource
타입은 CSSImageValue
타입도 이미지 소스로 사용할 수 있도록 확장되었습니다.
CanvasDrawImage
믹스인을 사용하는 인터페이스의 경우:
-
CanvasImageSource
객체가CSSImageValue
를 나타내는 경우, 해당 값의 내부 이미지 알고리즘을 호출한 결과를drawImage
의 소스 이미지로 사용해야 합니다.
참고: 이것은 결국 HTML 명세의 canvas 섹션으로 이동되어야 합니다. 이슈 819를 참고하세요.
7. 이미지 그리기
<paint()> 함수 이미지를 box에 대해 시각적 뷰포트 내에 있는 경우, 사용자 에이전트는 반드시 draw a paint image 알고리즘을 호출하여 나온 이미지 출력을 표시해야 합니다.
참고: 사용자 에이전트는 시각적 뷰포트 내의 <paint()> 함수에 대해 매 프레임마다 draw a paint image를 실행할 필요는 없습니다. 결과를 캐시할 수 있고, (추가적인 무효화 단계를 사용할 수도 있음) 올바른 이미지 출력을 표시할 수 있습니다.
참고: 사용자 에이전트는 시각적 뷰포트 밖에 있는 이미지를 그리는 것을 선택적으로 지연시킬 수 있습니다.
requestAnimationFrame
내에서 스타일을 업데이트하는 경우
예시:
requestAnimationFrame( function () { element. styleMap. set( '--custom-prop-invalidates-paint' , 42 ); });
그리고 element
가 시각적 뷰포트 내에 있다면, 사용자 에이전트는 draw a paint image를 실행하고 현재 프레임에 대해 결과를 표시해야 합니다.
draw a paint image 함수는 object size negotiation 알고리즘 도중 사용자 에이전트에 의해 호출되며, 이는 <image> 렌더링을 담당하며, snappedConcreteObjectSize는 아래와 같이 정의됩니다. concreteObjectSize는 box의 구체적 객체 크기입니다. snappedConcreteObjectSize는 보통 concreteObjectSize와 동일합니다. 하지만 사용자 에이전트가 픽셀 경계에 맞춰 크기를 조정할 수도 있습니다. 이 경우, 사용자 에이전트는 원래 크기에서 비례적으로 snappedConcreteObjectSize를 조정해야 하며 <paint()> 함수가 그리기를 맞춰 조정할 수 있도록 해야 합니다.
object size negotiation 알고리즘의 목적상, 페인트 이미지는 자연 치수를 갖지 않습니다.
참고: 향후 명세 버전에서 저자가 페인트 이미지의 자연 치수를 지정할 수 있는 기능이 추가될 수 있습니다. 이는 콜백 형태로 노출되어 저자가 정적 자연 치수를 정의하거나, 계산된 스타일과 크기 변경에 따라 동적으로 자연 치수를 업데이트할 수 있게 될 것입니다.
PaintSize
객체는 저자가 그려야 할 이미지의 크기를 나타냅니다. 이는 사용자 에이전트가 제공한 snappedConcreteObjectSize입니다.
참고: CSS Images 3 § 4.4 CSS 객체 크기 예제에서 구체적 객체 크기가 어떻게 계산되는지 예시를 볼 수 있습니다.
draw a paint image 함수는 사용자 에이전트가 임의의 시점, 임의의 snappedConcreteObjectSize로 추측적으로 호출할 수 있습니다. 이렇게 생성된 이미지는 표시되지 않습니다.
참고: 사용자 에이전트는 snappedConcreteObjectSize의 미래 값을 추측하기 위해 어떤 휴리스틱을 사용해도 됩니다. 예를 들어 크기가 변하지 않을 것이라 추측할 수도 있습니다.
참고: 이미지는 표시되지 않더라도 캐시될 수 있고, 이후 <paint()> 함수의 호출에서 캐시된 이미지를 사용할 수 있습니다.
[Exposed =PaintWorklet ]interface {
PaintSize readonly attribute double ;
width readonly attribute double ; };
height
-
paintFunction을 그리려는 box의 <paint()> 함수로 설정합니다.
-
name을 paintFunction의 첫 번째 인자로 설정합니다.
-
documentPaintDefinitionMap[name]이 존재하지 않으면, 이미지 출력은 잘못된 이미지로 하고, 모든 단계를 중단합니다.
-
documentDefinition을 get documentPaintDefinitionMap[name]의 결과로 설정합니다.
-
documentDefinition이
"invalid"
이면, 이미지 출력을 잘못된 이미지로 하고, 모든 단계를 중단합니다. -
inputArgumentSyntaxes를 documentDefinition의 입력 인자 문법으로 설정합니다.
-
inputArguments를 리스트로, "paint name" 인자 이후의 paintFunction의 모든 인자로 설정합니다.
-
inputArguments가 등록된 문법 inputArgumentSyntaxes와 일치하지 않으면, 이미지 출력을 잘못된 이미지로 하고, 모든 단계를 중단합니다.
이 단계는 다음과 같은 경우 실패할 수 있습니다:// paint.js registerPaint( 'failing-argument-syntax' , class { static get inputArguments() { return [ '<length>' ]; } paint( ctx, size, styleMap, args) { /* paint code here. */ } }); < style > . example-1 { background-image : paint ( failing -argument-syntax , red ); } . example-2 { background-image : paint ( failing -argument-syntax , 1 px , 2 px ); } </ style > < div class = example-1 ></ div > < div class = example-2 ></ div > < script > CSS. paintWorklet. addModule( 'paint.js' ); </ script > example-1
은"red"
가 등록된 문법과 일치하지 않으므로 잘못된 이미지가 생성됩니다.example-2
는 함수 인자가 너무 많으므로 잘못된 이미지가 생성됩니다. -
workletGlobalScope를 PaintWorkletGlobalScope 중 하나로 설정합니다. (글로벌 스코프 선택 규칙은 § 7.1 글로벌 스코프 선택 참조)
사용자 에이전트는 이 시점에 페인트
Worklet
이 주어지면 임의로 워크렛 글로벌 스코프를 생성할 수도 있다. -
invoke a paint callback을 name, inputArguments, snappedConcreteObjectSize, workletGlobalScope로 실행합니다. 선택적으로 병렬로 실행할 수 있습니다.
참고: 만약 사용자 에이전트가 invoke a paint callback을 별도 스레드에서 병렬로 실행할 경우, 해당 스레드에서 사용할 수 있는 페인트 워클릿 글로벌 스코프를 선택해야 합니다.
-
paintDefinitionMap을 workletGlobalScope의 페인트 정의 map으로 설정합니다.
-
paintDefinitionMap[name]이 존재하지 않으면, 다음 단계를 실행합니다:
-
테스크를 큐에 추가하여 다음 단계를 실행합니다:
-
documentPaintDefinitionMap[name]을
"invalid"
로 설정합니다. -
사용자 에이전트는
PaintWorkletGlobalScope
에 클래스가 등록되지 않았음을 디버깅 콘솔에 오류로 표시해야 합니다.
-
이미지 출력을 잘못된 이미지로 하고, 모든 단계를 중단합니다.
참고: 이 단계는 어떤 페인트 워클릿 글로벌 스코프가
registerPaint(name, paintCtor)
를 받지 못한 경우를 처리합니다(다른 글로벌 스코프는 받았을 수 있음). 다른 글로벌 스코프에서 호출된 페인트 콜백은 성공할 수 있지만, 다음 프레임의 draw a paint image 호출에서는 성공하지 않을 수 있습니다. -
-
definition을 get paintDefinitionMap[name]의 결과로 설정합니다.
-
paintClassInstanceMap을 workletGlobalScope의 페인트 클래스 인스턴스 map으로 설정합니다.
-
paintInstance를 get paintClassInstanceMap[|name|]의 결과로 설정합니다. paintInstance가 null이면, 다음 단계를 실행합니다:
-
definition의 생성자 유효 플래그가 false이면, 이미지 출력을 잘못된 이미지로 하고, 모든 단계를 중단합니다.
-
paintCtor를 definition의 클래스 생성자로 설정합니다.
-
paintInstance를 Construct(paintCtor)의 결과로 설정합니다.
construct가 예외를 발생시키면, definition의 생성자 유효 플래그를 false로 설정하고, 이미지 출력을 잘못된 이미지로 하고, 모든 단계를 중단합니다.
-
paintClassInstanceMap[name]을 paintInstance로 설정합니다.
-
-
inputProperties를 definition의 입력 속성으로 설정합니다.
-
styleMap을
StylePropertyMapReadOnly
로 새로 생성하여, inputProperties에 나열된 속성들의 계산된 값만 포함시킵니다. -
renderingContext를 create a PaintRenderingContext2D object로 생성합니다:
-
"width" - snappedConcreteObjectSize가 제공한 너비
-
"height" - snappedConcreteObjectSize가 제공한 높이
-
"paintRenderingContext2DSettings" - definition이 제공한 PaintRenderingContext2DSettings 객체
참고: renderingContext는 paint 호출 간에 재사용되지 않습니다. 암묵적으로 renderingContext에는 저장된 데이터나 상태가 없습니다. 예를 들어 컨텍스트에 clip을 설정하고 다음 호출 때 같은 clip 적용을 기대할 수 없습니다.
참고: 암묵적으로 이는 paint 메서드가 끝나면 renderingContext가 사실상 "무효화"됨을 의미합니다. 저자 코드가 renderingContext 참조를 유지하고 메서드를 호출해도, 현재 이미지나 이후 이미지에 아무런 영향이 없습니다.
-
-
paintSize를
PaintSize
로 새로 생성하고, snappedConcreteObjectSize가 정의한 너비와 높이로 초기화합니다. -
이 단계에서는 paintSize, styleMap, inputArguments가 이전 호출과 동일하다면, 이전 호출의 이미지를 재사용할 수 있습니다. 이 경우, 이미지 출력을 캐시된 이미지로 하고 모든 단계를 중단합니다.
아래 예제에서div-1
과div-2
모두 자바스크립트 인자가 동일한 paint 함수를 사용합니다. 사용자 에이전트는 한 번의 호출 결과를 캐시해 두 요소에 모두 사용할 수 있습니다.// paint.js registerPaint( 'simple' , class { paint( ctx, size) { ctx. fillStyle= 'green' ; ctx. fillRect( 0 , 0 , size. width, size. height); } }); < style > . div-1 { width : 50 px ; height : 50 px ; background-image : paint ( simple ); } . div-2 { width : 100 px ; height : 100 px ; background-size : 50 % 50 % ; background-image : paint ( simple ); } </ style > < div class = div-1 ></ div > < div class = div-2 ></ div > < script > CSS. paintWorklet. addModule( 'paint.js' ); </ script > -
paintFunctionCallback을 definition의 paint 함수로 설정합니다.
-
Invoke paintFunctionCallback을 인자 «renderingContext, paintSize, styleMap, inputArguments»로 호출하며, paintInstance를 콜백 this 값으로 사용합니다.
paintFunctionCallback이 허용 가능한 시간 내에 완료되지 않으면(사용자 에이전트가 판단, 즉 "장시간 실행되는 스크립트") 사용자 에이전트는 스크립트를 종료하고, 이미지 출력을 잘못된 이미지로 하고, 모든 단계를 중단할 수 있습니다.
참고: 사용자 에이전트는 디버깅 도구 내에서 저자가 페인트 클래스를 얼마나 비용이 많이 드는지 보여주는 툴을 제공할 수 있습니다. 또한 "응답 없음 스크립트" 대화상자를 보여줄 수도 있습니다.
-
이미지 출력은 메서드에 전달된 renderingContext에서 생성됩니다.
참고: 결과 이미지의 내용은 접근성을 고려하여 설계되지 않았습니다. 저자는 표준 접근성 API를 통해 필요한 정보를 전달할 수 있습니다.
7.1. 글로벌 스코프 선택
사용자 에이전트가 페인트 PaintWorkletGlobalScope
를
paint Worklet
의
글로벌 스코프 리스트에서 선택해야 할 때 반드시 다음을 수행해야
합니다:
-
메모리 제약이 없는 한, 최소 두 개 이상의
PaintWorkletGlobalScope
중에서 선택합니다. -
동일한
PaintWorkletGlobalScope
를 연속으로 1000번 이상 재사용하지 않습니다.참고: 1000번 제한은 매우 높은 상한값으로 설정된 것이며, 향후 더 낮아질 수 있습니다.
참고: 이러한 규칙은 저자가 글로벌 객체나 클래스에 재생성 불가능한 상태를 저장하는 것에 의존하지 않도록 하기 위해 존재합니다. 자세한 내용은 worklets 명세의 코드 멱등성 논의를 참고하세요.
8. 예제
8.1. 예제 1: 색상 원
아래 예제는 <paint()> 함수가 애니메이션될 수 있다는 점을 활용합니다.
예를 들어, 아래 예제에서 textarea에 포커스가 가면 --circle-color
속성이 deepskyblue
에서
purple
로 전환됩니다.
이 기능은 단순 전환뿐 아니라 CSS 애니메이션, Web Animations API에도 적용됩니다.
<!DOCTYPE html> < style > # example { --circle-color : deepskyblue ; background-image : paint ( circle ); font-family : sans-serif ; font-size : 36 px ; transition : -- circle - color 1 s ; } # example : focus { --circle-color : purple ; } </ style > < textarea id = "example" > CSS is awesome.</ textarea > < script > CSS. registerProperty({ name: '--circle-color' , syntax: '<color>' , initialValue: 'black' , inherits: false }); CSS. paintWorklet. addModule( 'circle.js' ); </ script >
// circle.js registerPaint( 'circle' , class { static get inputProperties() { return [ '--circle-color' ]; } paint( ctx, geom, properties) { // 채우기 색상 변경 const color= properties. get( '--circle-color' ); ctx. fillStyle= color. cssText; // 중심점과 반지름 계산 const x= geom. width/ 2 ; const y= geom. height/ 2 ; const radius= Math. min( x, y); // 원 그리기 \o/ ctx. beginPath(); ctx. arc( x, y, radius, 0 , 2 * Math. PI, false ); ctx. fill(); } });
8.2. 예제 2: 이미지 플레이스홀더
저자는 이미지가 로딩 중일 때 paint를 사용하여 플레이스홀더 이미지를 그릴 수 있습니다.
<!DOCTYPE html> < style > # example { --image : url( '#someUrlWhichIsLoading' ); background-image : paint ( image -with-placeholder ); } </ style > < div id = "example" ></ div > < script > CSS. registerProperty({ name: '--image' , syntax: '<image> | none' , initialValue: 'none' , }); CSS. paintWorklet. addModule( 'image-placeholder.js' ); </ script >
// image-placeholder.js registerPaint( 'image-with-placeholder' , class { static get inputProperties() { return [ '--image' ]; } paint( ctx, geom, properties) { const img= properties. get( '--image' ); switch ( img. state) { case 'ready' : // 이미지가 로드됨! 이미지를 그림. ctx. drawImage( img, 0 , 0 , geom. width, geom. height); break ; case 'pending' : // 이미지가 로딩 중, 산을 그림. drawMountains( ctx); break ; case 'invalid' : default : // 이미지가 잘못됨(예: 로딩 실패), 슬픈 얼굴을 그림. drawSadFace( ctx); break ; } } });
8.3. 예제 3: 호
<!DOCTYPE html> < style > # example { width : 200 px ; height : 200 px ; background-image : paint ( arc , purple , 0.4 turn , 0.8 turn , 40 px , 15 px ), paint ( arc , blue , -20deg , 170 deg , 30 px , 20 px ), paint ( arc , red , 45 deg , 220 deg , 50 px , 10 px ); } </ style > < div id = "example" ></ div > < script > CSS. paintWorklet. addModule( 'arc.js' ); </ script >
// arc.js registerPaint( 'arc' , class { static get inputArguments() { return [ '<color>' , '<angle>' , // 시작 각도 '<angle>' , // 끝 각도 '<length>' , // 반지름 '<length>' , // 선 두께 ]; } paint( ctx, geom, _, args) { ctx. strokeStyle= args[ 0 ]. cssText; // 중심점 계산 const x= geom. width/ 2 ; const y= geom. height/ 2 ; // 시작/끝 각도를 라디안으로 변환 const startAngle= this . convertAngle( args[ 1 ]) - Math. PI/ 2 ; const endAngle= this . convertAngle( args[ 2 ]) - Math. PI/ 2 ; // 반지름과 선 두께를 px로 변환 const radius= this . convertLength( args[ 3 ]); const lineWidth= this . convertLength( args[ 4 ]); ctx. lineWidth= lineWidth; ctx. beginPath(); ctx. arc( x, y, radius, startAngle, endAngle, false ); ctx. stroke(); } convertAngle( angle) { switch ( angle. unit) { case 'deg' : return angle. value* Math. PI/ 180 ; case 'rad' : return angle. value; case 'grad' : return angle. value* Math. PI/ 200 ; case 'turn' : return angle. value* Math. PI/ 0.5 ; default : throw Error( `알 수 없는 각도 단위: ${ angle. unit} ` ); } } convertLength( length) { switch ( length. type) { case 'px' : return length. value; default : throw Error( `알 수 없는 길이 타입: ${ length. type} ` ); } } });
8.4. 예제 4: 크기에 따라 다른 색상
< h1 > Heading 1</ h1 > < h1 > Another heading</ h1 > < style > h1 { background-image : paint ( heading -color ); } </ style > < script > CSS. paintWorklet. addModule( 'heading-color.js' ); </ script >
// heading-color.js registerPaint( 'heading-color' , class { static get inputProperties() { return []; } paint( ctx, geom, properties) { // 이미지의 너비와 높이에 따라 색상 선택 const width= geom. width; const height= geom. height; const color= colorArray[( width* height) % colorArray. length]; // 단색 이미지만 그림 ctx. fillStyle= color; ctx. fillRect( 0 , 0 , width, height); } });
8.5. 예제 5: 요소 영역 밖 그리기
border-image 속성을 사용하면 요소 영역 밖에 그릴 수 있습니다.
< style > # overdraw { --border-width : 10 ; border-style : solid ; border-width : calc( var ( --border-width ) * 1 px ); border-image-source : paint ( overdraw ); border-image-slice : 0 fill ; border-image-outset : calc( var ( --border-width ) * 1 px ); width : 200 px ; height : 200 px ; } </ style > < div id = "overdraw" ></ div > < script > CSS. paintWorklet. addModule( 'overdraw.js' ); </ script >
// overdraw.js registerPaint( 'overdraw' , class { static get inputProperties() { return [ '--border-width' ]; } paint( ctx, geom, properties) { const borderWidth= parseInt( properties. get( '--border-width' )); ctx. shadowColor= 'rgba(0,0,0,0.25)' ; ctx. shadowBlur= borderWidth; ctx. fillStyle= 'rgba(255, 255, 255, 1)' ; ctx. fillRect( borderWidth, borderWidth, geom. width- 2 * borderWidth, geom. height- 2 * borderWidth); } });
9. 보안 고려사항
이 기능들에 의해 새롭게 도입된 알려진 보안 문제는 없습니다.
10. 개인정보 보호 고려사항
-
페인트 콜백의 타이밍은 링크의 "방문됨" 상태를 감지하는 고대역폭 채널로 사용될 수 있습니다. (자세히 보기) 이는 근본적으로 새로운 개인정보 유출은 아니며, 방문 상태는 다양한 상호작용에서 누출될 수 있지만, 추가적인 완화책이 없다면, 이 방식은 정보 유출에 있어 특히 고대역폭 채널입니다.
현재로서는 공식적인 완화책이 계획되어 있지 않습니다. 이 개인정보 유출은 모든 채널을 해결하기 위해 좀 더 직접적으로 접근할 필요가 있습니다.
11. 변경 사항
2018년 8월 9일 CR 발표 이후의 변경 사항:
-
페인트 워클릿에 입력되는 속성 목록을 알려진 또는 사용자 정의 속성으로만 필터링했습니다.
-
PaintRenderingContext2D
에 알파 플래그를 추가하여 렌더링 표면을 강제로 불투명하게 하거나 투명성을 허용할지 제어할 수 있도록 했습니다. -
출력 비트맵의 크기 정의를 수정했습니다:
출력 비트맵의 크기는 렌더링되는 객체의 구체적인 객체 크기
렌더링되는 프래그먼트의 크기입니다.