UNPKG

@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
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 };