ng-cw-v12
Version:
Angular UI component library
407 lines (402 loc) • 20 kB
JavaScript
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