Pixelate 像素化
基于 Canvas 的图片像素化组件。可将任意图片转换为像素风格,支持可配置的像素块大小、灰度模式以及自定义调色板映射。
NOTE
示例图片没有特别含义,仅作为展示像素化效果的彩色样本。
| 名称 | 分类 | 说明 | 类型 | 默认值 |
|---|---|---|---|---|
pixelSize | 风格 | 像素块大小(值越大越像素化) | number | 8 |
grayscale | 风格 | 启用灰度模式 | boolean | false |
width | 尺寸 | 组件宽度 | number | string | — |
height | 尺寸 | 组件高度 | number | string | — |
palette | 颜色 | 自定义调色板(十六进制字符串或 RGB 元组) | string[] | number[][] | — |
background | 颜色 | 透明区域的背景色 | string | '#FFFFFF' |
src | 内容 | 图片源地址或 base64 | string | — |
rendered | 事件 | 像素化渲染完成后触发 | () => void | — |
error | 事件 | 图片加载失败时触发 | (event: Event) => void | — |
canvasRef | 暴露 | Canvas 元素引用 | HTMLCanvasElement | — |
originRef | 暴露 | 隐藏的原始图片元素引用 | HTMLImageElement | — |
render() | 方法 | 手动触发重新渲染 | () => void | — |
getSize() | 方法 | 获取渲染后的画布尺寸 | () => { width: number; height: number } | — |
getImageData() | 方法 | 获取像素化后的 ImageData | () => ImageData | null | — |
基础用法
传入图片 src 即可使用默认像素大小(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 控制像素化程度。值越大,像素块越大,像素化效果越明显。
<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 属性,可将像素化输出转换为灰度图像(使用 ITU-R BT.601 亮度公式)。
<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>自定义调色板
通过 palette 属性将像素颜色映射到有限的颜色集合。支持十六进制字符串(如 '#FF0000')或 RGB 元组(如 [255, 0, 0])。每个像素块会映射到 RGB 空间中欧几里得距离最近的调色板颜色。
<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>尺寸控制
使用 width 和 height 属性控制渲染尺寸。接受数值(像素)或字符串(CSS 单位如 '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>暴露方法
通过模板引用访问组件实例,调用 render()、getSize() 和 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>