vue3抠图组件-支持自定义颜色过滤

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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值