Image fragmentation effect

The previous article used CSS masks and custom properties (@Property) to create a shard effect. This time we use a new API: CSS Painting API to achieve the same effect.

First, what is the CSS Painting API: part of the CSS Houdini APIs that allow developers to write JavaScript functions that draw directly into the background, borders, and content of a Painting element

CSS Houdini:

Houdini is a set of low-level apis that expose parts of the CSS engine so that developers can extend CSS by adding styles and layout processes to the browser rendering engine. In layman's terms, the entire process was rendered directly by the browser and displayed on a page. Now the browser provides a set of apis that allow developers to access the CSS object Model (CSSOM) when appropriate. Developers can write code that the browser interprets into CSS to create new CSS functionality. Instead of waiting for the browser's own vendor implementation... Let's stop here and get back to business.Copy the code

Next step: How to use the CSS Painting API?

It is part of the CSS Houdini APIs and will eventually be parsed into CSS using an unconventional solution. Here's how to use it:

  1. useregisterPaint()To define apaint worklet
  2. Registered definedworklet
  3. On thepaint()In the

Now that we know how to use it, we can make a change: we can use the CSS Paint API to create masks instead of using SCSS to generate too much code.

HTML code:

<! -- n*m represents the total number of masks -->
<img src="https://z3.ax1x.com/2021/08/18/f7Uya4.png" style="--f-n:100; --f-m:100;">
Copy the code

The CSS code:

// Same principle as last time@property --f-o{
    syntax: '<number>';
    inherits: false;
    initial-value: 1;
  }

  img {
    width: 500px;
    height: 400px;
    -webkit-mask: paint(fragmentation);
    --f-o:1;
    transition:--f-o 1s;
  }

  img:hover {
    --f-o:0;
  }
Copy the code

Js code:

// index.html
if (CSS.paintWorklet) {
    CSS.paintWorklet.addModule('./index.js');
  }

// index.js
registerPaint('fragmentation'.class  {
		// inputProperties() can return all attributes that affect an element, not just custom attributes
    static get inputProperties() {
        return [
            '--f-n'.'--f-m'.'--f-o']}paint(ctx, size, properties) {

        const n = properties.get('--f-n');
        const m = properties.get('--f-m');
        const o = properties.get('--f-o');
        const w = size.width/n;
        const h = size.height/m;
        const l = 10;

        const mask = 0xffffffff;
        const seed = 30;
        let m_w  = (123456789 + seed) & mask;
        let m_z  = (987654321 - seed) & mask;
        let random =  function() {
            m_z = (36969 * (m_z & 65535) + (m_z >>> 16)) & mask;
            m_w = (18000 * (m_w & 65535) + (m_w >>> 16)) & mask;

            var result = ((m_z << 16) + (m_w & 65535)) > > >0;
            result /= 4294967296;
            return result;
        }

        for(var i=0; i<n; i++) {for(var j=0; j<m; j++) { ctx.fillStyle ='rgba(0,0,0,'+((random()*(l-1) + 1) - (1-o)*l)+') '; ctx.fillRect(i*w, j*h, w, h); }}}})Copy the code

The overall idea remains the same, and the gradual change of N *m masks is still used to achieve the fragmentation effect. The difference is that the amount of code is reduced. In fact, we use Houdini API to generate the previous repeated code with JS code

For details on how to use the CSS painting API: ->

The paint() function has three arguments: - drawing Context - paint size-properties We can also use the paint() functionclassInternal custom propertiesCopy the code

The end result is the same as before

In fact, the idea is the same. The key lies in the algorithm of mask generation, so a different algorithm can achieve different effects:

The CSS code:

/ / to the index.htmlThe index of the.js// index2.js
import Delaunator from 'https://cdn.skypack.dev/delaunator@5.0.0'; registerPaint('fragmentation', class { static get inputProperties() { return [ '--f-n', '--f-m', '--f-o' ] } paint(ctx, size, properties) { const n = properties.get('--f-n'); const o = properties.get('--f-o'); const w = size.width; const h = size.height; const l = 7; Var dots = [[0, 0], [0, w], [h, 0], [w, h]]. /* we always include the corners */ /* we generate N random points within the area of the element */ for (var i = 0; i < n; i++) { dots.push([Math.random() * w, Math.random() * h]); } /**/ /* We call Delaunator to generate the triangles*/ var delaunay = Delaunator.from(dots); var triangles = delaunay.triangles; /**/ for (var i = 0; i < triangles.length; i += 3) { /* we loop the triangles points */ /* we draw the path of the triangles */ ctx.beginPath(); ctx.moveTo(dots[triangles[i]][0] , dots[triangles[i]][1]); ctx.lineTo(dots[triangles[i + 1]][0], dots[triangles[i + 1]][1]); ctx.lineTo(dots[triangles[i + 2]][0], dots[triangles[i + 2]][1]); ctx.closePath(); /**/ var alpha = (Math.random()*(l-1) + 1) - (1-o)*l; /* the alpha value */ /* we fill the area of triangle with the semi-transparent color */ ctx.fillStyle = 'rgba(0.0.0.'+alpha+')'; /* we consider stroke to fight the gaps */ ctx.strokeStyle = 'rgba(0.0.0.'+alpha+')'; ctx.stroke(); ctx.fill(); }}})Copy the code

Of course, it can also be 🪜, ⭕️, ⭐ ⭕…