Vue3+ts+less 实现图片像素过滤
不喜欢说废话,直接上代码
优点: 无需依赖任何组件库,插件(除了项目整个less 如果你项目没有请先 npm i -D less)
缺点:无法精确降噪(肯定a逻辑是全局过滤像素)
使用场景: jpg图片变透明,…
使用教程:就是一个vue3组件,引入就完事了
<script lang="ts" setup>
// 假如这是你的app.vue
import Koutu from "@/views/koutu.vue"; // 这是你放组件的路径,记得改成你自己的
import jpg from "@/views/1.jpg"; // 这是你的示例图片路径
</script>
<template>
<koutu :_src="jpg" :hidden-other="false"></koutu>
</template>
效果如下:
诶?没看出区别嘛
换个底色试试,点一下切换主题
适用场景:过滤指定颜色如白色等单一颜色区域
<template>
<div style="display: flex">
<div class="image-container">
<span class="demonstration" v-if="!props.hiddenOther">转换前</span>
<img
v-if="!props.hiddenOther"
:src="props._src"
ref="imgRef"
@load="onImageLoad"
@error="onImageError"
crossOrigin="anonymous"
/>
</div>
<div class="image-container">
<span class="demonstration" v-if="!props.hiddenOther">转化后</span>
<div class="canvas-container texture">
<canvas id="image-canvas" ref="canvasRef"></canvas>
<input
type="color"
class="colorbox"
v-model="selectedColor"
v-if="!props.hiddenOther"
/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from "vue";
interface IProp {
_src: string; // 图片路径
scale?: number; // 设置过滤后的缩放大小
hiddenOther?: boolean; // 是否隐藏其他控件
}
const props = withDefaults(defineProps<IProp>(), {
_src: () => "",
scale: () => 1, // 设置过滤后的缩放大小
hiddenOther: () => false, // 是否显示其他控件 默认不隐藏
});
const canvasRef = ref<HTMLCanvasElement | null>(null); // 图片过滤后的canvas展示元素ref
const imgRef = ref<HTMLImageElement | null>(null); // 图片元素ref
const selectedColor = ref("#aaaaaa"); // 颜色选择器 默认选中灰白色
watch(selectedColor, () => {
onImageLoad();
});
/**
* function hexToRgb(hex: string): [number, number, number]
* @param hex 字符串,表示一个十六进制颜色值
* @returns 返回一个包含RGB值的数组,表示十六进制颜色值的RGB分量
*
* 目的是处理 颜色选择器的色值 之后用于过滤指定像素
*/
function hexToRgb(hex: string): [number, number, number] {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result
? [
parseInt(result[1], 16),
parseInt(result[2], 16),
parseInt(result[3], 16),
]
: [170, 170, 170]; // 默认返回白色
}
/**
* @function filter(data: Uint8ClampedArray, filterRgb?: string): void
* @param data 画布像素信息
* @param filterRgb 过滤的rgb值
*
* 过滤指定像素
*/
function filter(data: Uint8ClampedArray, filterRgb?: string) {
let [_r, _g, _b] = [170, 170, 170];
if (filterRgb) {
[_r, _g, _b] = hexToRgb(filterRgb);
}
let count = 0; // 记录过滤的像素数量
for (let i = 0; i < data.length; i += 4) {
let r = data[i],
g = data[i + 1],
b = data[i + 2];
// 修改过滤条件,只匹配纯白色像素(RGB 值均为 255)
if (r >= _r && g >= _g && b >= _b) {
data[i + 3] = 0; // 把纯白色改成透明的
count++;
}
// // 放宽过滤条件,色值在240-256之间都认为是白色
// if ([r, g, b].every((v) => v >= 138)) {
// data[i + 3] = 0; // 把白色改成透明的
// count++;
// }
}
// console.log(`Filtered ${count} pixels`);
}
/**
* function onImageLoad(): void
* @description 图片加载成功后,进行过滤操作
*/
function onImageLoad() {
if (canvasRef.value && imgRef.value) {
const width = imgRef.value.width;
const height = imgRef.value.height;
console.log(width, height);
// 动态设置画布的尺寸
canvasRef.value.width = width;
canvasRef.value.height = height;
const ctx = canvasRef.value.getContext("2d");
if (ctx) {
ctx.scale(props.scale, props.scale); // 缩放画布
ctx.drawImage(imgRef.value, 0, 0);
// 获取画布像素信息
const imageData = ctx.getImageData(0, 0, width, height);
// 一个像素点由RGBA四个值组成,data为[R,G,B,A [,R,G,B,A[...]]]组成的一维数组
// 可以通过修改该数组的数据,达到修改图片内容的目的
const data = imageData.data;
filter(data, selectedColor.value); // 这里对图像数据进行处理
// 把新的内容画进画布里
ctx.putImageData(imageData, 0, 0);
}
}
}
function onImageError() {
console.error("图片加载失败");
}
</script>
<style scoped lang="less">
.image-container {
// box-sizing: border-box;
// margin-top: 30px;
// margin-right: 30px;
// border: 1px solid var(--el-border-color);
// border-radius: 10px;
// padding: 20px;
}
.demonstration {
text-align: center;
display: block;
color: var(--el-text-color-secondary);
font-size: 14px;
}
/* 画布容器 start */
.canvas-container {
position: relative;
width: 100%;
.colorbox {
position: absolute;
top: 0;
left: -50px;
}
}
.texture {
/* 设置透明底纹 */
--ccc: #ccc;
---transparent: transparent;
font-size: 0;
background-image: -webkit-gradient(
linear,
0 0,
100% 100%,
color-stop(0.25, var(---transparent)),
color-stop(0.25, transparent),
to(transparent)
),
-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, var(---transparent)), color-stop(0.25, transparent), to(transparent)),
-webkit-gradient(linear, 0 0, 100% 100%, color-stop(0.75, transparent), color-stop(0.75, var(---transparent))),
-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.75, transparent), color-stop(0.75, var(---transparent)));
background-size: 10px 10px;
}
.image-canvas {
scale: 1;
}
/* 画布容器 end */
</style>