前言
水印挺常见的,一般就是用来标识来源。下面介绍一下如何实现背景水印和图片水印,水印的核心是canvas
,最好对canvas
有些了解。
背景水印
参考文章:VUE项目前端页面添加水印 这篇文章写的很详细了,我这里主要是参考该文章自己实现一下。
<template>
<div class="main">
</div>
</template>
<script setup lang="ts">
import {
onMounted } from 'vue';
// 生成水印
const initWatermark = () => {
// 创建一个canvas
const canvas = document.createElement('canvas');
// 设置画布的宽高
canvas.width = 200;
canvas.height = 200;
// 获取画笔
const ctx = canvas.getContext('2d');
// 水印,水印实际上就是将文字添加到画布上
ctx.font = '30px Arial'; // 设置字体大小和字体
ctx.rotate(-0.4);// 设置文字旋转角度
// 创建实体水印
// ctx.fillStyle = 'rgba(0,0,0,.3)'; // 颜色
// ctx.fillText('这是水印', canvas.width / 6, canvas.height / 2);// 设置显示文字和偏移量
// 创建虚心水印
// ctx.strokeStyle = 'rgba(0,0,0,.3)';
// ctx?.strokeText('这是水印', canvas.width / 6, canvas.height / 2);
// 渐变水印
const gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
gradient.addColorStop(0, 'red');
gradient.addColorStop(0.5, 'yellow');
gradient.addColorStop(1, 'green');
ctx.fillStyle = gradient; // 颜色
ctx.fillText('这是水印', canvas.width / 6, canvas.height / 2);// 设置显示文字和偏移量
// 将画布转成图片
const img = canvas.toDataURL();
const main = document.querySelector('.main');
main.style.backgroundImage = `url(${
img})`;
};
onMounted(() => {
initWatermark();
});
</script>
<style lang="scss" scoped>
.main{
width: 600px;
height: 400px;
border: 1px solid red;
margin: 0 auto;
}
</style>
实体水印
虚心水印
渐变水印
防止水印被取消
文章中提出了一个有意思的如何防止水印被清除掉。对于开发人员很清楚,水印实际上就是一个背景图,在控制台了把背景取消就可以被清除掉。
一种方法是不允许打开控制台,这个需要监听键盘、监听鼠标右键。取消掉其默认行为就好。
main?.addEventListener('contextmenu', (event) => {
event.preventDefault();
});
另一种方法就是文章中提供的MutationObserver
,通过监听dom树的变化来实现
// 监听水印的变化
const handleWatermark = () => {
// 获取需要观察的节点
const target = document.querySelector('.main');
// 观察器配置
const config = {
attributes: true, // 监听目标元素属性的变化
childList: true, // 监听目标原型子节点的变化
subtree: false // 是否观察后代的变化。默认false
};
// 创建观察器
const observer = new MutationObserver((abc) => {
// console.log(abc);
// 获取背景图片
const bgi = target?.style?.backgroundImage;
console.log('背景的值:', bgi);
if (!bgi) {
// 当背景被取消后,重新添加
initWatermark();
}
});
// 开始观察
observer.observe(target, config);
// 停止观察
// observer.disconnect()
};
给图片添加水印
就以csdn为例,当上传图片后,图片会自动添加上水印。
基本原理是:
- 拿到上传的图片
- 将图片绘制到画布上
- 在画布上添加水印
- 将画布转为图片
- 将新的图片数据传给后台
下面是一个简单的例子
<template>
<div class="main">
<input type="file" id="fileInput" accept="image/*" :multiple="false" />
<img v-if="imgUrl" :src="imgUrl" class="img" />
</div>
</template>
<script setup lang="ts">
import {
onMounted, ref } from 'vue';
const imgUrl = ref('');
onMounted(() => {
const fileInput = document.getElementById('fileInput');
// 监听文件上传
fileInput?.addEventListener('change', handleFileChange);
});
// 处理文件
const handleFileChange = async(event) => {
const [file] = event.target.files;
// 读取文件
const dataUrl = await readFile(file);
// 创建图片
const img = await createImage(dataUrl);
// 生成水印
imgUrl.value = createWatermark(img);
};
// 文件读取
const readFile = file => {
return new Promise((resolve) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
resolve(reader.result);
};
});
};
// 生成图片
const createImage = dataUrl => {
return new Promise((resolve) => {
const img = new Image();
img.src = dataUrl;
img.onload = () => {
resolve(img);
};
});
};
// 生成水印
const createWatermark = img => {
// 创建canvas
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
// 绘制图片
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
// 添加水印
ctx.font = '20px Arial';
ctx.rotate(-0.4);
ctx.fillStyle = 'green';
ctx.fillText('这是水印', canvas.width * 0.1, canvas.height * 0.3);
// 返回带有水印的图片
return canvas.toDataURL();
};
</script>
<style lang="scss" scoped>
.main{
width: 600px;
height: 400px;
border: 1px solid red;
margin: 0 auto;
}
.img{
width:300px;
height: 200px;
margin-top: 50px;
}
</style>
效果