You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

116 lines
2.9 KiB

  1. import {
  2. ref,
  3. Ref,
  4. unref,
  5. shallowRef,
  6. onBeforeUnmount,
  7. getCurrentInstance
  8. } from "vue";
  9. import { isDef } from "/@/utils/is";
  10. import { useRafThrottle } from "/@/utils/operate";
  11. import { addResizeListener, removeResizeListener } from "/@/utils/resize";
  12. const domSymbol = Symbol("watermark-dom");
  13. type attr = {
  14. font?: string;
  15. fillStyle?: string;
  16. };
  17. export function useWatermark(
  18. appendEl: Ref<HTMLElement | null> = ref(document.body) as Ref<HTMLElement>
  19. ) {
  20. const func = useRafThrottle(function () {
  21. const el = unref(appendEl);
  22. if (!el) return;
  23. const { clientHeight: height, clientWidth: width } = el;
  24. updateWatermark({ height, width });
  25. });
  26. const id = domSymbol.toString();
  27. const watermarkEl = shallowRef<HTMLElement>();
  28. const clear = () => {
  29. const domId = unref(watermarkEl);
  30. watermarkEl.value = undefined;
  31. const el = unref(appendEl);
  32. if (!el) return;
  33. domId && el.removeChild(domId);
  34. removeResizeListener(el, func);
  35. };
  36. function createBase64(str: string, attr?: attr) {
  37. const can = document.createElement("canvas");
  38. const width = 300;
  39. const height = 240;
  40. Object.assign(can, { width, height });
  41. const cans = can.getContext("2d");
  42. if (cans) {
  43. cans.rotate((-20 * Math.PI) / 120);
  44. cans.font = attr?.font ?? "15px Reggae One";
  45. cans.fillStyle = attr?.fillStyle ?? "rgba(0, 0, 0, 0.15)";
  46. cans.textAlign = "left";
  47. cans.textBaseline = "middle";
  48. cans.fillText(str, width / 20, height);
  49. }
  50. return can.toDataURL("image/png");
  51. }
  52. function updateWatermark(
  53. options: {
  54. width?: number;
  55. height?: number;
  56. str?: string;
  57. attr?: attr;
  58. } = {}
  59. ) {
  60. const el = unref(watermarkEl);
  61. if (!el) return;
  62. if (isDef(options.width)) {
  63. el.style.width = `${options.width}px`;
  64. }
  65. if (isDef(options.height)) {
  66. el.style.height = `${options.height}px`;
  67. }
  68. if (isDef(options.str)) {
  69. el.style.background = `url(${createBase64(
  70. options.str,
  71. options.attr
  72. )}) left top repeat`;
  73. }
  74. }
  75. const createWatermark = (str: string, attr?: attr) => {
  76. if (unref(watermarkEl)) {
  77. updateWatermark({ str, attr });
  78. return id;
  79. }
  80. const div = document.createElement("div");
  81. watermarkEl.value = div;
  82. div.id = id;
  83. div.style.pointerEvents = "none";
  84. div.style.top = "0px";
  85. div.style.left = "0px";
  86. div.style.position = "absolute";
  87. div.style.zIndex = "100000";
  88. const el = unref(appendEl);
  89. if (!el) return id;
  90. const { clientHeight: height, clientWidth: width } = el;
  91. updateWatermark({ str, width, height, attr });
  92. el.appendChild(div);
  93. return id;
  94. };
  95. function setWatermark(str: string, attr?: attr) {
  96. createWatermark(str, attr);
  97. addResizeListener(document.documentElement, func);
  98. const instance = getCurrentInstance();
  99. if (instance) {
  100. onBeforeUnmount(() => {
  101. clear();
  102. });
  103. }
  104. }
  105. return { setWatermark, clear };
  106. }