增加水印功能

This commit is contained in:
zouzhibing 2022-06-17 14:27:56 +08:00
parent 01828a12ae
commit 4e1e0e7ce2
5 changed files with 162 additions and 141 deletions

View File

@ -15,11 +15,14 @@
//margin: 20px;
padding: 20px;
box-sizing: border-box;
width: 100%;
height: 100%;
.m-container-layout-inner{
background: white;
padding: 20px;
background: white;
width: 100%;
box-sizing: border-box;
min-height: 100%;
}
}
</style>

View File

@ -54,12 +54,12 @@ const othersRouter = {
name: 'qrcode',
meta: { title: '生成二维码' }
},
// {
// path: 'water-marker',
// component: () => import('@/views/other/water-marker.vue'),
// name: 'water-marker',
// meta: { title: '生成水印' }
// },
{
path: 'water-marker',
component: () => import('@/views/other/water-marker.vue'),
name: 'water-marker',
meta: { title: '生成水印' }
},
{
path: 'right-menu',
component: () => import('@/views/other/right-menu.vue'),

75
src/utils/waterMarker.ts Normal file
View File

@ -0,0 +1,75 @@
function watermark (options) {
const {
container = document.body, // 容器
width = '240', // canvas元素宽
height = '100', // canvas元素高
textAlign = 'left', // 文字对齐
textBaseline = 'bottom', // 基准线
font = '16px Microsoft Yahei', // 字体大小及样式
fillStyle = '#000', // 自定义水印的颜色
content = 'Vue Admin Perfect', // 水印内容
globalAlpha = 0.3, // 设置图形和图像透明度的值
rotate = 16, // 文字旋转角度
zIndex = 1000, // 元素堆叠顺序
isCancel =true
} = options
const canvas = document.createElement('canvas')
canvas.setAttribute('width', width)
canvas.setAttribute('height', height)
const ctx = canvas.getContext('2d') // 获取 canvas2d 上下文
ctx.globalAlpha = globalAlpha
ctx.textAlign = textAlign
ctx.textBaseline = textBaseline
ctx.font = font
ctx.fillStyle = fillStyle
ctx.rotate((Math.PI * rotate) / 180)
// ctx.rotate(-10 * Math.PI / 140);
ctx.fillText(content, 50, 50)
const base64Url = canvas.toDataURL() // 返回一个包含图片展示的 data URI
const __wm = document.querySelector('.__wm')// 选择器
const watermarkDiv = __wm || document.createElement('div')
const styleStr = `
position:absolute;
top:0px;
left:0px;
width:100%;
height:100%;
z-index:${zIndex};
pointer-events:none;
background-repeat:repeat;
background-image:url('${base64Url}')`
watermarkDiv.setAttribute('style', styleStr)
watermarkDiv.classList.add('__wm') // 为元素添加“__wm”类名
container.style.position = 'relative'
if (!__wm) {
container.appendChild(watermarkDiv) // 添加元素
}
// const MutationObserver = window.MutationObserver || window.WebKitMutationObserver
// // 检查浏览器是否支持这个API
// if (MutationObserver) {
// const args = arguments[0]
// let mo = new MutationObserver(function () {
// const __wm = document.querySelector('.__wm')
// // 只在__wm元素变动才重新调用
// if ((__wm && __wm.getAttribute('style') !== styleStr) || !__wm || document.body.style.position !== 'relative') {
// console.log('============',isCancel)
// // 避免一直触发
// mo.disconnect()
// mo = null
// watermark(args)
// }
// })
// mo.observe(document.body, {
// attributes: true, // 观察目标节点的属性节点
// subtree: false, // 观察目标节点的所有后代节点
// childList: true // 观察目标节点的子节点
// })
// }
}
export default watermark

View File

@ -1,8 +1,9 @@
<template>
<u-container-layout class="m-cropper">
<div class="left">
<div class="cropper-content">
<vue-cropper
<div style="display: flex">
<div class="left">
<div class="cropper-content">
<vue-cropper
ref="cropper"
:img="option.img"
:output-size="option.size"
@ -19,35 +20,36 @@
:auto-crop-height="option.autoCropHeight"
:center-box="option.centerBox"
@real-time="realTime"
/>
</div>
<el-button type="primary" @click.prevent="zoom(1)">放大</el-button>
<el-button type="primary" @click.prevent="zoom(-1)">缩小</el-button>
<el-button type="primary" @click.prevent="rotateLeft">向左旋转</el-button>
<el-button type="primary" @click.prevent="rotateRight">向右旋转</el-button>
<el-button type="primary" @click.prevent="reset">清除</el-button>
<el-button type="primary" @click.prevent="cropImage">获取结果</el-button>
<el-button type="primary" @click.prevent="down('base64')">下载图片</el-button>
<el-upload
/>
</div>
<el-button type="primary" @click.prevent="zoom(1)">放大</el-button>
<el-button type="primary" @click.prevent="zoom(-1)">缩小</el-button>
<el-button type="primary" @click.prevent="rotateLeft">向左旋转</el-button>
<el-button type="primary" @click.prevent="rotateRight">向右旋转</el-button>
<el-button type="primary" @click.prevent="reset">清除</el-button>
<el-button type="primary" @click.prevent="cropImage">获取结果</el-button>
<el-button type="primary" @click.prevent="down('base64')">下载图片</el-button>
<el-upload
class="upload-demo"
:show-file-list="false"
action
:before-upload="beforeUpload"
>
<el-button type="primary">选择上传图片</el-button>
</el-upload>
</div>
<div>
<h4 style="margin-top: 10px">实时预览</h4>
<div class="show-preview" :style="{'width': option.previews.w + 'px', 'height': option.previews.h + 'px', 'overflow': 'hidden', 'margin': '5px'}">
<div :style="option.previews.div">
<img :src="option.previews.url" :style="option.previews.img">
</div>
>
<el-button type="primary">选择上传图片</el-button>
</el-upload>
</div>
<h4 style="margin-top: 10px">获取结果</h4>
<div style="background: #ccc;width: 100px;height: 100px">
<img :src="option.cropImg" style="width: 100%;height: 100%" v-if="option.cropImg"/>
<div>
<h4 style="margin-top: 10px">实时预览</h4>
<div class="show-preview" :style="{'width': option.previews.w + 'px', 'height': option.previews.h + 'px', 'overflow': 'hidden', 'margin': '5px'}">
<div :style="option.previews.div">
<img :src="option.previews.url" :style="option.previews.img">
</div>
</div>
<h4 style="margin-top: 10px">获取结果</h4>
<div style="background: #ccc;width: 100px;height: 100px">
<img :src="option.cropImg" style="width: 100%;height: 100%" v-if="option.cropImg"/>
</div>
</div>
</div>
</u-container-layout>

View File

@ -1,123 +1,64 @@
<template>
<div class="m-water-marker"></div>
<div class="m-water-marker">
<el-button @click="addWaterMarker(1)" type="primary">生成默认颜色水印</el-button>
<el-button @click="addWaterMarker(2)" type="primary" >生成随机颜色水印</el-button>
<el-input v-model="opacity" style="width: 100px;margin-left: 20px"></el-input>
<el-button @click="addWaterMarker(3)" type="primary" >设置透明度</el-button>
<el-button @click="addWaterMarker(4)" type="primary" >取消水印</el-button>
</div>
</template>
<script lang="ts" setup>
import { nextTick, onMounted } from 'vue'
import { nextTick, onMounted, ref } from 'vue'
import watermark from '@/utils/waterMarker'
import { getColor } from '@/utils/index'
function watermark (options) {
const {
container = document.body, //
width = '100', // canvas
height = '100', // canvas
textAlign = 'left', //
textBaseline = 'bottom', // 线
font = '16px Microsoft Yahei', //
fillStyle = '#000', //
content = '内部文档,请勿外传', //
globalAlpha = 0.1, //
rotate = 16, //
zIndex = 1000, //
} = options;
let canvas = document.createElement('canvas');
canvas.setAttribute('width', width);
canvas.setAttribute('height', height);
let ctx = canvas.getContext('2d'); // canvas2d
ctx.globalAlpha = globalAlpha;
ctx.textAlign = textAlign;
ctx.textBaseline = textBaseline;
ctx.font = font;
ctx.fillStyle = fillStyle;
ctx.rotate((Math.PI * rotate) / 180);
ctx.fillText(content, 50, 50);
const base64Url = canvas.toDataURL(); // data URI
const __wm = document.querySelector('.__wm');//
const watermarkDiv = __wm || document.createElement("div");
const styleStr = `
position:absolute;
top:0px;
left:0px;
width:100%;
height:100%;
z-index:${zIndex};
pointer-events:none;
background-repeat:repeat;
background-image:url('${base64Url}')`;
watermarkDiv.setAttribute('style', styleStr);
watermarkDiv.classList.add('__wm'); // __wm
document.body.style.position = 'relative';
if (!__wm) {
document.body.appendChild(watermarkDiv); //
}
const MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
// API
if (MutationObserver) {
const args = arguments[0];
let mo = new MutationObserver(function () {
const __wm = document.querySelector('.__wm');
// __wm
if ((__wm && __wm.getAttribute('style') !== styleStr) || !__wm || document.body.style.position !== 'relative') {
//
mo.disconnect();
mo = null;
watermark(args);
const opacity = ref(0.2)
const options = {
rotate: -10,
globalAlpha: opacity.value
}
function getRandom (start, end) {
const differ = end - start
const random = Math.random()
return (start + differ * random).toFixed(0)
}
const message = ['red']
//
for (let i = 0; i < 30; i++) {
message.push(getColor())
}
const addWaterMarker = (type) => {
if (type === 1) {
options.fillStyle = '#000'
watermark(options)
} else if (type === 2) {
options.fillStyle = message[getRandom(0, 30)]
watermark(options)
} else if (type === 3) {
options.globalAlpha = opacity.value
watermark(options)
} else if (type === 4) {
nextTick(() => {
const wm = document.getElementsByClassName('__wm')[0]
if (wm) {
document.body.removeChild(wm)
}
});
mo.observe(document.body, {
attributes: true, //
subtree: true, //
childList: true, //
})
}
}
onMounted(()=>{
nextTick(()=>{
const canvas = document.createElement('canvas')
const width = 100
const height = 100
canvas.width = width
canvas.height = height
const context = canvas.getContext('2d')
context.font = "normal small-caps lighter 16px sans-serif";
context.fillStyle = "#666";
context.textAlign = "center";
context.textBaseline = "Middle";
context.rotate(-10 * Math.PI / 140);
context.fillText('content', parseFloat(width) / 2, parseFloat(height) / 2);
context.fillText('content', 60, 100);
var base64Url = canvas.toDataURL();
const watermarkDiv = document.getElementsByClassName('m-water-marker')[0]
watermarkDiv.style.backgroundAttachment = "fixed";
watermarkDiv.style.backgroundPosition = "center";
watermarkDiv.style.backgroundImage = "url(" + canvas.toDataURL("image/png") + ")";
// const options = {
// container : document.body, //
// width : '100', // canvas
// height : '100', // canvas
// textAlign : 'left', //
// textBaseline : 'bottom', // 线
// font : '16px Microsoft Yahei', //
// fillStyle : '#000', //
// content : '', //
// globalAlpha : 0.1, //
// rotate : 16, //
// zIndex : 1000, //
// }
watermark(options)
})
onMounted(() => {
nextTick(() => {
})
})
</script>
<style>
.m-water-marker{
width: 1000px;
height: 500px;
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 20px;
}
</style>