增加水印功能
This commit is contained in:
parent
01828a12ae
commit
4e1e0e7ce2
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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'),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue