<template>
  <div ref="watermarkContainerRef" class="watermark-container">
    <!--  插槽-->
    <slot></slot>
  </div>
</template>

<script setup>
import { ref, onMounted, watchEffect, onUnmounted, computed } from "vue";
// 使用 defineProps 定义一个组件的 props，这些 props 描述了组件从父组件接收的属性
const props = defineProps({
  // 文本内容，类型为字符串，必须提供，默认值为'张苹果博客'
  text: {
    type: Array,
    required: true,
    default: []
  },
  // 字体大小，类型为数字，默认值为10
  fontSize: {
    type: Number,
    default: 15
  },
  // 间距，类型为数字，默认值为2
  gap: {
    type: Number,
    default: 2
  },
  // 颜色，类型为字符串，默认值为' rgba(136, 135, 135, 0.58)'
  color: {
    type: String,
    default: "rgba(136, 135, 135, 0.58)"
  },
  //角度 类型为数字 默认值 -45
  angle: {
    type: Number,
    default: -45
  }
});
/**
 * 绘制多行文本
 * @param ctx canvas对象
 * @param words 文本数组
 * @param x 坐标
 * @param y 坐标(行高)
 * @param maxWidth 最大宽度
 */
function drawTextWithWrap(ctx, words, x, y, maxWidth) {
  let line = "";
  const row = JSON.parse(JSON.stringify(y)); //拷贝一份Y的 行间距
  for (let n = 0; n < words.length; n++) {
    const testLine = line + words[n];
    const metrics = ctx.measureText(testLine);
    const testWidth = metrics.width;
    if (testWidth > maxWidth) {
      ctx.fillText(line, x, y);
      line = words[n];
      y = row * (n + 1);
    } else {
      line = testLine;
    }
  }
  ctx.fillText(line, x, y);
}
// 定义一个用于绘制水印的函数，这里可以封装一下单独引入。
// 它是一个计算属性，意味着它的值会根据其依赖的 props 的变化而自动重新计算
const waterMarkBg = (props) => {
  return computed(() => {
    // 创建一个新的 canvas 元素
    const canvas = document.createElement("canvas");
    // 获取设备的像素比，如果未定义则默认为1
    const devicePixelRatio = window.devicePixelRatio || 1;
    // 根据像素比计算字体大小
    const fontSize = props.fontSize * devicePixelRatio;
    // 设置字体样式
    const font = fontSize + "px serif";
    // 获取 canvas 的 2D 渲染上下文
    const ctx = canvas.getContext("2d");
    // 设置字体
    ctx.font = font;
    // 测量文本的宽度
    // const { width } = ctx.measureText(props.text);
    // 计算 canvas 的大小，至少为 60，并根据文本宽度和间距因子进行调整
    const canvasSize = Math.max(60, 180) * props.gap + devicePixelRatio;
    // 设置 canvas 的宽高
    canvas.width = canvasSize;
    canvas.height = canvasSize;
    // 将 canvas 的原点移动到中心
    ctx.translate(canvas.width / 5, canvas.height / 5);
    // 旋转 canvas 45 度
    ctx.rotate((Math.PI / 180) * props.angle);
    // 设置填充颜色
    ctx.fillStyle = props.color;
    // 设置文本对齐方式和基线
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    // 再次设置字体
    ctx.font = font;
    drawTextWithWrap(ctx, props.text, 0, 25, 100);
    // 在 canvas 上填充文本
    // ctx.fillText(props.text, 0, 0);

    // 返回一个对象，包含 base64 编码的图片数据、canvas 的大小和样式尺寸
    return {
      base64: canvas.toDataURL(),
      size: canvasSize,
      styleSize: canvasSize / devicePixelRatio
    };
  });
};

// 用于存储 MutationObserver 的变量
let ob;
// 用于存储水印 div 的变量
let div;
// 调用 waterMarkBg 函数获取水印相关的计算属性
const bg = waterMarkBg(props);
// 创建一个 ref 用于存储水印容器的 DOM 引用
const watermarkContainerRef = ref("");
// 创建一个 ref 用于标记水印是否需要重新生成
const flag = ref(0);

// 在组件挂载后执行
onMounted(() => {
  // 创建一个新的 MutationObserver，用于监听水印容器的变化
  ob = new MutationObserver((records) => {
    // 遍历所有的变化记录
    for (const record of records) {
      // 遍历所有被移除的节点
      for (const dom of record.removedNodes) {
        // 如果被移除的节点是水印 div，则更新 flag 的值并返回
        if (dom === div) {
          flag.value++;
          return;
        }
      }
      // 如果变化的节点就是水印 div，则更新 flag 的值并返回
      if (record.target === div) {
        flag.value++;
        return;
      }
    }
  });
  // 包括子节点的变化、属性的变化以及子树的变化
  ob.observe(watermarkContainerRef.value, {
    childList: true,
    attributes: true,
    subtree: true
  });
});

//卸载
onUnmounted(() => {
  ob && ob.disconnect();
  div = null;
});

// 生成水印
watchEffect(() => {
  // 触发 watchEffect 的重新执行
  flag.value;
  // 如果水印容器没有值，则直接返回，不执行后续操作
  if (!watermarkContainerRef.value) {
    return;
  }
  // 如果之前已经存在水印 div，则先移除它
  if (div) {
    div.remove();
  }
  // 创建一个新的 div 元素用于作为水印的容器
  div = document.createElement("div");
  // 从计算属性 bg 中获取 base64 编码的图片数据和样式尺寸
  const { base64, styleSize } = bg.value;
  // 设置 div 的背景图片为水印图片的 base64 编码
  div.style.backgroundImage = `url(${base64})`;
  // 设置背景图片的尺寸
  div.style.backgroundSize = `${styleSize}px ${styleSize}px`;
  // 设置背景图片重复显示
  div.style.backgroundRepeat = "repeat";
  // 设置水印 div 的 z-index 为 9999，以确保它显示在大多数其他元素之上
  div.style.zIndex = 9999;
  // 设置水印 div 不响应鼠标事件，如点击、悬停等
  div.style.pointerEvents = "none";
  // 设置水印 div 的位置为绝对定位
  div.style.position = "absolute";
  // 使用 inset 属性设置 div 占据整个父容器的空间
  div.style.inset = "0";
  // 将水印 div 添加到水印容器中
  watermarkContainerRef.value.appendChild(div);
});
</script>

<style scoped>
.watermark-container {
  position: relative;
  height: 100vh;
  color: rgba(136, 135, 135, 0.58);
}
</style>
