As one of the apis exposed by the CSS Houdini, the CSS Paint API has been much anticipated since its inception. It extends some of the capabilities of CSS to make it more expressive. This article will introduce one by one from its definition to application, so that we have a more profound understanding of it.

The API is introduced

A brief introduction to Houdini.

The word Houdini is a reference to Harry Houdini, a famous magician of the 20th century, who is also known as the greatest magician, escape artist and superperformer in history.

Houdini is a set of low-level apis that expose part of the CSS engine and give developers the ability to intervene in browser rendering and layout to extend CSS.

As stated in the definition, the Paint API was created for the Paint part of the browser rendering process.

It is important to note that most of the apis exposed at this stage, including the Paint API that will be introduced next, are still in draft or Level 1 phase.

The W3C defines the Paint API as:

The CSS Paint API allows developers to use JavaScript to create CSS images that are adaptive to changes in style and size. Images created this way can be applied to properties such as background-image, border-image, and mask-image by calling paint().

Simply put, CSS now allows us to draw our own images, in addition to solid colors, gradients, and external links.

.box{
 background-image: paint(custom-image)
}
Copy the code

As usual, if you look at the support of the Paint API, you can see that the support of the Paint API is relatively good compared to other apis

For specific usage, please refer to the following template:

  1. Create a JS file and register a PaintWorklet using the registerPaint() method

  2. Call the addModule() method to add this module

  3. Use the paint() function call in CSS

Below we according to just said process, simple actual combat. Instead of painting the Mona Lisa, let’s create a starry backdrop and get familiar with the Paint API.

API application

Create a starry. Js and write the frame in the format above.

