comic-plus
Version:
<p align="center"> <img width="200px" src="./logo.png"/> </p>
186 lines (185 loc) • 6.78 kB
JavaScript
;
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
const vue = require("vue");
const main_props = require("./main.props.js");
const util = require("../util/util.js");
const Watermark = vue.defineComponent({
name: "CuWatermark",
props: main_props.watermarkProps,
setup(props, { slots }) {
const containerRef = vue.ref();
const watermarkRef = vue.ref();
const getMarkStyle = vue.computed(() => {
const [gapX, gapY] = props.gap;
const [offsetX, offsetY] = props.offset;
const gapXCenter = gapX / 2;
const gapYCenter = gapY / 2;
const offsetTop = offsetY || gapYCenter;
const offsetLeft = offsetX || gapXCenter;
const markStyle = {
zIndex: props.zIndex,
position: "absolute",
left: 0,
top: 0,
width: "100%",
height: "100%",
pointerEvents: "none",
backgroundRepeat: "repeat"
};
let positionLeft = offsetLeft - gapXCenter;
let positionTop = offsetTop - gapYCenter;
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 assignFont = vue.computed(() => {
return Object.assign(
{
fontSize: 16,
fontFamily: "sans-serif",
fontStyle: "normal",
fontWeight: "normal",
color: "rgba(0, 0, 0, 0.15)"
},
props.font
);
});
function destroyWatermark() {
if (!watermarkRef.value) return;
watermarkRef.value.remove();
watermarkRef.value = null;
}
function getMarkSize(ctx) {
const { fontSize, fontFamily } = assignFont.value;
let defaultWidth, defaultHeight;
const content = props.content;
const image = props.image;
const width = props.width;
const height = props.height;
if (!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) * 3;
}
return [width ?? defaultWidth, height ?? defaultHeight];
}
function rotateWatermark(ctx, rotateX, rotateY, rotate) {
ctx.translate(rotateX, rotateY);
ctx.rotate(Math.PI / 180 * Number(rotate));
ctx.translate(-rotateX, -rotateY);
}
function fillTexts(ctx, drawX, drawY, drawWidth, drawHeight) {
const { fontSize, fontFamily, fontStyle, fontWeight, color } = assignFont.value;
const ratio = util.getPixelRatio();
const content = props.content;
const mergedFontSize = Number(fontSize) * ratio;
ctx.font = `${fontStyle} normal ${fontWeight} ${mergedFontSize}px/${drawHeight}px ${fontFamily}`;
ctx.fillStyle = color;
ctx.textAlign = "center";
ctx.textBaseline = "top";
ctx.translate(drawWidth / 2, 0);
const contents = Array.isArray(content) ? content : [content];
contents == null ? void 0 : contents.forEach((item, index) => {
ctx.fillText(item ?? "", drawX, drawY + index * (mergedFontSize + 2 * ratio));
});
}
function appendWatermark(base64Url, markWidth) {
const [gapX] = props.gap;
if (!containerRef.value) return;
const attrs = util.getStyleStr({
...getMarkStyle.value,
backgroundImage: `url('${base64Url}')`,
backgroundSize: `${(gapX + markWidth) * 2}px`
});
watermarkRef.value.setAttribute("style", attrs);
containerRef.value.append(watermarkRef.value);
}
function renderWatermark() {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const image = props.image;
const rotate = props.rotate;
if (!ctx) return;
if (!watermarkRef.value) {
watermarkRef.value = document.createElement("div");
}
const ratio = util.getPixelRatio();
const [gapX, gapY] = props.gap;
const [markWidth, markHeight] = getMarkSize(ctx);
const canvasWidth = (gapX + markWidth) * ratio;
const canvasHeight = (gapY + markHeight) * ratio;
canvas.setAttribute("width", `${canvasWidth * 2}px`);
canvas.setAttribute("height", `${canvasHeight * 2}px`);
const drawX = gapX * ratio / 2;
const drawY = gapY * ratio / 2;
const drawWidth = markWidth * ratio;
const drawHeight = markHeight * ratio;
const rotateX = (drawWidth + gapX * ratio) / 2;
const rotateY = (drawHeight + gapY * ratio) / 2;
const alternateDrawX = drawX + canvasWidth;
const alternateDrawY = drawY + canvasHeight;
const alternateRotateX = rotateX + canvasWidth;
const alternateRotateY = rotateY + canvasHeight;
ctx.save();
rotateWatermark(ctx, rotateX, rotateY, rotate);
if (image) {
const img = new Image();
img.onload = () => {
ctx.drawImage(img, drawX, drawY, drawWidth, drawHeight);
ctx.restore();
rotateWatermark(ctx, alternateRotateX, alternateRotateY, rotate);
ctx.drawImage(img, alternateDrawX, alternateDrawY, drawWidth, drawHeight);
appendWatermark(canvas.toDataURL(), markWidth);
};
img.crossOrigin = "anonymous";
img.referrerPolicy = "no-referrer";
img.src = image;
} else {
fillTexts(ctx, drawX, drawY, drawWidth, drawHeight);
ctx.restore();
rotateWatermark(ctx, alternateRotateX, alternateRotateY, rotate);
fillTexts(ctx, alternateDrawX, alternateDrawY, drawWidth, drawHeight);
appendWatermark(canvas.toDataURL(), markWidth);
}
}
vue.watch(
props,
() => {
renderWatermark();
},
{ deep: true }
);
vue.onMounted(() => {
renderWatermark();
});
vue.onUnmounted(() => {
destroyWatermark();
});
util.useMutationObserver(
containerRef,
() => {
destroyWatermark();
renderWatermark();
},
{
attributes: true,
childList: true,
subtree: true,
attributeFilter: ["style", "class"]
}
);
return () => vue.h("div", { style: { position: "relative" }, ref: containerRef }, slots);
}
});
exports.default = Watermark;