Files
2026-01-05 12:47:14 +08:00

199 lines
4.7 KiB
Vue

<script setup lang="ts">
import { computed, defineComponent, reactive, toRefs, watch } from 'vue'
import { PREFIX } from '../_constants'
import { getMainClass, getMainStyle } from '../_utils'
import { watermarkEmits, watermarkProps } from './watermark'
const props = defineProps(watermarkProps)
defineEmits(watermarkEmits)
const state = reactive({
base64Url: '',
})
const {
zIndex,
gapX,
gapY,
width,
height,
rotate,
image,
imageWidth,
imageHeight,
content,
fontStyle,
fontWeight,
fontColor,
fontSize,
fontFamily,
} = toRefs(props)
async function init() {
let ratio = 1
uni.getSystemInfo({
success(res) {
ratio = res.pixelRatio
},
})
const canvasWidth = `${(gapX.value + width.value) * ratio}`
const canvasHeight = `${(gapY.value + height.value) * ratio}`
const markWidth = width.value * ratio
const markHeight = height.value * ratio
const canvas: any = uni.createOffscreenCanvas({
type: '2d',
width: Number(canvasWidth),
height: Number(canvasHeight),
})
const ctx: any = canvas.getContext('2d')
if (ctx) {
if (image?.value) {
// 创建一个图片
const img = canvas.createImage() as HTMLImageElement
dealWithImage(ctx, img, ratio, ctx.canvas, markWidth, markHeight)
}
else if (content?.value) {
dealWithText(ctx, ratio, ctx.canvas, markWidth, markHeight)
}
}
else {
throw new Error('当前环境不支持Canvas')
}
}
function initH5() {
const canvas = document.createElement('canvas')
const ratio = window.devicePixelRatio
const ctx = canvas.getContext('2d')
const canvasWidth = `${(gapX.value + width.value) * ratio}px`
const canvasHeight = `${(gapY.value + height.value) * ratio}px`
const markWidth = width.value * ratio
const markHeight = height.value * ratio
canvas.setAttribute('width', canvasWidth)
canvas.setAttribute('height', canvasHeight)
if (ctx) {
if (image?.value) {
const img = new Image()
dealWithImage(ctx, img, ratio, canvas, markWidth, markHeight)
}
else if (content?.value) {
dealWithText(ctx, ratio, canvas, markWidth, markHeight)
}
}
else {
throw new Error('当前环境不支持Canvas')
}
}
function dealWithImage(ctx: any, img: HTMLImageElement, ratio: number, canvas: HTMLCanvasElement, markWidth: number, markHeight: number) {
ctx.translate(markWidth / 2, markHeight / 2)
ctx.rotate((Math.PI / 180) * Number(rotate.value))
img.crossOrigin = 'anonymous'
img.referrerPolicy = 'no-referrer'
img.src = image!.value! // 要加载的图片 url, 可以是base64
img.onload = () => {
ctx.drawImage(
img,
(-imageWidth.value * ratio) / 2,
(-imageHeight.value * ratio) / 2,
imageWidth.value * ratio,
imageHeight.value * ratio,
)
ctx.restore()
state.base64Url = canvas.toDataURL()
}
}
function dealWithText(ctx: any, ratio: number, canvas: HTMLCanvasElement, markWidth: number, markHeight: number) {
ctx.textBaseline = 'middle'
ctx.textAlign = 'center'
// 文字绕中间旋转
ctx.translate(markWidth / 2, markHeight / 2)
ctx.rotate((Math.PI / 180) * Number(rotate.value))
const markSize = Number(fontSize.value) * ratio
ctx.font = `${fontStyle.value} normal ${fontWeight.value} ${markSize}px/${markHeight}px ${fontFamily.value}`
ctx.fillStyle = fontColor.value
if (Array.isArray(content.value)) {
content.value?.forEach((item, index) => {
ctx.fillText(item, 0, (index - 1) * markSize)
})
}
else {
ctx.fillText(content.value, 0, 0)
}
ctx.restore()
state.base64Url = canvas.toDataURL()
}
// #ifdef H5
initH5()
// #endif
// #ifndef H5
init()
// #endif
watch(
() => [
zIndex.value,
gapX.value,
gapY.value,
width.value,
height.value,
rotate.value,
image?.value,
imageWidth.value,
imageHeight.value,
content?.value,
fontStyle.value,
fontWeight.value,
fontColor.value,
fontSize.value,
fontFamily.value,
],
() => {
// #ifdef H5
initH5()
// #endif
// #ifndef H5
init()
// #endif
},
)
const classes = computed(() => {
return getMainClass(props, componentName, {
[`${componentName}-full-page`]: props.fullPage,
})
})
const styles = computed(() => {
return getMainStyle(props, {
zIndex: props.zIndex,
backgroundSize: `${props.gapX + props.width}px`,
backgroundImage: `url('${state.base64Url}')`,
})
})
</script>
<script lang="ts">
const componentName = `${PREFIX}-watermark`
export default defineComponent({
name: componentName,
options: {
virtualHost: true,
addGlobalClass: true,
styleIsolation: 'shared',
},
})
</script>
<template>
<view
:class="classes"
:style="styles"
/>
</template>
<style lang="scss">
@import './index';
</style>