Pixelate
A canvas-based image pixelation component. Converts any image into pixel-art style with configurable block size, grayscale mode, and custom color palette mapping.
Inspired by Pixelium Design and Pixel-UI Pixelit.
NOTE
The demo images have no particular meaning — they are just colorful samples to showcase the pixelation effect.
| Name | Category | Description | Type | Default |
|---|---|---|---|---|
pixelSize | Style | Pixel block size (higher = more pixelated) | number | 8 |
grayscale | Style | Enable grayscale mode | boolean | false |
width | Size | Component width | number | string | — |
height | Size | Component height | number | string | — |
palette | Color | Custom color palette (hex strings or RGB tuples) | string[] | number[][] | — |
background | Color | Background color for transparent areas | string | '#FFFFFF' |
src | Content | Image source URL or base64 | string | — |
rendered | Event | Emitted after pixelation rendering completes | () => void | — |
error | Event | Emitted when the image fails to load | (event: Event) => void | — |
canvasRef | Expose | Canvas element ref | HTMLCanvasElement | — |
originRef | Expose | Hidden original image element ref | HTMLImageElement | — |
render() | Method | Manually trigger re-render | () => void | — |
getSize() | Method | Get rendered canvas dimensions | () => { width: number; height: number } | — |
getImageData() | Method | Get pixelated ImageData | () => ImageData | null | — |
Basic Usage
Provide an image src to pixelate it with the default pixel size of 8.
<template>
<div style="display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-end">
<px-pixelate src="/images/pixelate/001.png" :width="400" />
</div>
</template>Pixel Size
Use pixel-size to control the level of pixelation. Higher values produce larger pixel blocks.
<template>
<div>
<div style="margin-bottom: 12px">
<label>Pixel Size: {{ pixelSize }}</label>
<input type="range" v-model.number="pixelSize" min="1" max="32" style="margin-left: 8px" />
</div>
<px-pixelate src="/images/pixelate/001.png" :pixel-size="pixelSize" :width="400" />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const pixelSize = ref(8);
</script>Grayscale
Enable grayscale to convert the pixelated output to grayscale using the ITU-R BT.601 luma formula.
<template>
<div style="display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-end">
<div>
<p style="margin-bottom: 8px">Original</p>
<px-pixelate src="/images/pixelate/002.jpg" :pixel-size="4" :width="300" />
</div>
<div>
<p style="margin-bottom: 8px">Grayscale</p>
<px-pixelate src="/images/pixelate/002.jpg" :pixel-size="4" grayscale :width="300" />
</div>
</div>
</template>Custom Palette
Use palette to map pixel colors to a limited set. Supports hex strings (e.g. '#FF0000') or RGB tuples (e.g. [255, 0, 0]). Each pixel block is mapped to the nearest palette color using Euclidean distance in RGB space.
<template>
<div style="display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-end">
<div>
<p style="margin-bottom: 8px">Original</p>
<px-pixelate src="/images/pixelate/001.png" :pixel-size="4" :width="260" />
</div>
<div>
<p style="margin-bottom: 8px">Game Boy Palette</p>
<px-pixelate src="/images/pixelate/001.png" :pixel-size="4" :palette="gameboyPalette" :width="260" />
</div>
<div>
<p style="margin-bottom: 8px">Sunset Palette</p>
<px-pixelate src="/images/pixelate/001.png" :pixel-size="4" :palette="sunsetPalette" :width="260" />
</div>
</div>
</template>
<script setup lang="ts">
// Classic Game Boy green palette
const gameboyPalette = ['#0f380f', '#306230', '#8bac0f', '#9bbc0f'];
// Warm sunset palette
const sunsetPalette = ['#2b0f3e', '#5c2d82', '#c0392b', '#e67e22', '#f1c40f', '#ecf0f1'];
</script>Dimensions
Use width and height to control the rendered size. Accepts numbers (pixels) or strings (CSS units like '50%').
<template>
<div style="display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-end">
<div>
<p style="margin-bottom: 8px">100 × 100</p>
<px-pixelate src="/images/pixelate/002.jpg" :pixel-size="4" :width="100" :height="100" />
</div>
<div>
<p style="margin-bottom: 8px">200 × 150</p>
<px-pixelate src="/images/pixelate/002.jpg" :pixel-size="4" :width="200" :height="150" />
</div>
<div>
<p style="margin-bottom: 8px">400 (auto height)</p>
<px-pixelate src="/images/pixelate/002.jpg" :pixel-size="4" :width="400" />
</div>
</div>
</template>Exposed Methods
Access the component instance via a template ref to call render(), getSize(), and getImageData().
<template>
<div>
<div style="margin-bottom: 12px; display: flex; gap: 8px; flex-wrap: wrap">
<px-button type="primary" @click="reRender">Re-render</px-button>
<px-button @click="showSize">Get Size</px-button>
<px-button @click="showImageData">Get ImageData</px-button>
</div>
<p v-if="info" style="margin-bottom: 8px; font-family: monospace">{{ info }}</p>
<px-pixelate ref="pixelateRef" src="/images/pixelate/001.png" :pixel-size="pixelSize" :width="400" />
</div>
</template>
<script setup lang="ts">
import type { PixelateInstance } from 'sakana-element';
import { ref } from 'vue';
const pixelateRef = ref<PixelateInstance>();
const info = ref('');
const pixelSize = ref(8);
function reRender() {
pixelSize.value = Math.max(1, Math.floor(Math.random() * 16) + 1);
info.value = `Re-rendered with pixelSize = ${pixelSize.value}`;
}
function showSize() {
const size = pixelateRef.value?.getSize();
info.value = size ? `Canvas size: ${size.width} × ${size.height}` : 'Not rendered yet';
}
function showImageData() {
const data = pixelateRef.value?.getImageData();
info.value = data
? `ImageData: ${data.width} × ${data.height} (${data.data.length} bytes)`
: 'No ImageData available';
}
</script>