UNPKG

ng-cw-v12

Version:

Angular UI component library

407 lines (402 loc) 20 kB
import * as i0 from '@angular/core'; import { Directive, Input, HostListener, NgModule } from '@angular/core'; class ImgPreviewDirective { constructor(el, renderer) { this.el = el; this.renderer = renderer; this.ncImgPreview = ''; // 可选的大图URL,如果不提供则使用原图 this.ncMinScale = 0.5; // 最小缩放比例 this.ncMaxScale = 5; // 最大缩放比例 this.ncScaleStep = 0.25; // 缩放步长 /** 滚轮缩放 */ this._wheelScale = false; this.previewContainer = null; this.previewImg = null; this.toolbar = null; this.isPreviewOpen = false; // 图片变换状态 this.rotation = 0; // 旋转角度 this.scale = 1; // 缩放比例 // 工具按钮引用 this.zoomInBtn = null; this.zoomOutBtn = null; // 事件监听器引用 this.wheelListener = null; this.keyListener = null; } set ncWheelScale(val) { this._wheelScale = val !== null && val !== undefined && val !== false && val !== 'false'; } get ncWheelScale() { return this._wheelScale; } ngOnInit() { // 给图片添加预览样式 this.renderer.setStyle(this.el.nativeElement, 'cursor', 'pointer'); this.renderer.setStyle(this.el.nativeElement, 'transition', 'transform 0.2s ease'); } onImageClick(event) { event.preventDefault(); event.stopPropagation(); this.openPreview(); } onMouseEnter() { this.renderer.setStyle(this.el.nativeElement, 'transform', 'scale(1.05)'); } onMouseLeave() { this.renderer.setStyle(this.el.nativeElement, 'transform', 'scale(1)'); } openPreview() { if (this.isPreviewOpen) return; this.isPreviewOpen = true; // 重置变换状态 this.rotation = 0; this.scale = 1; // 创建预览容器 this.previewContainer = this.renderer.createElement('div'); this.renderer.addClass(this.previewContainer, 'nc-img-preview-overlay'); // 设置遮罩层样式 this.renderer.setStyle(this.previewContainer, 'position', 'fixed'); this.renderer.setStyle(this.previewContainer, 'top', '0'); this.renderer.setStyle(this.previewContainer, 'left', '0'); this.renderer.setStyle(this.previewContainer, 'width', '100vw'); this.renderer.setStyle(this.previewContainer, 'height', '100vh'); this.renderer.setStyle(this.previewContainer, 'background-color', 'rgba(0, 0, 0, 0.45)'); this.renderer.setStyle(this.previewContainer, 'z-index', '9999'); this.renderer.setStyle(this.previewContainer, 'display', 'flex'); this.renderer.setStyle(this.previewContainer, 'justify-content', 'center'); this.renderer.setStyle(this.previewContainer, 'align-items', 'center'); this.renderer.setStyle(this.previewContainer, 'cursor', 'pointer'); this.renderer.setStyle(this.previewContainer, 'opacity', '0'); this.renderer.setStyle(this.previewContainer, 'transition', 'opacity 0.3s ease'); this.renderer.setStyle(this.previewContainer, 'user-select', 'none'); this.renderer.setStyle(this.previewContainer, '-webkit-user-select', 'none'); this.renderer.setStyle(this.previewContainer, '-moz-user-select', 'none'); this.renderer.setStyle(this.previewContainer, '-ms-user-select', 'none'); this.renderer.setStyle(this.previewContainer, 'overflow', 'auto'); // 创建预览图片 this.previewImg = this.renderer.createElement('img'); this.renderer.addClass(this.previewImg, 'nc-img-preview-img'); const imgSrc = this.ncImgPreview || this.el.nativeElement.src; this.renderer.setAttribute(this.previewImg, 'src', imgSrc); this.renderer.setStyle(this.previewImg, 'max-width', '100%'); this.renderer.setStyle(this.previewImg, 'max-height', '100%'); this.renderer.setStyle(this.previewImg, 'object-fit', 'contain'); this.renderer.setStyle(this.previewImg, 'border-radius', '8px'); this.renderer.setStyle(this.previewImg, 'box-shadow', '0 4px 20px rgba(0, 0, 0, 0.3)'); this.renderer.setStyle(this.previewImg, 'transform', 'scale(0.8)'); this.renderer.setStyle(this.previewImg, 'transition', 'transform 0.3s ease'); this.renderer.setStyle(this.previewImg, 'cursor', 'default'); this.renderer.setStyle(this.previewImg, 'user-select', 'none'); this.renderer.setStyle(this.previewImg, '-webkit-user-select', 'none'); this.renderer.setStyle(this.previewImg, '-moz-user-select', 'none'); this.renderer.setStyle(this.previewImg, '-ms-user-select', 'none'); this.renderer.setStyle(this.previewImg, '-webkit-user-drag', 'none'); this.renderer.setStyle(this.previewImg, '-khtml-user-drag', 'none'); this.renderer.setStyle(this.previewImg, '-moz-user-drag', 'none'); this.renderer.setStyle(this.previewImg, '-o-user-drag', 'none'); this.renderer.setStyle(this.previewImg, 'user-drag', 'none'); this.renderer.setAttribute(this.previewImg, 'draggable', 'false'); // 创建工具条容器 this.toolbar = this.renderer.createElement('div'); this.renderer.addClass(this.toolbar, 'nc-img-preview-toolbar'); this.renderer.setStyle(this.toolbar, 'position', 'fixed'); this.renderer.setStyle(this.toolbar, 'top', '20px'); this.renderer.setStyle(this.toolbar, 'right', '20px'); this.renderer.setStyle(this.toolbar, 'display', 'flex'); this.renderer.setStyle(this.toolbar, 'gap', '8px'); this.renderer.setStyle(this.toolbar, 'background-color', 'rgba(0, 0, 0, 0.6)'); this.renderer.setStyle(this.toolbar, 'border-radius', '8px'); this.renderer.setStyle(this.toolbar, 'padding', '8px'); this.renderer.setStyle(this.toolbar, 'backdrop-filter', 'blur(10px)'); this.renderer.setStyle(this.toolbar, 'z-index', '10000'); this.renderer.setStyle(this.toolbar, 'user-select', 'none'); this.renderer.setStyle(this.toolbar, '-webkit-user-select', 'none'); this.renderer.setStyle(this.toolbar, '-moz-user-select', 'none'); this.renderer.setStyle(this.toolbar, '-ms-user-select', 'none'); // 创建工具按钮 const rotateLeftBtn = this.createToolButton('↺', '向左旋转'); const rotateRightBtn = this.createToolButton('↻', '向右旋转'); this.zoomInBtn = this.createToolButton('+', '放大'); this.zoomOutBtn = this.createToolButton('−', '缩小'); const closeBtn = this.createToolButton('×', '关闭'); // 添加按钮事件监听器 this.renderer.listen(rotateLeftBtn, 'click', (event) => { event.preventDefault(); event.stopPropagation(); this.rotateImage(-90); }); this.renderer.listen(rotateRightBtn, 'click', (event) => { event.preventDefault(); event.stopPropagation(); this.rotateImage(90); }); this.renderer.listen(this.zoomInBtn, 'click', (event) => { event.preventDefault(); event.stopPropagation(); this.scaleImage(this.ncScaleStep); }); this.renderer.listen(this.zoomOutBtn, 'click', (event) => { event.preventDefault(); event.stopPropagation(); this.scaleImage(-this.ncScaleStep); }); this.renderer.listen(closeBtn, 'click', (event) => { event.preventDefault(); event.stopPropagation(); this.closePreview(); }); // 组装工具条 this.renderer.appendChild(this.toolbar, rotateLeftBtn); this.renderer.appendChild(this.toolbar, rotateRightBtn); this.renderer.appendChild(this.toolbar, this.zoomOutBtn); this.renderer.appendChild(this.toolbar, this.zoomInBtn); this.renderer.appendChild(this.toolbar, closeBtn); // 组装预览容器 this.renderer.appendChild(this.previewContainer, this.previewImg); this.renderer.appendChild(document.body, this.previewContainer); this.renderer.appendChild(document.body, this.toolbar); // 添加事件监听器 this.renderer.listen(this.previewContainer, 'click', (event) => { if (event.target === this.previewContainer) { this.closePreview(); } }); // 添加鼠标滚轮事件监听器(如果启用了滚轮缩放) if (this.ncWheelScale) { this.wheelListener = this.renderer.listen(this.previewContainer, 'wheel', (event) => { event.preventDefault(); event.stopPropagation(); // 根据滚轮方向决定缩放方向 // deltaY > 0 表示向下滚动(缩小),deltaY < 0 表示向上滚动(放大) const delta = event.deltaY > 0 ? -this.ncScaleStep : this.ncScaleStep; this.scaleImage(delta); }); } // 添加键盘事件监听器 this.keyListener = this.renderer.listen('document', 'keydown', (event) => { if (event.key === 'Escape') { this.closePreview(); } }); // 禁止页面滚动和文本选择 this.renderer.setStyle(document.body, 'overflow', 'hidden'); this.renderer.setStyle(document.body, 'user-select', 'none'); this.renderer.setStyle(document.body, '-webkit-user-select', 'none'); this.renderer.setStyle(document.body, '-moz-user-select', 'none'); this.renderer.setStyle(document.body, '-ms-user-select', 'none'); // 动画效果 setTimeout(() => { if (this.previewContainer && this.previewImg) { this.renderer.setStyle(this.previewContainer, 'opacity', '1'); this.renderer.setStyle(this.previewImg, 'transform', 'scale(1)'); // 初始化按钮状态 this.updateZoomButtonsState(); } }, 10); } createToolButton(text, title) { const button = this.renderer.createElement('div'); this.renderer.addClass(button, 'nc-img-preview-toolbar-button'); this.renderer.setStyle(button, 'width', '36px'); this.renderer.setStyle(button, 'height', '36px'); this.renderer.setStyle(button, 'background-color', 'rgba(255, 255, 255, 0.1)'); this.renderer.setStyle(button, 'border-radius', '6px'); this.renderer.setStyle(button, 'display', 'flex'); this.renderer.setStyle(button, 'justify-content', 'center'); this.renderer.setStyle(button, 'align-items', 'center'); this.renderer.setStyle(button, 'cursor', 'pointer'); this.renderer.setStyle(button, 'transition', 'background-color 0.2s ease, transform 0.1s ease'); this.renderer.setStyle(button, 'font-size', '18px'); this.renderer.setStyle(button, 'color', 'white'); this.renderer.setStyle(button, 'font-weight', 'bold'); this.renderer.setStyle(button, 'user-select', 'none'); this.renderer.setAttribute(button, 'title', title); // 设置按钮内容 const buttonText = this.renderer.createText(text); this.renderer.appendChild(button, buttonText); // 添加悬停效果 this.renderer.listen(button, 'mouseenter', () => { this.renderer.setStyle(button, 'background-color', 'rgba(255, 255, 255, 0.2)'); this.renderer.setStyle(button, 'transform', 'scale(1.1)'); }); this.renderer.listen(button, 'mouseleave', () => { this.renderer.setStyle(button, 'background-color', 'rgba(255, 255, 255, 0.1)'); this.renderer.setStyle(button, 'transform', 'scale(1)'); }); // 添加点击效果 this.renderer.listen(button, 'mousedown', (event) => { event.preventDefault(); this.renderer.setStyle(button, 'transform', 'scale(0.95)'); }); this.renderer.listen(button, 'mouseup', (event) => { event.preventDefault(); this.renderer.setStyle(button, 'transform', 'scale(1.1)'); }); // 阻止文本选择开始 this.renderer.listen(button, 'selectstart', (event) => { event.preventDefault(); }); // 阻止拖拽开始 this.renderer.listen(button, 'dragstart', (event) => { event.preventDefault(); }); return button; } rotateImage(angle) { if (!this.previewImg) return; this.rotation += angle; this.updateImageTransform(); } scaleImage(delta) { if (!this.previewImg) return; const newScale = this.mathAdd(this.scale, delta); // 确保缩放比例在合理范围内,超出范围时设置为边界值 if (newScale < this.ncMinScale) { this.scale = this.ncMinScale; } else if (newScale > this.ncMaxScale) { this.scale = this.ncMaxScale; } else { this.scale = newScale; } this.updateImageTransform(); this.updateZoomButtonsState(); } updateImageTransform() { if (!this.previewImg) return; const transform = `scale3d(${this.scale}, ${this.scale}, 1) rotate(${this.rotation}deg)`; this.renderer.setStyle(this.previewImg, 'transform', transform); } updateZoomButtonsState() { if (!this.zoomInBtn || !this.zoomOutBtn) return; const isMinScale = this.scale <= this.ncMinScale; const isMaxScale = this.scale >= this.ncMaxScale; // 更新按钮状态 this.setButtonDisabled(this.zoomOutBtn, isMinScale); this.setButtonDisabled(this.zoomInBtn, isMaxScale); } setButtonDisabled(button, disabled) { if (disabled) { this.renderer.setStyle(button, 'opacity', '0.4'); this.renderer.setStyle(button, 'pointer-events', 'none'); } else { this.renderer.setStyle(button, 'opacity', '1'); this.renderer.setStyle(button, 'pointer-events', 'auto'); } } closePreview() { if (!this.isPreviewOpen || !this.previewContainer) return; // 清理事件监听器 if (this.keyListener) { this.keyListener(); this.keyListener = null; } if (this.wheelListener) { this.wheelListener(); this.wheelListener = null; } // 移除工具栏 if (this.toolbar) { this.renderer.removeChild(document.body, this.toolbar); this.toolbar = null; } // 添加关闭动画 this.renderer.setStyle(this.previewContainer, 'opacity', '0'); if (this.previewImg) { this.renderer.setStyle(this.previewImg, 'transform', 'scale(0.8)'); } // 延迟移除元素以完成动画 setTimeout(() => { if (this.previewContainer) { this.renderer.removeChild(document.body, this.previewContainer); this.previewContainer = null; this.previewImg = null; this.zoomInBtn = null; this.zoomOutBtn = null; } this.isPreviewOpen = false; // 恢复页面滚动和文本选择 this.renderer.removeStyle(document.body, 'overflow'); this.renderer.removeStyle(document.body, 'user-select'); this.renderer.removeStyle(document.body, '-webkit-user-select'); this.renderer.removeStyle(document.body, '-moz-user-select'); this.renderer.removeStyle(document.body, '-ms-user-select'); }, 300); } mathAdd(value1, value2) { // 将浮点数转换为字符串 let aStr = value1.toString(); let bStr = value2.toString(); // 找到小数点后的位置 let aDecimals = (aStr.split('.')[1] || '').length; let bDecimals = (bStr.split('.')[1] || '').length; // 找到最大的小数位数 let maxDecimals = Math.max(aDecimals, bDecimals); // 将浮点数转换为整数 let aInt = Math.round(value1 * Math.pow(10, maxDecimals)); let bInt = Math.round(value2 * Math.pow(10, maxDecimals)); // 进行整数加法 let resultInt = aInt + bInt; // 将结果转换回浮点数 return resultInt / Math.pow(10, maxDecimals); } } ImgPreviewDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: ImgPreviewDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive }); ImgPreviewDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "12.1.5", type: ImgPreviewDirective, selector: "[ncImgPreview]", inputs: { ncImgPreview: "ncImgPreview", ncMinScale: "ncMinScale", ncMaxScale: "ncMaxScale", ncScaleStep: "ncScaleStep", ncWheelScale: "ncWheelScale" }, host: { listeners: { "click": "onImageClick($event)", "mouseenter": "onMouseEnter()", "mouseleave": "onMouseLeave()" } }, exportAs: ["ncImgPreview"], ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: ImgPreviewDirective, decorators: [{ type: Directive, args: [{ selector: '[ncImgPreview]', exportAs: 'ncImgPreview' }] }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.Renderer2 }]; }, propDecorators: { ncImgPreview: [{ type: Input }], ncMinScale: [{ type: Input }], ncMaxScale: [{ type: Input }], ncScaleStep: [{ type: Input }], ncWheelScale: [{ type: Input }], onImageClick: [{ type: HostListener, args: ['click', ['$event']] }], onMouseEnter: [{ type: HostListener, args: ['mouseenter'] }], onMouseLeave: [{ type: HostListener, args: ['mouseleave'] }] } }); class NcImgPreviewModule { } NcImgPreviewModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcImgPreviewModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); NcImgPreviewModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcImgPreviewModule, declarations: [ImgPreviewDirective], exports: [ImgPreviewDirective] }); NcImgPreviewModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcImgPreviewModule, imports: [[]] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcImgPreviewModule, decorators: [{ type: NgModule, args: [{ declarations: [ ImgPreviewDirective ], imports: [], exports: [ ImgPreviewDirective ] }] }] }); /** * Generated bundle index. Do not edit. */ export { ImgPreviewDirective, NcImgPreviewModule }; //# sourceMappingURL=ng-cw-v12-img-preview.js.map