Skip to content

Instantly share code, notes, and snippets.

@appsforartists
Last active July 19, 2018 20:16
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save appsforartists/e5d2a4b7826bf5962fad142bcd255465 to your computer and use it in GitHub Desktop.
Save appsforartists/e5d2a4b7826bf5962fad142bcd255465 to your computer and use it in GitHub Desktop.
Snippets showing how to use -webkit-canvas to make a paint worklet work in Safari
/** @license
* Copyright 2016 - 2017 Google LLC. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import {
CSSProperty,
myPainter,
inputProperties,
} from './myPainter.mjs';
// We have an element whose background is either the Paint Worklet or
// -webkit-canvas.
setPainter = (element: HTMLElement) => {
if (element) {
this.element = element;
element.style.setProperty('background-image', 'paint(my-worklet-name)');
if (typeof paintWorklet === 'undefined' && document.getCSSCanvasContext) {
// TODO: update the dimensions on resize
this.dimensions = {
width: element.clientWidth,
height: element.clientHeight,
};
this.webkitCanvas = document.getCSSCanvasContext(
'2d',
'my-worklet-name',
devicePixelRatio * this.dimensions.width,
devicePixelRatio * this.dimensions.height,
);
this.webkitCanvas.scale(devicePixelRatio, devicePixelRatio);
element.style.setProperty('background', '-webkit-canvas(my-worklet-name)');
// ensure the canvas dimensions match the element dimensions:
element.style.setProperty('background-size', 'contain');
this.repaint();
}
}
}
repaint() {
if (this.webkitCanvas) {
// Our painter has the same API as a Paint Worklet. We just pass it our
// -webkit-canvas instead of the one we'd get from the worklet.
myPainter(
this.webkitCanvas,
this.dimensions,
styleMapFromElement(inputProperties, this.element)
);
}
}
// We put our painted element inside a container, where we set custom
// properties. In a React component, this happens inside
// `componentWillReceiveProps`.
//
// Note:
// -webkit-canvas doesn't automatically repaint when the properties change,
// so you have to manually repaint whenever you want an update:
componentWillReceiveProps({ someProperty = 16 }: Props) {
this.containerElement.style.setProperty(CSSProperty.SOME_PROPERTY, someProperty + 'px');
this.repaint();
}
declare interface CSSStyleValue {
cssText: string,
}
/**
* This is just a quick snippet to provide the painter with the same API that it
* would get from the worklet.
*
* Note: it allocates a new Map and Array every call, as well as reading a bunch
* of style properties - it probably isn't as performant as it should be.
*/
export function styleMapFromElement(inputProperties: Array<string>, element: HTMLElement): Map<string, CSSStyleValue> {
const style = getComputedStyle(element);
return new Map(
Array.from(
inputProperties,
key => [
key,
style.getPropertyValue(key)
]
) as Array<[string, CSSStyleValue]>
);
}
export default styleMapFromElement;
/** @license
* Copyright 2016 - 2017 Google LLC. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
// List all the custom properties that you want as inputs to your painter
// here.
export const CSSProperty = {
SOME_PROPERTY: '--some-property',
};
export const inputProperties = Object.values(CSSProperty);
// Notice that this API has the same shape as a paint worklet. myWorklet
// passes its arguments straight through. MyComponent calls this function
// to repaint the -webkit-canvas when it goes stale.
export function myPainter(context, dimensions, styleMap) {
// ... paint stuff
}
/** @license
* Copyright 2016 - 2017 Google LLC. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import {
myPainter,
inputProperties,
} from './myPainter.mjs';
// This is a very simple shim that passes its argument as-is to our paint
// function
registerPaint(
'my-worklet-name',
class {
static get inputProperties() {
return inputProperties;
}
paint(context, dimensions, styleMap) {
myPainter(context, dimensions, styleMap);
}
}
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment