ng-zorro-antd
Version:
An enterprise-class UI components based on Ant Design and Angular
334 lines (326 loc) • 14.3 kB
JavaScript
import { DOCUMENT } from '@angular/common';
import * as i0 from '@angular/core';
import { inject, numberAttribute, Input, ChangeDetectionStrategy, Component, NgModule } from '@angular/core';
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/
/** Returns the ratio of the device's physical pixel resolution to the css pixel resolution */
function getPixelRatio() {
return window.devicePixelRatio || 1;
}
function toLowercaseSeparator(key) {
return key.replace(/([A-Z])/g, '-$1').toLowerCase();
}
function getStyleStr(style) {
const keys = Object.keys(style);
const styleCss = keys.map((key) => `${toLowercaseSeparator(key)}: ${style[key]};`);
return styleCss.join(' ');
}
/** Whether to re-render the watermark */
function reRendering(mutation, watermarkElement) {
let flag = false;
// Whether to delete the watermark node
if (mutation.removedNodes.length) {
flag = Array.from(mutation.removedNodes).some(node => node === watermarkElement);
}
// Whether the watermark dom property value has been modified
if (mutation.type === 'attributes' && mutation.target === watermarkElement) {
flag = true;
}
return flag;
}
/** Rotate with the watermark as the center point */
function rotateWatermark(ctx, rotateX, rotateY, rotate) {
ctx.translate(rotateX, rotateY);
ctx.rotate((Math.PI / 180) * Number(rotate));
ctx.translate(-rotateX, -rotateY);
}
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/
/**
* Base size of the canvas, 1 for parallel layout and 2 for alternate layout
* Only alternate layout is currently supported
*/
const BaseSize = 2;
const FontGap = 3;
class NzWaterMarkComponent {
el;
cdr;
nzWidth = 120;
nzHeight = 64;
nzRotate = -22;
nzZIndex = 9;
nzImage = '';
nzContent = '';
nzFont = {};
nzGap = [100, 100];
nzOffset = [this.nzGap[0] / 2, this.nzGap[1] / 2];
document = inject(DOCUMENT);
waterMarkElement = this.document.createElement('div');
stopObservation = false;
observer = new MutationObserver(mutations => {
if (this.stopObservation) {
return;
}
mutations.forEach(mutation => {
if (reRendering(mutation, this.waterMarkElement)) {
this.destroyWatermark();
this.renderWatermark();
}
});
});
constructor(el, cdr) {
this.el = el;
this.cdr = cdr;
}
ngOnInit() {
this.observer.observe(this.el.nativeElement, {
subtree: true,
childList: true,
attributeFilter: ['style', 'class']
});
}
ngAfterViewInit() {
this.renderWatermark();
}
ngOnChanges(changes) {
const { nzRotate, nzZIndex, nzWidth, nzHeight, nzImage, nzContent, nzFont, gapX, gapY, offsetLeft, offsetTop } = changes;
if (nzRotate ||
nzZIndex ||
nzWidth ||
nzHeight ||
nzImage ||
nzContent ||
nzFont ||
gapX ||
gapY ||
offsetLeft ||
offsetTop) {
this.renderWatermark();
}
}
getFont() {
const font = {
color: 'rgba(0,0,0,.15)',
fontSize: 16,
fontWeight: 'normal',
fontFamily: 'sans-serif',
fontStyle: 'normal'
};
this.nzFont = { ...font, ...this.nzFont };
this.cdr.markForCheck();
}
getMarkStyle() {
const markStyle = {
zIndex: this.nzZIndex,
position: 'absolute',
left: 0,
top: 0,
width: '100%',
height: '100%',
pointerEvents: 'none',
backgroundRepeat: 'repeat',
visibility: 'visible'
};
/** Calculate the style of the nzOffset */
let positionLeft = (this.nzOffset?.[0] ?? this.nzGap[0] / 2) - this.nzGap[0] / 2;
let positionTop = (this.nzOffset?.[1] ?? this.nzGap[1] / 2) - this.nzGap[1] / 2;
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;
}
destroyWatermark() {
if (this.waterMarkElement) {
this.waterMarkElement.remove();
}
}
appendWatermark(base64Url, markWidth) {
this.stopObservation = true;
this.waterMarkElement.setAttribute('style', getStyleStr({
...this.getMarkStyle(),
backgroundImage: `url('${base64Url}')`,
backgroundSize: `${(this.nzGap[0] + markWidth) * BaseSize}px`
}));
this.el.nativeElement.append(this.waterMarkElement);
this.cdr.markForCheck();
// Delayed execution
setTimeout(() => {
this.stopObservation = false;
this.cdr.markForCheck();
});
}
getMarkSize(ctx) {
let defaultWidth = 120;
let defaultHeight = 64;
if (!this.nzImage && ctx.measureText) {
ctx.font = `${Number(this.nzFont.fontSize)}px ${this.nzFont.fontFamily}`;
const contents = Array.isArray(this.nzContent) ? this.nzContent : [this.nzContent];
const widths = contents.map(item => ctx.measureText(item).width);
defaultWidth = Math.ceil(Math.max(...widths));
defaultHeight = Number(this.nzFont.fontSize) * contents.length + (contents.length - 1) * FontGap;
}
return [this.nzWidth ?? defaultWidth, this.nzHeight ?? defaultHeight];
}
fillTexts(ctx, drawX, drawY, drawWidth, drawHeight) {
const ratio = getPixelRatio();
const mergedFontSize = Number(this.nzFont.fontSize) * ratio;
ctx.font = `${this.nzFont.fontStyle} normal ${this.nzFont.fontWeight} ${mergedFontSize}px/${drawHeight}px ${this.nzFont.fontFamily}`;
if (this.nzFont.color)
ctx.fillStyle = this.nzFont.color;
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
ctx.translate(drawWidth / 2, 0);
const contents = Array.isArray(this.nzContent) ? this.nzContent : [this.nzContent];
contents?.forEach((item, index) => {
ctx.fillText(item ?? '', drawX, drawY + index * (mergedFontSize + FontGap * ratio));
});
}
drawText(canvas, ctx, drawX, drawY, drawWidth, drawHeight, alternateRotateX, alternateRotateY, alternateDrawX, alternateDrawY, markWidth) {
this.fillTexts(ctx, drawX, drawY, drawWidth, drawHeight);
/** Fill the interleaved text after rotation */
ctx.restore();
rotateWatermark(ctx, alternateRotateX, alternateRotateY, this.nzRotate);
this.fillTexts(ctx, alternateDrawX, alternateDrawY, drawWidth, drawHeight);
this.appendWatermark(canvas.toDataURL(), markWidth);
}
renderWatermark() {
if (!this.nzContent && !this.nzImage) {
return;
}
const canvas = this.document.createElement('canvas');
const ctx = canvas.getContext('2d');
if (ctx) {
if (!this.waterMarkElement) {
this.waterMarkElement = this.document.createElement('div');
}
this.getFont();
const ratio = getPixelRatio();
const [markWidth, markHeight] = this.getMarkSize(ctx);
const canvasWidth = (this.nzGap[0] + markWidth) * ratio;
const canvasHeight = (this.nzGap[1] + markHeight) * ratio;
canvas.setAttribute('width', `${canvasWidth * BaseSize}px`);
canvas.setAttribute('height', `${canvasHeight * BaseSize}px`);
const drawX = (this.nzGap[0] * ratio) / 2;
const drawY = (this.nzGap[1] * ratio) / 2;
const drawWidth = markWidth * ratio;
const drawHeight = markHeight * ratio;
const rotateX = (drawWidth + this.nzGap[0] * ratio) / 2;
const rotateY = (drawHeight + this.nzGap[1] * ratio) / 2;
/** Alternate drawing parameters */
const alternateDrawX = drawX + canvasWidth;
const alternateDrawY = drawY + canvasHeight;
const alternateRotateX = rotateX + canvasWidth;
const alternateRotateY = rotateY + canvasHeight;
ctx.save();
rotateWatermark(ctx, rotateX, rotateY, this.nzRotate);
if (this.nzImage) {
const img = new Image();
const onLoad = () => {
cleanup();
ctx.drawImage(img, drawX, drawY, drawWidth, drawHeight);
/** Draw interleaved pictures after rotation */
ctx.restore();
rotateWatermark(ctx, alternateRotateX, alternateRotateY, this.nzRotate);
ctx.drawImage(img, alternateDrawX, alternateDrawY, drawWidth, drawHeight);
this.appendWatermark(canvas.toDataURL(), markWidth);
};
const onError = () => {
cleanup();
this.drawText(canvas, ctx, drawX, drawY, drawWidth, drawHeight, alternateRotateX, alternateRotateY, alternateDrawX, alternateDrawY, markWidth);
};
const cleanup = () => {
img.removeEventListener('load', onLoad);
img.removeEventListener('error', onError);
};
img.addEventListener('load', onLoad);
img.addEventListener('error', onError);
img.crossOrigin = 'anonymous';
img.referrerPolicy = 'no-referrer';
img.src = this.nzImage;
}
else {
this.drawText(canvas, ctx, drawX, drawY, drawWidth, drawHeight, alternateRotateX, alternateRotateY, alternateDrawX, alternateDrawY, markWidth);
}
}
}
ngOnDestroy() {
this.observer.disconnect();
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: NzWaterMarkComponent, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "19.2.2", type: NzWaterMarkComponent, isStandalone: true, selector: "nz-water-mark", inputs: { nzWidth: ["nzWidth", "nzWidth", numberAttribute], nzHeight: ["nzHeight", "nzHeight", numberAttribute], nzRotate: ["nzRotate", "nzRotate", numberAttribute], nzZIndex: ["nzZIndex", "nzZIndex", numberAttribute], nzImage: "nzImage", nzContent: "nzContent", nzFont: "nzFont", nzGap: "nzGap", nzOffset: "nzOffset" }, host: { classAttribute: "ant-water-mark" }, exportAs: ["NzWaterMark"], usesOnChanges: true, ngImport: i0, template: ` <ng-content></ng-content> `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: NzWaterMarkComponent, decorators: [{
type: Component,
args: [{
selector: 'nz-water-mark',
exportAs: 'NzWaterMark',
changeDetection: ChangeDetectionStrategy.OnPush,
template: ` <ng-content></ng-content> `,
host: {
class: 'ant-water-mark'
}
}]
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }], propDecorators: { nzWidth: [{
type: Input,
args: [{ transform: numberAttribute }]
}], nzHeight: [{
type: Input,
args: [{ transform: numberAttribute }]
}], nzRotate: [{
type: Input,
args: [{ transform: numberAttribute }]
}], nzZIndex: [{
type: Input,
args: [{ transform: numberAttribute }]
}], nzImage: [{
type: Input
}], nzContent: [{
type: Input
}], nzFont: [{
type: Input
}], nzGap: [{
type: Input
}], nzOffset: [{
type: Input
}] } });
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/
class NzWaterMarkModule {
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: NzWaterMarkModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.2", ngImport: i0, type: NzWaterMarkModule, imports: [NzWaterMarkComponent], exports: [NzWaterMarkComponent] });
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: NzWaterMarkModule });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: NzWaterMarkModule, decorators: [{
type: NgModule,
args: [{
exports: [NzWaterMarkComponent],
imports: [NzWaterMarkComponent]
}]
}] });
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/
/**
* Generated bundle index. Do not edit.
*/
export { NzWaterMarkComponent, NzWaterMarkModule };
//# sourceMappingURL=ng-zorro-antd-water-mark.mjs.map