CSS Paint API

The CSS Paint API is a powerful feature in modern CSS that allows you to create custom graphics and visual effects directly in CSS. It provides a way to dynamically generate image data for use in properties like background-image, border-image, and mask-image.

Understanding CSS Paint API Basics

Before diving into advanced techniques, let’s understand the basic concepts of the CSS Paint API.

  • Custom Properties (CSS Variables):

Custom properties, also known as CSS variables, are used to define reusable values in CSS. They are defined using the -- prefix and can be referenced throughout your CSS.

				
					/* Define custom properties */
:root {
  --primary-color: #007bff;
}

/* Use custom properties */
.element {
  color: var(--primary-color);
}

				
			
  • Paint Worklets:

Paint worklets are JavaScript modules that define custom paint functions. These functions generate image data to be used in CSS properties.

  • CSS Paint API Syntax:

To use the CSS Paint API, you need to define a custom paint function using a paint worklet. Here’s the basic syntax:

				
					registerPaint('paint-name', class {
  static get inputProperties() { return ['--property']; }

  paint(ctx, size, props) {
    // Paint logic here
  }
});

				
			

In this syntax:

  1. paint-name is the name of the custom paint function.
  2. inputProperties specifies the custom properties that the paint function depends on.
  3. ctx is the canvas rendering context.
  4. size is the size of the painted area.
  5. props contains the computed values of the custom properties.

Creating Custom Paint Functions

Now, let’s create some custom paint functions using the CSS Paint API.

  • Example 1: Gradient Border
				
					registerPaint('gradient-border', class {
  static get inputProperties() { return ['--border-color', '--border-width']; }

  paint(ctx, size, props) {
    const color = props.get('--border-color').toString();
    const width = parseInt(props.get('--border-width').toString());

    ctx.strokeStyle = color;
    ctx.lineWidth = width;
    ctx.strokeRect(0, 0, size.width, size.height);
  }
});

				
			
				
					.element {
  --border-color: linear-gradient(to right, red, blue);
  --border-width: 5px;
  border-image: paint(gradients);
}

				
			

In this example:

  1. We define a custom paint function named gradient-border.
  2. It takes two input properties: --border-color and --border-width.
  3. The paint function draws a rectangle with a gradient border using the provided color and width.
  • Example 2: Pattern Background
				
					registerPaint('pattern-background', class {
  paint(ctx, size, props) {
    const pattern = ctx.createPattern(image, 'repeat');
    ctx.fillStyle = pattern;
    ctx.fillRect(0, 0, size.width, size.height);
  }
});

				
			
				
					.element {
  background-image: paint(pattern-background);
}

				
			

In this example:

  1. We define a custom paint function named pattern-background.
  2. The paint function fills the entire area with a pattern created from an image.

Advanced Techniques with CSS Paint API

  • Dynamic Shapes
				
					registerPaint('dynamic-shapes', class {
  static get inputProperties() { return ['--shape']; }

  paint(ctx, size, props) {
    const shape = props.get('--shape').toString();

    if (shape === 'circle') {
      ctx.beginPath();
      ctx.arc(size.width / 2, size.height / 2, Math.min(size.width, size.height) / 2, 0, 2 * Math.PI);
      ctx.fillStyle = 'red';
      ctx.fill();
    } else if (shape === 'square') {
      ctx.fillStyle = 'blue';
      ctx.fillRect(0, 0, size.width, size.height);
    }
  }
});

				
			
				
					.element {
  --shape: circle;
  background-image: paint(dynamic-shapes);
}

				
			

In this example:

  1. We define a custom paint function named dynamic-shapes.
  2. It takes an input property --shape to determine the shape to be drawn.
  3. The paint function dynamically draws either a circle or a square based on the provided shape.
  • Animated Background
				
					registerPaint('animated-background', class {
  static get inputProperties() { return ['--animation-time', '--animation-type']; }

  paint(ctx, size, props) {
    const animationTime = parseFloat(props.get('--animation-time').toString());
    const animationType = props.get('--animation-type').toString();

    const gradient = ctx.createLinearGradient(0, 0, size.width, size.height);
    gradient.addColorStop(0, 'red');
    gradient.addColorStop(0.5, 'green');
    gradient.addColorStop(1, 'blue');

    if (animationType === 'horizontal') {
      const offset = (performance.now() / animationTime) % size.width;
      gradient.x0 = offset;
      gradient.x1 = offset + size.width;
    } else if (animationType === 'vertical') {
      const offset = (performance.now() / animationTime) % size.height;
      gradient.y0 = offset;
      gradient.y1 = offset + size.height;
    }

    ctx.fillStyle = gradient;
    ctx.fillRect(0, 0, size.width, size.height);
  }
});

				
			
				
					.element {
  --animation-time: 2000ms; /* 2 seconds */
  --animation-type: horizontal;
  background-image: paint(animated-background);
}

				
			

In this example:

  1. We define a custom paint function named animated-background.
  2. It takes two input properties: --animation-time (duration of animation) and --animation-type (direction of animation).
  3. The paint function creates a gradient that smoothly transitions between red, green, and blue colors.
  4. Depending on the animation type (horizontal or vertical), the gradient’s start and end points are dynamically adjusted to create the animation effect.
  • Image Filters
				
					registerPaint('image-filters', class {
  paint(ctx, size, props) {
    const imageUrl = props.get('--image-url').toString();
    const filter = props.get('--filter').toString();

    const image = new Image();
    image.src = imageUrl;

    image.onload = () => {
      ctx.filter = filter;
      ctx.drawImage(image, 0, 0, size.width, size.height);
    };
  }
});

				
			
				
					.element {
  --image-url: url('example.jpg');
  --filter: grayscale(100%);
  background-image: paint(image-filters);
}

				
			

In this example:

  1. We define a custom paint function named image-filters.
  2. It takes two input properties: --image-url (URL of the image) and --filter (CSS filter to apply).
  3. The paint function loads the specified image and applies the specified CSS filter to it.
  4. This allows for dynamic application of image filters directly in CSS, without the need for external image editing tools.

The CSS Paint API is a powerful tool for creating custom graphics and visual effects directly in CSS. By understanding its basic concepts and exploring advanced techniques, you can enhance the visual appeal of your web projects and achieve unique design effects without relying on external image resources. Experiment with different paint functions and unleash your creativity to create stunning visual experiences on the web. Happy Coding! ❤️

Ad Block