@arco-design/web-vue
Version:
Arco Design Vue 2.0: A Vue.js 3 UI Library
267 lines (266 loc) • 9.04 kB
JavaScript
import { defineComponent, toRefs, shallowRef, ref, computed, onMounted, watch, createVNode, mergeProps } from "vue";
import { getPrefixCls } from "../_utils/global-config.js";
import { useMutationObserver } from "./hooks/use-mutation-observer.js";
import { useTheme } from "./hooks/use-theme.js";
import { canvasToGray, styleToString } from "./utils/index.js";
import { isArray } from "../_utils/is.js";
var _Watermark = defineComponent({
name: "Watermark",
props: {
content: {
type: [String, Array]
},
image: {
type: String
},
width: {
type: Number
},
height: {
type: Number
},
gap: {
type: Array,
default: () => [90, 90]
},
offset: {
type: Array
},
rotate: {
type: Number,
default: -22
},
font: {
type: Object
},
zIndex: {
type: Number,
default: 6
},
alpha: {
type: Number,
default: 1
},
antiTamper: {
type: Boolean,
default: true
},
grayscale: {
type: Boolean,
default: false
},
repeat: {
type: Boolean,
default: true
},
staggered: {
type: Boolean,
default: true
}
},
setup(props, {
slots,
attrs
}) {
const {
width,
height,
image,
rotate,
alpha,
repeat,
grayscale
} = toRefs(props);
const prefixCls = getPrefixCls("watermark");
const ratio = window.devicePixelRatio || 1;
const containerRef = shallowRef();
const watermarkMap = ref(/* @__PURE__ */ new Map());
const fontSize = computed(() => {
var _a, _b;
return (_b = (_a = props.font) == null ? void 0 : _a.fontSize) != null ? _b : 16;
});
const fontWeight = computed(() => {
var _a, _b;
return (_b = (_a = props.font) == null ? void 0 : _a.fontWeight) != null ? _b : "normal";
});
const fontStyle = computed(() => {
var _a, _b;
return (_b = (_a = props.font) == null ? void 0 : _a.fontStyle) != null ? _b : "normal";
});
const fontFamily = computed(() => {
var _a, _b;
return (_b = (_a = props.font) == null ? void 0 : _a.fontFamily) != null ? _b : "sans-serif";
});
const textAlign = computed(() => {
var _a, _b;
return (_b = (_a = props.font) == null ? void 0 : _a.textAlign) != null ? _b : "center";
});
const contents = computed(() => isArray(props.content) ? props.content : [props.content]);
const color = computed(() => {
var _a, _b;
return (_b = (_a = props.font) == null ? void 0 : _a.color) != null ? _b : theme.value === "dark" ? "rgba(255, 255, 255, 0.15)" : "rgba(0, 0, 0, 0.15)";
});
const gapX = computed(() => {
var _a, _b;
return (_b = (_a = props.gap) == null ? void 0 : _a[0]) != null ? _b : 90;
});
const gapY = computed(() => {
var _a, _b;
return (_b = (_a = props.gap) == null ? void 0 : _a[1]) != null ? _b : 90;
});
const gapXCenter = computed(() => gapX.value / 2);
const gapYCenter = computed(() => gapY.value / 2);
const offsetLeft = computed(() => {
var _a, _b;
return (_b = (_a = props.offset) == null ? void 0 : _a[0]) != null ? _b : gapXCenter.value;
});
const offsetTop = computed(() => {
var _a, _b;
return (_b = (_a = props.offset) == null ? void 0 : _a[1]) != null ? _b : gapYCenter.value;
});
const markStyle = computed(() => {
var _a;
const left = offsetLeft.value - gapXCenter.value;
const top = offsetTop.value - gapYCenter.value;
return {
position: "absolute",
left: left > 0 ? `${left}px` : 0,
top: top > 0 ? `${top}px` : 0,
width: left > 0 ? `calc(100% - ${left}px)` : "100%",
height: top > 0 ? `calc(100% - ${top}px)` : "100%",
pointerEvents: "none",
backgroundRepeat: props.repeat ? "repeat" : "no-repeat",
backgroundPosition: `${left > 0 ? 0 : left}px ${top > 0 ? 0 : top}px`,
zIndex: (_a = props.zIndex) != null ? _a : 6
};
});
const isStaggered = computed(() => props.repeat && props.staggered);
const appendWatermark = (base64, width2) => {
var _a;
if (containerRef.value) {
const watermarkEle = watermarkMap.value.get(containerRef.value);
if (watermarkEle) {
if (containerRef.value.contains(watermarkEle)) {
containerRef.value.removeChild(watermarkEle);
}
watermarkMap.value.delete(containerRef.value);
}
const newWatermarkEle = document.createElement("div");
newWatermarkEle.setAttribute("style", styleToString({
...markStyle.value,
backgroundImage: `url('${base64}')`,
backgroundSize: `${width2}px`
}));
(_a = containerRef.value) == null ? void 0 : _a.append(newWatermarkEle);
watermarkMap.value.set(containerRef.value, newWatermarkEle);
}
};
const getMarkSize = (ctx) => {
var _a, _b;
let defaultWidth = 120;
let defaultHeight = 28;
if (!image.value && ctx.measureText) {
ctx.font = `${fontSize.value}px ${fontFamily.value}`;
const widths = contents.value.map((item) => ctx.measureText(item).width);
defaultWidth = Math.ceil(Math.max(...widths));
defaultHeight = fontSize.value * contents.value.length + (contents.value.length - 1) * 3;
}
return [(_a = width.value) != null ? _a : defaultWidth, (_b = height.value) != null ? _b : defaultHeight];
};
const renderWatermark = () => {
var _a;
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (!ctx)
return;
const [markWidth, markheight] = getMarkSize(ctx);
const realMarkWidth = markWidth * ratio;
const realMarkHeight = markheight * ratio;
const canvasWidth = (gapX.value + markWidth) * ratio;
const canvasHeight = (gapY.value + markheight) * ratio;
const drawX = gapX.value / 2 * ratio;
const drawY = gapY.value / 2 * ratio;
const rotateX = canvasWidth / 2;
const rotateY = canvasHeight / 2;
const baseSize = isStaggered.value ? 2 : 1;
const fillWidth = (gapX.value + markWidth) * baseSize;
canvas.width = canvasWidth * baseSize;
canvas.height = canvasHeight * baseSize;
ctx.globalAlpha = alpha.value;
ctx.save();
ctx.translate(rotateX, rotateY);
ctx.rotate(Math.PI / 180 * rotate.value);
ctx.translate(-rotateX, -rotateY);
const drawImage = () => {
ctx.restore();
if (isStaggered.value) {
ctx.drawImage(canvas, 0, 0, canvasWidth, canvasHeight, canvasWidth, canvasHeight, canvasWidth, canvasHeight);
}
grayscale.value && canvasToGray(canvas);
appendWatermark(canvas.toDataURL(), fillWidth);
};
if (image.value) {
const img = new Image();
img.onload = () => {
ctx.drawImage(img, drawX, drawY, realMarkWidth, realMarkHeight);
drawImage();
};
img.crossOrigin = "anonymous";
img.referrerPolicy = "no-referrer";
img.src = image.value;
} else {
const mergedFontSize = Number(fontSize.value) * ratio;
ctx.font = `${fontStyle.value} normal ${fontWeight.value} ${mergedFontSize}px/${markheight}px ${fontFamily.value}`;
ctx.fillStyle = color.value;
ctx.textAlign = textAlign.value;
ctx.textBaseline = "top";
ctx.translate(realMarkWidth / 2, 0);
(_a = contents.value) == null ? void 0 : _a.forEach((item, index) => {
ctx.fillText(item != null ? item : "", drawX, drawY + index * (mergedFontSize + 3 * ratio));
});
drawImage();
}
};
const isWatermarkEle = (ele) => Array.from(watermarkMap.value.values()).includes(ele);
const handleMutations = (mutations) => {
if (!props.antiTamper)
return;
for (const mutation of mutations) {
const isRemoved = Array.from(mutation.removedNodes).some((node) => isWatermarkEle(node));
const isModified = mutation.type === "attributes" && isWatermarkEle(mutation.target);
if (isRemoved || isModified) {
renderWatermark();
break;
}
}
};
const {
theme
} = useTheme(renderWatermark);
onMounted(() => {
renderWatermark();
useMutationObserver(containerRef.value, handleMutations, {
attributes: true,
childList: true,
characterData: true,
subtree: true
});
});
watch(props, renderWatermark, {
deep: true,
flush: "post"
});
return () => {
var _a;
return createVNode("div", mergeProps({
"ref": containerRef,
"class": prefixCls,
"style": {
position: "relative",
overflow: "hidden"
}
}, attrs), [(_a = slots.default) == null ? void 0 : _a.call(slots)]);
};
}
});
export { _Watermark as default };