Smart use of CSS to mosaic pictures

Python WeChat Subscription Applet Course Video

Python Actual Quantitative Transaction Finance System

1. Introduction to image-rendering

An interesting feature in CSS is called image-rendering It can use algorithms to display the scaled pictures better.

Suppose we have a smaller two-dimensional code capture (bottom left) that is magnified 10 times and the image will be blurred (bottom right):

Add the image-rendering: pixelated feature to the enlarged image, and the CSS will pixelize it using an algorithm to make its image outline sharper edges:

This feature is well suited for use on single-color, well-defined pictures that need to be enlarged to create a sense of sight (reduce distortion after enlargement) of pseudo-vectors.

For colorful and detailed photos, image-rendering: pixelated creates a mosaic look when used:

This is a long way from the mosaic effect that the title of this article would like to achieve - currently the picture needs to be enlarged to show the effect, and we want to cover the image with a mosaic of equal size while preserving the original size.

However, the image-rendering feature will not work for elements whose dimensions are not scaled.

2. Realization of Mosaic with Dimensions such as Trench

The principle of equal-sized mosaics is to blur a picture and then sharpen it to get small squares.

image-rendering: pixelated helps us achieve the sharpening step, and we have to think about how to achieve blurring.

The blurring scheme of using a filter first is not feasible because image-rendering is strongly correlated with the image zoom factor, so you should think about how you can take advantage of the picture's zoom ability.

One thing to say here is that the pictures on the WEB look like smart objects in Photoshop - you can resize them at will (for example, zoom in many times to blur them), but if you finally change them back to their original size, they will look the same (without any distortion).

How to preserve the "blurred" information of enlarged pictures is a priority problem.

Smart buddies have come up with the idea that they can try canvas, because canvas can easily get and draw images, and the information drawn is pure data, not graphic objects, so zooming in and out of the data will distort (to the original size), which is exactly what we want.

But there are also pits:

  • The information displayed by the external image is processed by the image-rendering: pixelated algorithm, and canvas cannot get it because that is what the display layer does. Canvas still gets raw image content that is not sharpened and blurred;
  • It doesn't make sense to add image-rendering: pixelated to canvas without scaling itself.

This means that you can't zoom in and sharpen the picture outside the canvas, then write the canvas to zoom out (and iterate over) to get the sharpened original size picture.

3. Interesting canvas stretching

To address these issues, let's first look at an interesting feature of canvas.

If we define width and height in the canvas tag:

<canvas width="100" height="50" >canvas>

It also defines another width and height in the style for canvas:

