前端实现添加水印,两种方式

一、自定义指令的方式

javascript 复制代码
/*
  需求:给整个页面添加背景水印。

  思路:
    1、使用 canvas 特性生成 base64 格式的图片文件,设置其字体大小,颜色等。
    2、将其设置为背景图片,从而实现页面或组件水印效果
  
  使用:设置水印文案,颜色,字体大小即可
  <div v-waterMarker="{text:'版权所有',textColor:'rgba(180, 180, 180, 0.4)'}"></div>
*/

const addWaterMarker = (str, parentNode, font, textColor) => {
	// 水印文字,父元素,字体,文字颜色
	let can = document.createElement("canvas");
	parentNode.appendChild(can);
	can.width = 205;
	can.height = 140;
	can.style.display = "none";
	let cans = can.getContext("2d");
	cans.rotate((-20 * Math.PI) / 180);
	cans.font = font || "16px Microsoft JhengHei";
	cans.fillStyle = textColor || "rgba(180, 180, 180, 0.3)";
	cans.textAlign = "left";
	cans.textBaseline = "Middle";
	cans.fillText(str, can.width / 10, can.height / 2);
	parentNode.style.backgroundImage = "url(" + can.toDataURL("image/png") + ")";
};

const waterMarker = {
	mounted(el, binding) {
		addWaterMarker(binding.value.text, el, binding.value.font, binding.value.textColor);
	}
};

export default waterMarker;

二、组件的方式

html 复制代码
<template>
    <WaterMark watermark-text="你好你好你好">
        <div class="content">我这里是水印</div>
    </WaterMark>
</template>
<script setup lang="ts">
import WaterMark from "./WaterMark.vue";
</script>
<style scoped>
.content {
    display: flex;
    justify-content: space-around;
    width: 100%;
}
</style>
html 复制代码
//./WaterMark.vue
<template>
    <div ref="watermarkContainer" class="watermark-container">
        <!-- 插槽内容,可以放置需要水印的内容 -->
        <slot></slot>
    </div>
</template>

<script setup lang="ts">
import { onMounted, onUnmounted, ref, Ref, watch } from "vue";
// 定义Props
// defineProps<{ watermarkText: string }>();
const props = defineProps({
    watermarkText: {
        type: String,
        required: true,
        default: "默认水印",
    },
});
// const watermarkUrl: Ref<string> = ref("");
const watermarkContainer = ref<HTMLElement | null>(null);
const watermark = ref<HTMLDivElement | null>(null);
const drawWatermark = () => {
    console.log("drawWatermark----");
    const watermarkImage = getWatermarkImage();
    if (watermark.value) {
        watermark.value.remove();
    }
    watermark.value = document.createElement("div");
    watermark.value.style.position = "absolute";
    watermark.value.style.backgroundImage = `url(${watermarkImage})`;
    watermark.value.style.backgroundSize = "200px 200px";
    watermark.value.style.backgroundRepeat = "repeat";
    // watermark.value.style.opacity = "0.5";
    watermark.value.style.zIndex = "99";
    watermark.value.style.pointerEvents = "none";
    watermark.value.style.transform = "rotate(-45deg)";
    watermark.value.style.inset = "0";
    watermarkContainer.value?.appendChild(watermark.value);
};
const getWatermarkImage = () => {
    const canvas = document.createElement("canvas");
    const ctx = canvas.getContext("2d");
    if (!ctx) return;
    canvas.width = 200;
    canvas.height = 150;
    ctx.font = "16px Arial";
    ctx.fillStyle = "rgba(184, 184, 184, 0.5)";
    // ctx.textAlign = "center";
    ctx.fillText(props.watermarkText, 20, 100);
    return canvas.toDataURL("image/png");
};

const observer = new MutationObserver((mutations) => {
    mutations.forEach((mutation) => {
        console.log(mutation);
        if (
            mutation.type === "attributes" ||
            mutation.attributeName === "style"
        ) {
            drawWatermark();
        }
    });
});

onMounted(() => {
    drawWatermark();
    if (watermarkContainer.value) {
        observer.observe(watermarkContainer.value, {
            childList: true,
            subtree: true,
            attributes: true,
            // attributeFilter: ["style"],
        });
    }

    watch(
        () => props.watermarkText,
        () => {
            drawWatermark();
        },
    );
});
onUnmounted(() => {
    observer.disconnect();
});
</script>

<style>
.watermark-container {
    position: relative;
    width: 100%;
    height: 100%;
}
</style>