@opentiny/vue-renderless
Version:
An enterprise-class UI component library, support both Vue.js 2 and Vue.js 3, as well as PC and mobile.
214 lines (213 loc) • 7.15 kB
JavaScript
import {
__spreadProps,
__spreadValues
} from "../chunk-G2ADBYYC.js";
const SelfFontGap = 3;
const getMarkSize = ({ props, ctx, state }) => {
const { content } = props;
const { fontSize, fontFamily } = state.font;
let defaultWidth = 120;
let defaultHeight = 64;
if (!props.image && ctx.measureText) {
ctx.font = `${Number(fontSize)}px ${fontFamily}`;
const contents = Array.isArray(content) ? content : [content];
const widths = contents.map((item) => ctx.measureText(item).width);
defaultWidth = Math.ceil(Math.max(...widths));
defaultHeight = Number(fontSize) * contents.length + (contents.length - 1) * SelfFontGap;
}
return [Math.max(props.width, defaultWidth), Math.max(props.height, defaultHeight)];
};
const selfDrawText = ({ props, state, ctx, canvasWidth, canvasHeight, ratios }) => {
const { rotate, content } = props;
const { fontSize, color, fontStyle, fontWeight, fontFamily } = state.font;
const devicePixelRatio = window.devicePixelRatio || 1;
const mergedFontSize = Number(fontSize) * devicePixelRatio;
const drawHeight = canvasHeight / devicePixelRatio;
ctx.font = `${fontStyle} normal ${fontWeight} ${mergedFontSize}px/${drawHeight}px ${fontFamily}`;
ctx.fillStyle = color;
ctx.textAlign = "center";
ctx.textBaseline = "bottom";
ratios.forEach((ratio) => {
ctx.save();
ctx.translate(canvasWidth / ratio, canvasHeight / ratio);
ctx.rotate(Math.PI / 180 * rotate);
const contents = Array.isArray(content) ? content : [content];
const fontHeight = mergedFontSize + SelfFontGap * devicePixelRatio;
const startHeight = (canvasHeight - contents.length * fontHeight) / 2 - canvasHeight / 2;
contents == null ? void 0 : contents.forEach((item, index) => {
ctx.fillText(item || "", 0, startHeight + (index + 1) * fontHeight);
});
ctx.restore();
});
};
const imageLoad = (props, state, ctx, result, canvas) => {
const { image, width, height, rotate, interlaced } = props;
const img = new Image();
return new Promise((resolve) => {
img.onload = () => {
const ratios = interlaced ? [4, 4 / 3] : [2];
ratios.forEach((ratio) => {
ctx.save();
ctx.translate(result.size.width / ratio, result.size.height / ratio);
ctx.rotate(Math.PI / 180 * rotate);
ctx.drawImage(img, -width / 2, -height / 2, width, height);
ctx.restore();
});
resolve(__spreadValues({
base64: canvas.toDataURL()
}, result));
};
img.onerror = () => {
const propsTmp = props.content ? props : __spreadProps(__spreadValues({}, props), {
content: "Invalid data"
});
if (interlaced) {
selfDrawText({
props,
ctx,
state,
canvasWidth: result.size.width,
canvasHeight: result.size.height,
ratios: [4, 4 / 3]
});
} else {
selfDrawText({
props: propsTmp,
ctx,
state,
canvasWidth: result.size.width,
canvasHeight: result.size.height,
ratios: [2]
});
}
resolve(__spreadValues({
base64: canvas.toDataURL()
}, result));
};
img.crossOrigin = "anonymous";
img.referrerPolicy = "no-referrer";
img.src = image;
});
};
const useWatermarkBg = function(props, state) {
const { gap, image, interlaced } = props;
const [gapX, gapY] = gap;
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const devicePixelRatio = window.devicePixelRatio || 1;
const [markWidth, markHeight] = getMarkSize({ props, state, ctx });
const singleWidth = (gapX + markWidth) * devicePixelRatio;
const singleHeight = (gapY + markHeight) * devicePixelRatio;
const canvasWidth = interlaced ? singleWidth * 2 : singleWidth;
const canvasHeight = interlaced ? singleHeight * 2 : singleHeight;
canvas.width = canvasWidth;
canvas.height = canvasHeight;
const result = {
size: {
width: canvasWidth,
height: canvasHeight
},
styleSize: {
width: canvasWidth / devicePixelRatio,
height: canvasHeight / devicePixelRatio
}
};
if (image) {
return imageLoad(props, state, ctx, result, canvas);
} else {
if (interlaced) {
selfDrawText({ props, state, ctx, canvasWidth, canvasHeight, ratios: [4, 4 / 3] });
} else {
selfDrawText({ props, state, ctx, canvasWidth, canvasHeight, ratios: [2] });
}
}
return new Promise(
(resolve) => resolve(__spreadValues({
base64: canvas.toDataURL()
}, result))
);
};
const updateWatermark = (watermarkBg, props) => {
const { base64, styleSize } = watermarkBg;
const watermarkDiv = document.createElement("div");
watermarkDiv.style.position = "absolute";
watermarkDiv.style.inset = "0";
watermarkDiv.style.backgroundImage = `url(${base64})`;
watermarkDiv.style.backgroundSize = `${styleSize.width}px ${styleSize.height}px`;
watermarkDiv.style.backgroundRepeat = "repeat";
watermarkDiv.style.backgroundPosition = `${props.offset[0] || 20}px ${(props == null ? void 0 : props.offset[1]) || 20}px`;
watermarkDiv.style.width = "100%";
watermarkDiv.style.height = "100%";
watermarkDiv.style.pointerEvents = "none";
watermarkDiv.style.zIndex = `${props.zIndex}`;
return watermarkDiv;
};
async function setSlotDivStyle({ vm, props }) {
const parentRef = vm.$refs.parentRef;
if (!parentRef) {
return;
}
for (let child of parentRef.children) {
child.style.position = "relative";
child.style.zIndex = `${props.zIndex + 1}`;
}
}
async function createWatermark({ vm, state, props }) {
const parentRef = vm.$refs.parentRef;
if (!parentRef) {
return;
}
const bgValue = await useWatermarkBg(props, state);
if (state.watermarkDiv) {
state.watermarkDiv.remove();
}
state.watermarkDiv = updateWatermark(bgValue, props);
parentRef.appendChild(state.watermarkDiv);
}
const reRenderWatermark = ({ state }) => () => {
state.flag++;
};
const mounted = ({ vm, state, props }) => () => {
setSlotDivStyle({ vm, props });
createWatermark({ vm, state, props });
state.observerInstance = new MutationObserver((records) => {
for (const record of records) {
for (const dom of Array.from(record.removedNodes)) {
if (dom === state.watermarkDiv) {
state.flag++;
return;
}
}
if (record.type === "attributes" && record.target === state.watermarkDiv) {
state.flag++;
return;
}
}
});
const parentRef = vm.$refs.parentRef;
if (parentRef) {
state.observerInstance.observe(parentRef, {
childList: true,
attributes: true,
subtree: true
});
if (props.customClass) {
parentRef.classList.add(props.customClass);
}
}
};
const unmounted = ({ state }) => () => {
if (state.observerInstance) {
state.observerInstance.disconnect();
}
state.watermarkDiv = null;
};
const watchProps = ({ state, vm, props }) => () => createWatermark({ state, vm, props });
export {
mounted,
reRenderWatermark,
unmounted,
updateWatermark,
useWatermarkBg,
watchProps
};