一、自定义指令的方式
/*需求:给整个页面添加背景水印。思路: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;
二、组件的方式
<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>
//./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>