227 lines
7.8 KiB
JavaScript
227 lines
7.8 KiB
JavaScript
import { defineComponent, computed, shallowRef, ref, onMounted, watch, onBeforeUnmount, openBlock, createElementBlock, normalizeStyle, renderSlot } from 'vue';
|
|
import { useMutationObserver } from '@vueuse/core';
|
|
import { watermarkProps } from './watermark.mjs';
|
|
import { reRendering, getStyleStr, getPixelRatio } from './utils.mjs';
|
|
import useClips, { FontGap } from './useClips.mjs';
|
|
import _export_sfc from '../../../_virtual/plugin-vue_export-helper.mjs';
|
|
import { isArray } from '@vue/shared';
|
|
import { isUndefined } from '../../../utils/types.mjs';
|
|
|
|
const __default__ = defineComponent({
|
|
name: "ElWatermark"
|
|
});
|
|
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
...__default__,
|
|
props: watermarkProps,
|
|
setup(__props) {
|
|
const props = __props;
|
|
const style = {
|
|
position: "relative"
|
|
};
|
|
const color = computed(() => {
|
|
var _a, _b;
|
|
return (_b = (_a = props.font) == null ? void 0 : _a.color) != null ? _b : "rgba(0,0,0,.15)";
|
|
});
|
|
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 textBaseline = computed(() => {
|
|
var _a, _b;
|
|
return (_b = (_a = props.font) == null ? void 0 : _a.textBaseline) != null ? _b : "hanging";
|
|
});
|
|
const gapX = computed(() => props.gap[0]);
|
|
const gapY = computed(() => props.gap[1]);
|
|
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 getMarkStyle = () => {
|
|
const markStyle = {
|
|
zIndex: props.zIndex,
|
|
position: "absolute",
|
|
left: 0,
|
|
top: 0,
|
|
width: "100%",
|
|
height: "100%",
|
|
pointerEvents: "none",
|
|
backgroundRepeat: "repeat"
|
|
};
|
|
let positionLeft = offsetLeft.value - gapXCenter.value;
|
|
let positionTop = offsetTop.value - gapYCenter.value;
|
|
if (positionLeft > 0) {
|
|
markStyle.left = `${positionLeft}px`;
|
|
markStyle.width = `calc(100% - ${positionLeft}px)`;
|
|
positionLeft = 0;
|
|
}
|
|
if (positionTop > 0) {
|
|
markStyle.top = `${positionTop}px`;
|
|
markStyle.height = `calc(100% - ${positionTop}px)`;
|
|
positionTop = 0;
|
|
}
|
|
markStyle.backgroundPosition = `${positionLeft}px ${positionTop}px`;
|
|
return markStyle;
|
|
};
|
|
const containerRef = shallowRef(null);
|
|
const watermarkRef = shallowRef();
|
|
const stopObservation = ref(false);
|
|
const destroyWatermark = () => {
|
|
if (watermarkRef.value) {
|
|
watermarkRef.value.remove();
|
|
watermarkRef.value = void 0;
|
|
}
|
|
};
|
|
const appendWatermark = (base64Url, markWidth) => {
|
|
var _a;
|
|
if (containerRef.value && watermarkRef.value) {
|
|
stopObservation.value = true;
|
|
watermarkRef.value.setAttribute("style", getStyleStr({
|
|
...getMarkStyle(),
|
|
backgroundImage: `url('${base64Url}')`,
|
|
backgroundSize: `${Math.floor(markWidth)}px`
|
|
}));
|
|
(_a = containerRef.value) == null ? void 0 : _a.append(watermarkRef.value);
|
|
setTimeout(() => {
|
|
stopObservation.value = false;
|
|
});
|
|
}
|
|
};
|
|
const getMarkSize = (ctx) => {
|
|
let defaultWidth = 120;
|
|
let defaultHeight = 64;
|
|
let space = 0;
|
|
const { image, content, width, height, rotate } = props;
|
|
if (!image && ctx.measureText) {
|
|
ctx.font = `${Number(fontSize.value)}px ${fontFamily.value}`;
|
|
const contents = isArray(content) ? content : [content];
|
|
let maxWidth = 0;
|
|
let maxHeight = 0;
|
|
contents.forEach((item) => {
|
|
const {
|
|
width: width2,
|
|
fontBoundingBoxAscent,
|
|
fontBoundingBoxDescent,
|
|
actualBoundingBoxAscent,
|
|
actualBoundingBoxDescent
|
|
} = ctx.measureText(item);
|
|
const height2 = isUndefined(fontBoundingBoxAscent) ? actualBoundingBoxAscent + actualBoundingBoxDescent : fontBoundingBoxAscent + fontBoundingBoxDescent;
|
|
if (width2 > maxWidth)
|
|
maxWidth = Math.ceil(width2);
|
|
if (height2 > maxHeight)
|
|
maxHeight = Math.ceil(height2);
|
|
});
|
|
defaultWidth = maxWidth;
|
|
defaultHeight = maxHeight * contents.length + (contents.length - 1) * FontGap;
|
|
const angle = Math.PI / 180 * Number(rotate);
|
|
space = Math.ceil(Math.abs(Math.sin(angle) * defaultHeight) / 2);
|
|
defaultWidth += space;
|
|
}
|
|
return [width != null ? width : defaultWidth, height != null ? height : defaultHeight, space];
|
|
};
|
|
const getClips = useClips();
|
|
const renderWatermark = () => {
|
|
const canvas = document.createElement("canvas");
|
|
const ctx = canvas.getContext("2d");
|
|
const image = props.image;
|
|
const content = props.content;
|
|
const rotate = props.rotate;
|
|
if (ctx) {
|
|
if (!watermarkRef.value) {
|
|
watermarkRef.value = document.createElement("div");
|
|
}
|
|
const ratio = getPixelRatio();
|
|
const [markWidth, markHeight, space] = getMarkSize(ctx);
|
|
const drawCanvas = (drawContent) => {
|
|
const [textClips, clipWidth] = getClips(drawContent || "", rotate, ratio, markWidth, markHeight, {
|
|
color: color.value,
|
|
fontSize: fontSize.value,
|
|
fontStyle: fontStyle.value,
|
|
fontWeight: fontWeight.value,
|
|
fontFamily: fontFamily.value,
|
|
textAlign: textAlign.value,
|
|
textBaseline: textBaseline.value
|
|
}, gapX.value, gapY.value, space);
|
|
appendWatermark(textClips, clipWidth);
|
|
};
|
|
if (image) {
|
|
const img = new Image();
|
|
img.onload = () => {
|
|
drawCanvas(img);
|
|
};
|
|
img.onerror = () => {
|
|
drawCanvas(content);
|
|
};
|
|
img.crossOrigin = "anonymous";
|
|
img.referrerPolicy = "no-referrer";
|
|
img.src = image;
|
|
} else {
|
|
drawCanvas(content);
|
|
}
|
|
}
|
|
};
|
|
onMounted(() => {
|
|
renderWatermark();
|
|
});
|
|
watch(() => props, () => {
|
|
renderWatermark();
|
|
}, {
|
|
deep: true,
|
|
flush: "post"
|
|
});
|
|
onBeforeUnmount(() => {
|
|
destroyWatermark();
|
|
});
|
|
const onMutate = (mutations) => {
|
|
if (stopObservation.value) {
|
|
return;
|
|
}
|
|
mutations.forEach((mutation) => {
|
|
if (reRendering(mutation, watermarkRef.value)) {
|
|
destroyWatermark();
|
|
renderWatermark();
|
|
}
|
|
});
|
|
};
|
|
useMutationObserver(containerRef, onMutate, {
|
|
attributes: true,
|
|
subtree: true,
|
|
childList: true
|
|
});
|
|
return (_ctx, _cache) => {
|
|
return openBlock(), createElementBlock("div", {
|
|
ref_key: "containerRef",
|
|
ref: containerRef,
|
|
style: normalizeStyle([style])
|
|
}, [
|
|
renderSlot(_ctx.$slots, "default")
|
|
], 4);
|
|
};
|
|
}
|
|
});
|
|
var Watermark = /* @__PURE__ */ _export_sfc(_sfc_main, [["__file", "watermark.vue"]]);
|
|
|
|
export { Watermark as default };
|
|
//# sourceMappingURL=watermark2.mjs.map
|