/* starry.js */
registerPaint('starry'.class {
    paint(ctx, geom, properties) {
    // code here}})Copy the code

The paint() method, which is also used to draw a custom image core, takes three parameters.

The first parameter is the context of the current drawing area. Its full name is PaintRenderingContext2D, which may look familiar to those familiar with Canvas. Yeah, it’s a subset of CanvasRenderingContext2D.

The second argument is an object containing the width and height of the currently drawn region, which is read-only.

The third parameter can be used to get CSS properties. With CSS variables or other apis, we can achieve richer functionality. We will see a more specific use of this parameter in the following examples.

With the parameters introduced, let’s refine the paint() method.

You can do it the same way you do it in the canvas.

  • Start by drawing a gradient of sky
drawSky(ctx, geom) {
    const { height, width } = geom
    // Draw a rectangular background
    ctx.rect(0.0, width, height)
    ctx.stroke()

    // Add a gradient night sky to the rectangular background
    const skyGradient = ctx.createLinearGradient(0.0, width, height);
    skyGradient.addColorStop(0.'#3323CB')
    skyGradient.addColorStop(1.'#090A55')
    ctx.fillStyle = skyGradient
    ctx.fill()
}
Copy the code
  • Then finish drawing a star
drawStar(ctx) {
  const starColor = '#FFE900'
  const x = 10
  const y = 10
  const r = 5
  
  ctx.translate(x, y)
  ctx.scale(r, r);

  ctx.beginPath()
  // Draw the pentacle
  for (var i = 0; i < 5; i++) {
    ctx.lineTo(Math.cos((18 + i * 72) / 180 * Math.PI),
      -Math.sin((18 + i * 72) / 180 * Math.PI));
    ctx.lineTo(Math.cos((54 + i * 72) / 180 * Math.PI) * 0.5,
      -Math.sin((54 + i * 72) / 180 * Math.PI) * 0.5);
  }
  ctx.closePath();

  // Fill the color
  ctx.fillStyle = starColor
  ctx.fill()

}

Copy the code

Of course, the sky doesn’t have just one star. We can draw more stars and make them appear randomly.

drawVariousStars(ctx, geom) {
  const { width, height } = geom
  const Num = 10 / / the number of
  // Random display of stars
  for (var i = 0; i < Num; i++) {
    var r = Math.random() * 5 + 5;
    var x = Math.random() * width;
    var y = Math.random() * height * 0.65;
    var a = Math.random() * 360;
    this.drawStar(ctx, r, x, y, a); }}Copy the code

The rest of the reference frame can be written, let’s see the effect.

At this point, I’m sure you’re familiar with the basic capabilities of the Paint API, but we seem to be using it to do things we can do with Canvas and other CSS capabilities, and it’s not “futuristic” at all.

Remember the first sentence of the article: “As one of the apis exposed by CSS Houdini…” . The Paint API might look mediocre on its own, but with other apis, it’s much more powerful.

Let’s look at the example above.

If you want to render two different sets of sky, you may need to write a starry2.js.

Next we work with another Houdini API — Properties and Values API — to change the sky through configuration.

The advanced

Also, a brief introduction to the Properties and Values API.

The CSS Properties and Values API allows developers to customize CSS Properties and set Properties such as type verification, default Values, and inheritance.

When you see the sentence “let developers customize CSS properties”, I believe many students will immediately think of CSS variables. Yes, the Properties and Values API is similar to CSS variables in this respect, but as mentioned in the definition, it can also set Properties such as type validation, default Values, etc. to let the browser know what to do with the variable.

You can register it in JS or CSS.

<script>
window.CSS.registerProperty({
  name: '--sky-color-start'./ / variable names
  syntax: '<color>'./ / type
  inherits: false.// Whether to inherit
  initialValue: '#fff' / / the default value
})
</script>
Copy the code
<style>
@property --sky-color-start {
  syntax: '<color>';
  inherits: false;
  initial-value: #fff;
}
</style>
Copy the code

Inherits and initial-value, as defined in the definition, indicate whether the variable inherits with a default value. The only possible thing to notice is the syntax attribute, which represents the type of the variable and does type checking on the variable. The value can be length, length-percentage, Angle, time, resolution, INTEGER, number, percentage,color, custon-ident, URL,image… Any type you can think of.

Now we register variables that were previously written in the code in this way.

<style> // Sky gradient color@property --sky-color-start {
  syntax: '<color>';
  inherits: false;
  initial-value: #090A55; } // Sky gradient color@property --sky-color-end {
  syntax: '<color>';
  inherits: false;
  initial-value: #3323CB; } // Star color@property --star-color {
  syntax: '<color>';
  inherits: false;
  initial-value: #FFE900; } // Number of stars@property --star-num {
  syntax: '<number>';
  inherits: false;
  initial-value: 10;
}
</style>
Copy the code

You can then get the defined CSS variable in starry. Js by passing the third argument to the paint() method

registerPaint('gradient-border'.class {
  // Get the variable in paint() via Properties
  static get inputProperties() {
    return [
      '--sky-color-start'.'--sky-color-end'.'--star-color'.'--star-num']}paint(ctx, geom, properties){
    console.log(properties.get('--sky-color-start').toString()) // 'rgb(9, 10, 85)'
    console.log(+properties.get('--star-num')) / / 10
  }

Copy the code

Since it is a CSS variable registered through the Properties and Values API, the browser knows how to handle its special behavior, so we now implement a hover function to change the sky: We only need to change the value of the CSS variable to hover, and with transition, we can achieve a more user-friendly transition experience.

.starry {
  background-image: paint(starry);
  transition: --sky-color-start 1sease-in-out; // hover to modify the defined variable &:hover {
    --sky-color-start: #f9f8ff;
    --sky-color-end: #bdefff;
    --star-color: transparent; }}Copy the code

Write in the last

Students who often pay attention to the dynamic CSS may have such a feeling: “this CSS feature is very practical, XX requirements (style) finally do not need to hack”, but the compatibility of the heart is cool, knowledge has not been able to apply into the reserve, do not know when can be used. Compared to JavaScript, which can be polyfilled, CSS in the so-called front Three musketeers seems to have been dormant for too long.

Houdini is a way to break the ice. Today, I used a small example to introduce the use of CSS Panit API. With it, in the past, when some designers need to provide cutting scenes, we can confidently say “I can do this by myself”, and also let us have more solutions in the face of different demand scenarios.

Not only that, but being part of Houdini makes it possible for any front-end engineer who loves design and creation. In addition to Paint API, Properties and Values API, there are Layout API, Typed OM, Font Metrics API, Box Tree API and many other capabilities waiting for us to develop. Those of you who are interested can learn more at the references link at the end of this article.

Refer to the link

Houdini is currently supported

Houdini W3C draft document

Houdini implementation cases

Introduction to the CSS Paint API

New horizons in CSS: Houdini and the Paint API

Using the CSS Painting API