canvas {
  width: 200px;
  height: 200px;

So what size will canvas be displayed in?

The answer is to display the size of the CSS, but the content size of the canvas will be based on the width and height defined in the canvas label. This means that although we are looking at a canvas of 200px * 200px, its contents are actually stretched (width is stretched twice, height is stretched four times).

Note: Canvas on left and original on right

This is also canvas as Replaceable elements One feature - CSS cannot modify its content. Imagine that if CSS could dynamically modify the size of the canvas content, it would mean that the canvas content would be clipped off, or that there would be more white space, which is obviously undesirable. Therefore, it is a reasonable browser behavior for canvas to zoom in to the size specified by the style while preserving the integrity of the content.

With this feature of canvas, we can achieve equal-size mosaics in this way:

  • Create a canvas, specify its width and height by style, and set the image-rendering: pixelated feature;
  • Calculate the optimal display size of the picture (displayed in a form similar to background-size: contain);
  • Set the canvas width and height (non-style) to 1/N of the style width and height;
  • Draw an image with a width and height of 1/N of the optimal display size.

In this way, we actually draw an image with a size of only 1/N of the optimal size, and then zoom in with the N-power of canvas to return to the optimal visual size. Because the image is drawn by canvas, it will remain blurred when zoomed back to the optimal size to meet the matching requirements of image-rendering.

Note: The "best size" mentioned here refers to the best size corresponding to the "Ensure full display image" in step 2, not the original size of the picture.

4. Code implementation

We follow the steps above to write the corresponding code, but of course we want to be more flexible, such as the N above, which can be customized by users. In addition, the code in this chapter can Get on Github.

HTML section

Mainly for the control to select pictures, canvas, easy canvas to get images! [] (), text boxes for user-defined zoom multiples, execution buttons:

  <input id="file" type="file" accept="image/*" />
  <canvas id="canvas">canvas>
  <img id="img-raw" />
  <label for="compress-times">Compression multiples: label>
  <input id="compress-times" type="number" value="12">
  <button>Mosaicization button>

CSS section

Styles dictate the canvas's appearance size and configure the image-rendering: pixelated feature. In addition! The [] () tag is just an intermediary that passes the user's selected picture to the canvas and can be directly hidden:

    canvas {
      display: block;
      border: gray solid 1px;
      width: 600px;
      height: 600px;
      image-rendering: pixelated;

    img {
      display: none;

JS section

    let imgBlobUrl;
    const file = document.getElementById('file');
    const img = document.getElementById('img-raw');
    const compressTimes = document.getElementById('compress-times');
    const defaultCompressTimes = compressTimes.value | 0;
    const canvas = document.getElementById('canvas');
    const button = document.querySelector('button');

    const boundingRect = canvas.getBoundingClientRect();
    const ctx = canvas.getContext('2d');
    const canvas_w = boundingRect.width;
    const canvas_h = boundingRect.height;

    // Set picture size as background-size: contain
    function matchImgSizeToCanvas(imgElem = img) {
      let w = imgElem.width;
      let h = imgElem.height;
      if (w > canvas_w || h > canvas_h) {
        let radio = Math.max(h / canvas_h, w / canvas_w);
        radio = Number(radio.toFixed(2));
        imgElem.width = parseInt(w / radio);
        imgElem.height = parseInt(h / radio);

    // Draw a 1/N image with the canvas width and height property set to 1/N of the style width and height to achieve N-fold magnification of the canvas content
    function run() {
      let ct = parseInt(compressTimes.value) || defaultCompressTimes;
      canvas.width = parseInt(canvas_w / ct);
      canvas.height = parseInt(canvas_h / ct);
      ctx.drawImage(img, 0, 0, parseInt(img.width / ct), parseInt(img.height / ct));

    function cleanCanvas() {
      ctx.clearRect(0, 0, canvas_w, canvas_h);

    function reset() {

    file.addEventListener('change', function (e) {
      const picFile = this.files[0];
      imgBlobUrl = window.URL.createObjectURL(picFile);
      img.onload = function init() {
      img.src = imgBlobUrl;
    }, false);

    button.addEventListener('click', reset, false);

Execution effect:

V. Mosaic Plug-in Encapsulation

With the example above, we learned how to use the canvas feature to design equal-sized mosaic effects. Now we try to encapsulate this feature as a simple plug-in that can mosaic the list of pictures on a page at one click.

The implementation of the plug-in is also simple - when the user clicks the button, insert a canvas equal to the size of the container (size is set by style) into the picture container, draw the image that covers the canvas, and reduce the width and height property of the canvas to enlarge the canvas content:

Plug-in script

/** @file mosaic.js **/

class Mosaic {
    constructor(url, container, options = {}) {
        if (typeof container === 'string') {
            container = document.querySelector(container);

        if (!url || !container?.style) {
            console.error('parameter is incorrect');

        this.url = url;
        this.options = options;
        this.container = container;

    init() {
        const img = new Image();
        const canvas = document.createElement('canvas'); = 'absolute'; = 999; = 'pixelated';
        this.img = img;
        this.canvas = canvas;
        this.ctx = canvas.getContext('2d');
        const containerBoundingRect = this.container.getBoundingClientRect();
        const container_w = containerBoundingRect.width;
        const container_h = containerBoundingRect.height;

        // Initialize canvas size to container size by style = container_w + 'px'; = container_h + 'px';

        img.onload = () => {
  , container_h);

        img.src = this.url;
    run(w, h) {
        // Shrink multiple, which can be passed in by a parameter, defaults to 12
        const compressTimes = parseInt(this.options.compressTimes) || 12;
        let compress_w = parseInt(w / compressTimes);
        let compress_h = parseInt(h / compressTimes);
        // Modify Canvas Size Attribute to 1/Shrink Multiple
        this.canvas.width = compress_w;
        this.canvas.height = compress_h;
        // Draw a picture to cover the reduced canvas
        this.ctx.drawImage(this.img, 0, 0, compress_w, compress_h);
        this.img = null;
    remove() {
        this.canvas = null;

export default Mosaic;

Plugin Use Page

/** @file plugin-demo.html **/
 ul {
 list-style: none;
 margin: 0;
 padding: 0;

 li {
 float: left;
 line-height: 0;
 margin: 0 20px 20px 0;

 li>img {
 max-height: 180px;

 div {
 display: block;
 clear: both;

    <li><img src="./assert/0.png" />li>
    <li><img src="./assert/1.png" />li>
    <li><img src="./assert/2.png" />li>
    <li><img src="./assert/3.png" />li>
    <button id="generate">Lay Mosaic button>
    <button id="remove">Remove Mosaic button>

  <script type="module">
 import Mosaic from './mosaic.js';

 let liElems = document.querySelectorAll('li');
 let mosaicList = [];

 document.querySelector('#generate').onclick = () => {
 for (let i = 0; i < liElems.length; i++) {
 let liElem = liElems[i];
 let url = liElem.querySelector('img').src;
 let mosaic = new Mosaic(url, liElem);

 function remove() {
 mosaicList.forEach((mosaic) => {
 mosaicList.length = 0;

 document.querySelector('#remove').onclick = remove;


Execution effect:

6. Compatibility

image-rendering compatibility can be obtained from caniuse As found above, the current coverage is as follows:

More influential are IE, UC, and Android version 4.4.4 browsers, which need to consider whether to use this CSS feature on their products as appropriate.

That's all for this article, and the code can Get on Github.

I hope you can get something out of it.

Keywords: Front-end css Computer Vision IT

Added by belphegor on Sat, 26 Feb 2022 19:33:43 +0200