Files
mini-yu/uni_modules/tdesign-uniapp/components/watermark/watermark.vue
lingxiao865 c5af079d8c first commit
2026-02-10 08:05:03 +08:00

201 lines
5.3 KiB
Vue

<template>
<view
:class="classPrefix + ' ' + tClass"
:style="tools._style([customStyle])"
>
<block v-if="content">
{{ content }}
</block>
<slot name="content" />
<slot />
<canvas
:id="canvasId"
:canvas-id="canvasId"
type="2d"
:style="canvasStyle"
/>
<view
:class="movable ? 'watermark-move' : ''"
:style="tools._style(watermarkStyle)"
/>
</view>
</template>
<script>
import tools from '../common/utils.wxs';
import watermarkProps from './props';
import { prefix } from '../common/config';
import { uniComponent } from '../common/src/index';
import { appBaseInfo, nextTick } from '../common/utils';
import generateBase64Url from './utils/generateBase64Url';
import randomMovingStyle from './utils/randomMovingStyle';
const name = `${prefix}-watermark`;
export default uniComponent({
name,
options: {
styleIsolation: 'shared',
},
externalClasses: [`${prefix}-class`],
props: {
...watermarkProps,
},
data() {
return {
classPrefix: name,
tools,
watermarkStyle: {},
initialed: false,
canvasId: `watermark-canvas-${Math.random().toString(36)
.slice(2, 11)}`,
};
},
computed: {
canvasStyle() {
let result = 'width: 100px; height: 100px;';
let shouldHide = true;
// #ifdef APP-PLUS || MP-ALIPAY
if (!this.initialed) {
shouldHide = false;
}
// #endif
if (shouldHide) {
result += 'display: none;';
}
return result;
},
},
watch: {
watermarkContent: 'renderWatermark',
movable: 'renderWatermark',
rotate: 'renderWatermark',
x: 'renderWatermark',
y: 'renderWatermark',
width: 'renderWatermark',
height: 'renderWatermark',
alpha: 'renderWatermark',
lineSpace: 'renderWatermark',
moveInterval: 'renderWatermark',
zIndex: 'renderWatermark',
offset: 'renderWatermark',
removable: 'renderWatermark',
isRepeat: 'renderWatermark',
layout: 'renderWatermark',
},
async mounted() {
await nextTick();
this.renderWatermark();
},
methods: {
watermarkColor() {
return appBaseInfo.theme === 'dark'
? 'rgba(238, 238, 238, 0.1)'
: 'rgba(0, 0, 0, 0.1)';
},
renderWatermark() {
const query = uni.createSelectorQuery().in(this);
query
.select(`#${this.canvasId}`)
.fields({ node: true, size: true })
.exec(async (res) => {
if (!res[0]?.node) {
console.error('Canvas node not found');
return;
}
const canvas = res[0].node;
const gapX = this.movable ? 0 : this.x;
const gapY = this.movable ? 0 : this.y;
const offset = this.offset || [];
const offsetLeft = offset[0] || gapX / 2;
const offsetTop = offset[1] || gapY / 2;
const bgImageOptions = {
width: this.width,
height: this.height,
rotate: this.movable ? 0 : this.rotate,
lineSpace: this.lineSpace,
alpha: this.alpha,
gapX,
gapY,
watermarkContent: this.watermarkContent,
offsetLeft,
offsetTop,
watermarkColor: this.watermarkColor(),
layout: this.layout,
};
generateBase64Url.call(
this,
canvas,
this.canvasId,
bgImageOptions,
(base64Url, backgroundSize) => {
let animationVars = {};
if (this.movable) {
const {
left0,
left25,
left50,
left75,
top0,
top25,
top50,
top75,
} = randomMovingStyle();
animationVars = {
'--watermark-left-0': left0,
'--watermark-left-25': left25,
'--watermark-left-50': left50,
'--watermark-left-75': left75,
'--watermark-top-0': top0,
'--watermark-top-25': top25,
'--watermark-top-50': top50,
'--watermark-top-75': top75,
'--watermark-animation-duration': `${
(this.moveInterval * 4) / 60
}s`,
};
}
this.watermarkStyle = {
zIndex: this.zIndex,
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
width: '100%',
height: '100%',
backgroundSize: `${
backgroundSize?.width || gapX + this.width
}px`,
pointerEvents: 'none',
backgroundRepeat: this.movable ? 'no-repeat' : 'repeat',
backgroundImage: `url('${base64Url}')`,
...animationVars,
};
},
() => {
this.initialed = true;
},
).catch((e) => {
console.log('e', e);
});
});
},
},
});
</script>
<style scoped>
@import './watermark.css';
/* diff with td-mini */
.watermark-move {
animation: watermark var(--watermark-animation-duration, 100s) linear infinite;
}
</style>