init
This commit is contained in:
14
uni_modules/nutui-uni/components/watermark/index.scss
Normal file
14
uni_modules/nutui-uni/components/watermark/index.scss
Normal file
@@ -0,0 +1,14 @@
|
||||
.nut-watermark {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: $watermark-z-index;
|
||||
pointer-events: none;
|
||||
background-repeat: repeat;
|
||||
|
||||
&-full-page {
|
||||
position: fixed;
|
||||
}
|
||||
}
|
||||
1
uni_modules/nutui-uni/components/watermark/index.ts
Normal file
1
uni_modules/nutui-uni/components/watermark/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './watermark'
|
||||
86
uni_modules/nutui-uni/components/watermark/watermark.ts
Normal file
86
uni_modules/nutui-uni/components/watermark/watermark.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import type { ExtractPropTypes } from 'vue'
|
||||
import { CLICK_EVENT } from '../_constants'
|
||||
import { commonProps, makeNumberProp, makeNumericProp, makeStringProp, truthProp } from '../_utils'
|
||||
|
||||
export const watermarkProps = {
|
||||
...commonProps,
|
||||
/**
|
||||
* @description 水印的名称
|
||||
*/
|
||||
name: String,
|
||||
/**
|
||||
* @description 水印之间的垂直间距
|
||||
*/
|
||||
gapY: makeNumberProp(48),
|
||||
/**
|
||||
* @description 水印之间的水平间距
|
||||
*/
|
||||
gapX: makeNumberProp(24),
|
||||
/**
|
||||
* @description 追加的水印元素的z-index
|
||||
*/
|
||||
zIndex: makeNumberProp(2000),
|
||||
/**
|
||||
* @description 水印的宽度
|
||||
*/
|
||||
width: makeNumberProp(120),
|
||||
/**
|
||||
* @description 水印的高度
|
||||
*/
|
||||
height: makeNumberProp(64),
|
||||
/**
|
||||
* @description 水印绘制时,旋转的角度
|
||||
*/
|
||||
rotate: makeNumberProp(-22),
|
||||
/**
|
||||
* @description 图片源,建议导出 2 倍或 3 倍图,优先使用图片渲染水印
|
||||
*/
|
||||
image: String,
|
||||
/**
|
||||
* @description 图片宽度
|
||||
*/
|
||||
imageWidth: makeNumberProp(120),
|
||||
/**
|
||||
* @description 图片高度
|
||||
*/
|
||||
imageHeight: makeNumberProp(64),
|
||||
/**
|
||||
* @description 水印文字内容
|
||||
*/
|
||||
content: {
|
||||
type: [String, Array<string>],
|
||||
default: '',
|
||||
},
|
||||
/**
|
||||
* @description 水印文字颜色
|
||||
*/
|
||||
fontColor: makeStringProp('rgba(0,0,0,.15)'),
|
||||
/**
|
||||
* @description 水印文字样式
|
||||
*/
|
||||
fontStyle: makeStringProp('normal'),
|
||||
/**
|
||||
* @description 水印文字字体
|
||||
*/
|
||||
fontFamily: makeStringProp('PingFang SC'),
|
||||
/**
|
||||
* @description 水印文字粗细
|
||||
*/
|
||||
fontWeight: makeStringProp('normal'),
|
||||
/**
|
||||
* @description 水印文字大小
|
||||
*/
|
||||
fontSize: makeNumericProp(14),
|
||||
/**
|
||||
* @description 是否覆盖整个页面
|
||||
*/
|
||||
fullPage: truthProp,
|
||||
}
|
||||
|
||||
export type WaterMarkProps = ExtractPropTypes<typeof watermarkProps>
|
||||
|
||||
export const watermarkEmits = {
|
||||
[CLICK_EVENT]: (val: Event) => val instanceof Object,
|
||||
}
|
||||
|
||||
export type WaterMarkEmits = typeof watermarkEmits
|
||||
198
uni_modules/nutui-uni/components/watermark/watermark.vue
Normal file
198
uni_modules/nutui-uni/components/watermark/watermark.vue
Normal file
@@ -0,0 +1,198 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user