import type { CSSProperties, PropType } from 'vue' import { getPrefixCls } from '../../RouteContext' export type WaterMarkProps = { /** 水印样式 */ markStyle?: CSSProperties /** 水印类名 */ markClassName?: string /** 水印之间的水平间距 */ gapX?: number /** 水印之间的垂直间距 */ gapY?: number /** 追加的水印元素的z-index */ zIndex?: number /** 水印的宽度 */ width?: number /** 水印的高度 */ height?: number /** 水印在canvas 画布上绘制的垂直偏移量,正常情况下,水印绘制在中间位置, 即 offsetTop = gapY / 2 */ offsetTop?: number // 水印图片距离绘制 canvas 单元的顶部距离 /** 水印在canvas 画布上绘制的水平偏移量, 正常情况下,水印绘制在中间位置, 即 offsetTop = gapX / 2 */ offsetLeft?: number /** 水印绘制时,旋转的角度,单位 ° */ rotate?: number /** ClassName 前缀 */ prefixCls?: string /** 高清印图片源, 为了高清屏幕显示,建议使用 2倍或3倍图,优先使用图片渲染水印。 */ image?: string /** 水印文字内容 */ content?: string | string[] /** 文字颜色 */ fontColor?: string /** 文字样式 */ fontStyle?: 'none' | 'normal' | 'italic' | 'oblique' /** 文字族 */ fontFamily?: string /** 文字粗细 */ fontWeight?: 'normal' | 'light' | 'weight' | number /** 文字大小 */ fontSize?: number | string } const waterMarkProps = { markStyle: { type: Object as PropType, default: () => undefined }, markClassName: { type: String as PropType, default: '' }, gapX: { type: Number, default: 212 }, gapY: { type: Number, default: 222 }, // antd 内容层 zIndex 基本上在 10 以下 https://github.com/ant-design/ant-design/blob/6192403b2ce517c017f9e58a32d58774921c10cd/components/style/themes/default.less#L335 zIndex: { type: Number, default: 9 }, width: { type: Number, default: 120 }, height: { type: Number, default: 64 }, offsetTop: { type: Number, default: undefined }, offsetLeft: { type: Number, default: undefined }, // 默认旋转 -22 度 rotate: { type: Number, default: -22 }, prefixCls: { type: String, default: '' }, image: { type: String, default: '' }, content: { type: [String, Array] as PropType, default: '' }, fontColor: { type: String, default: 'rgba(0,0,0,.15)' }, fontStyle: { type: String, default: 'normal' }, fontFamily: { type: String, default: 'sans-serif' }, fontWeight: { type: [Number, String] as PropType, default: 'normal' }, fontSize: { type: [Number, String] as PropType, default: 16 } } /** * 返回当前显示设备的物理像素分辨率与CSS像素分辨率之比 * * @param context * @see api 有些废弃了,其实类型 CanvasRenderingContext2D */ const getPixelRatio = (context: any) => { if (!context) { return 1 } const backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1 return (window.devicePixelRatio || 1) / backingStore } const WaterMark = defineComponent({ name: 'WaterMark', props: waterMarkProps, setup(props, { slots, attrs }) { const prefixCls = getPrefixCls('pro-layout-watermark', props.prefixCls) const wrapperCls = [`${prefixCls}-wrapper`, attrs.class] const waterMakrCls = [prefixCls, props.markClassName] const base64Url = ref('') watchEffect(() => { const canvas = document.createElement('canvas') const ctx = canvas.getContext('2d') const ratio = getPixelRatio(ctx) const canvasWidth = `${(props.gapX + props.width) * ratio}px` const canvasHeight = `${(props.gapY + props.height) * ratio}px` const canvasOffsetLeft = props.offsetLeft || props.gapX / 2 const canvasOffsetTop = props.offsetTop || props.gapY / 2 canvas.setAttribute('width', canvasWidth) canvas.setAttribute('height', canvasHeight) if (ctx) { // 旋转字符 rotate ctx.translate(canvasOffsetLeft * ratio, canvasOffsetTop * ratio) ctx.rotate((Math.PI / 180) * Number(props.rotate)) const markWidth = props.width * ratio const markHeight = props.height * ratio if (props.image) { const img = new Image() img.crossOrigin = 'anonymous' img.referrerPolicy = 'no-referrer' img.src = props.image img.onload = () => { ctx.drawImage(img, 0, 0, markWidth, markHeight) base64Url.value = canvas.toDataURL() } } else if (props.content) { const markSize = Number(props.fontSize) * ratio ctx.font = `${props.fontStyle} normal ${props.fontWeight} ${markSize}px/${markHeight}px ${props.fontFamily}` ctx.fillStyle = props.fontColor if (Array.isArray(props.content)) { props.content?.forEach((item: string, index: number) => ctx.fillText(item, 0, index * 50) ) } else { ctx.fillText(props.content, 0, 0) } base64Url.value = canvas.toDataURL() } } else { // eslint-disable-next-line no-console console.error('当前环境不支持Canvas') } }) return () => (
{slots.default?.()}
) } }) export default WaterMark