一、使用canvas创建一个与窗口大小一致的画布,然后使用canvas的fillText的方法绘制水印。
此方法有几个注意点:
1. 画布不能覆盖网页上的有效事件,因些需要给canvas设置pointer-events: none;样式
2.创建的画布大小需要是窗口对角线尺寸的正方形,保证在画布在旋转某个角度时4个角不会出现空白区。同时需要将canvas的与当前窗口的尺寸进行计算出向左、向上的偏移量。
3. 计算绘制的文字长度时,一定要设置font属性,否则会使用画布默认的字体大小计算长度,容易出现bug。
4. 计算行数、列数时,需要注意,尤其在fillText时规划好x,y坐标
5. 在旋转画布时,一定要将旋转中心设置到画布的x,y轴的中心位置,旋转完毕后再复原置原来位置。
import React, { useEffect, useRef } from "react"
import PropTypes from 'prop-types'
export default function Watermark({ text, rotate }) {
const canvasRef = useRef(null)
const setCanvasData = () => {
const canvas = canvasRef.current
const canvasParent = document.body
// 画布大小需要为容器的对角线长度,
// 画布在旋转时不会出现对角空白无水印!!!
const maxSizeContainer = Math.max(canvasParent.offsetWidth, canvasParent.offsetHeight)
const minSizeCanvas = Math.ceil(Math.SQRT2 * maxSizeContainer)
// 设置画面的最小尺寸
canvas.setAttribute('width', String(minSizeCanvas))
canvas.setAttribute('height', String(minSizeCanvas))
// 修改画布在容器中的偏移,使其中心位置与容器的中心位置永远保持一致
// 在旋转角度时也要以其中心点为中心进行旋转
canvas.style.position = 'fixed'
canvas.style.top = - Math.ceil((minSizeCanvas - maxSizeContainer) / 2) + 'px'
canvas.style.left = - Math.ceil((minSizeCanvas - maxSizeContainer) / 2) + 'px'
// 画笔
const ctx = canvas.getContext('2d')
// 设置笔触样式,font的样式一定要在measureText方法调用前
ctx.font = '16px 微软雅黑'
ctx.fillStyle = '#ddd'
ctx.textAlign = 'left'
ctx.textBaseline = 'middle'
// 计算字符串的横向与纵向间距,设置了font的大小与字体
const offsetX = Math.ceil(ctx.measureText(text).width) + 60
const offsetY = 110
// 列/行数
const rowCount = Math.ceil(minSizeCanvas / offsetY)
const colCount = Math.ceil(minSizeCanvas / offsetX)
// 旋转
ctx.translate(minSizeCanvas / 2, minSizeCanvas / 2)
ctx.rotate(rotate * Math.PI / 180)
ctx.translate(-minSizeCanvas / 2, -minSizeCanvas / 2)
// 行
for(let i=0; i<rowCount; i++) {
// 列
for(let j=0; j<colCount; j++) {
ctx.fillText(text, offsetX * j, offsetY * i)
}
}
}
useEffect(() => {
window.addEventListener('resize', setCanvasData)
return function cleanup() {
window.removeEventListener('resize', setCanvasData)
}
}, [])
useEffect(() => {
setCanvasData()
}, [text, rotate])
return (
<canvas ref={canvasRef} />
)
}
Watermark.propTypes = {
text: PropTypes.string.isRequired,
rotate: PropTypes.number
}
二、使用canvas的toDataURL方法导出图片的base64的代码,使用css background-repeat属性将图片作为背景进行填充。
import React from "react"
import styles from './scss/wm.module.scss'
const createImage = function (text) {
const canvas = document.createElement('canvas')
// 画笔
const ctx = canvas.getContext('2d')
// 修改画布在容器中的偏移,使其中心位置与容器的中心位置永远保持一致
// 在旋转角度时也要以其中心点为中心进行旋转
canvas.style.display = 'none'
// 设置笔触样式,font的样式一定要在measureText方法调用前
ctx.font = '16px 微软雅黑'
ctx.fillStyle = '#ddd'
ctx.textAlign = 'left'
ctx.textBaseline = 'middle'
// 计算字符串的横向与纵向间距,设置了font的大小与字体
const offsetX = Math.ceil(ctx.measureText(text).width) + 60
// 设置画面的最小尺寸
canvas.setAttribute('width', String(offsetX))
// 高度可以增加行间距
canvas.setAttribute('height', String(100))
ctx.fillText(text, 0, 8)
return canvas.toDataURL('image/png')
}
function WaterMark({ text, rotate }) {
const [count, setCount] = React.useState(0)
const onResize = React.useCallback(() => {
// 设置画布大小
const canvasParent = document.body
// 画布大小需要为容器的对角线长度,
// 画布在旋转时不会出现对角空白无水印!!!
const maxSizeContainer = Math.max(canvasParent.offsetWidth, canvasParent.offsetHeight)
const minSizeCanvas = Math.ceil(Math.SQRT2 * maxSizeContainer)
setCount(minSizeCanvas)
}, [])
React.useEffect(() => {
onResize()
window.addEventListener('resize', onResize)
return () => {
window.removeEventListener('resize', onResize)
}
}, [])
return (
<div className={styles.container} style={{
backgroundImage: `url(${createImage(text)})`,
height: count,
width: count
}} />
)
}
export default WaterMark
.container {
transform: rotate(-30deg) translate(-20%, -40%);
background-repeat: repeat;
width: 200%;
min-width: 200%;
height: 200%;
min-height: 200%;
}