Angular 图片预览组件

写在前面

环境 :Angular8

图片预览组件实现的功能包括:全屏查看;放大、缩小( 鼠标滚轮可放大、缩小图片);翻页( 键盘左右按键翻页);旋转;拖拽

功能实现

首先创建angular项目,在项目中创建图片预览组件以及全屏显示的中间件

全屏显示 中间件实现代码

import { Directive, Input, ElementRef, OnChanges, OnInit, SimpleChanges } from '@angular/core';

@Directive({
  selector: '[appScreenfull]'
})
export class FullScreenDirective implements OnChanges, OnInit {

  @Input('appScreenfull') fullscreenState: boolean;

  constructor(private el: ElementRef) { }

  ngOnChanges(changes: SimpleChanges) {
    console.log('fullscreenState isFirstChange:', changes["fullscreenState"].isFirstChange());
    console.log('fullscreenState', this.fullscreenState);
    if (!changes['fullscreenState'].isFirstChange()) {

      if (this.fullscreenState) {
        const element: any = this.el.nativeElement;

        // tslint:disable-next-line: max-line-length
        const requestMethod = element.requestFullscreen || element.webkitRequestFullScreen || element.mozRequestFullScreen || element.msRequestFullScreen;

        if (requestMethod) { // Native full screen.
          requestMethod.call(element);
        } else {
          console.log('FullScreen Request Method Not Supported on this browser.');
        }
      } else {
        const element: any = document;

        // tslint:disable-next-line: max-line-length
        const requestMethod = element.cancelFullscreen || element.webkitExitFullscreen || element.webkitCancelFullScreen || element.mozCancelFullScreen || element.msExitFullScreen;

        if (requestMethod) { // Native Cancel full screen.
          requestMethod.call(element);
        } else {
          console.log('FullScreen Cancel Request Method Not Supported on this browser.');
        }
      }
    }

  }

  ngOnInit() {

  }

}

图片预览组件

前端代码

<div [appScreenfull]="fullscreen" [style.height]="styleHeight">
    <div class="img-container" (wheel)="scrollZoom($event)" (dragover)="onDragOver($event)">
        <img [src]="src[index]" [ngStyle]="style" alt="Image not found..." (dragstart)="onDragStart($event)"
            (error)="imageNotFound(src[index])" />
        <!-- Div below will be used to hide the 'ghost' image when dragging -->
        <div></div>
        <!--缩小-->
        <button type="button" (click)="zoomOut()">
            <img src="assets/img/less-minimize.png" />
        </button>
        <!--放大-->
        <button type="button" (click)="zoomIn()">
            <img src="assets/img/add.png" />
        </button>
        <!--逆时针旋转-->
        <button type="button" (click)="rotateCounterClockwise()">
            <img src="assets/img/rotation-x-right.png" />
        </button>
        <!--顺时针旋转-->
        <button type="button" (click)="rotateClockwise()">
            <img src="assets/img/rotation-x-left.png" />
        </button>
        <!--全屏-->
        <button type="button" (click)="toggleFullscreen()">
            <img src="assets/img/fullscreen-arrows.png" />
        </button>
    </div>
    <!--翻页-->
    <div class="nav-button-container" *ngIf="src.length > 1">
        <button type="button" (click)="prevImage($event)" [disabled]="index === 0">
            <img src="assets/img/back-left-end.png" />
        </button>
        <span>{{index+1}}/{{src.length}}</span>
        <button type="button" (click)="nextImage($event)" [disabled]="index === src.length - 1">
            <img src="assets/img/back-right-end.png" />
        </button>
    </div>
</div>

样式

.img-container {
    width: 100%;
    height:95%;
    display: flex;
    justify-content: center;
    align-items: center;
    overflow: hidden;
    & img {
      max-width: 90%;
      max-height: 80%;
    }
    & button {
      z-index: 99;
      position: absolute;
      right: 15px; 
      height: 40px;
      width: 40px;
      border: 1px solid #555;
      border-radius: 50%;
      background-color: white;
      outline: none;
      opacity: 0.7;
      transition: opacity 200ms;
      &:not(:disabled) {
        cursor: pointer;
      }
      &:hover{
        opacity: 1;
      }
      &:disabled{
        opacity: 0.25;
      }
    }
  }
  /*放大、缩小 旋转等按钮样式*/
  .loop(@counter) when(@counter>0){
    .img-container>button:nth-of-type(@{counter}){
      bottom: 15px + (50px * (@counter - 1));
    }
    .loop((@counter - 1));// 递归调用自身
  }
  .loop(5);
  
  .nav-button-container {
    text-align: center;
    position: absolute;
    z-index: 98;
    bottom: 10px;
    left: 0;
    right: 0;    
  }
  .nav-button-container>button {
    position: relative;
    right: 0;
    margin: 0 10px;   
    outline: none;
    border: 1px solid #555;
    background-color: white;
    opacity: 0.7;
    transition: opacity 200ms;
    &:not(:disabled) {
      cursor: pointer;
    }
    &:hover{
      opacity: 1;
    }
    &:disabled{
        opacity: 0.25;
    }
  }
  .nav-button-container>span {
    font-size:2rem;
    color:#515151;
  }
  
  @keyframes rotation {
    from {
      -webkit-transform: rotate(0deg);
    }
  
    to {
      -webkit-transform: rotate(359deg);
    }
  }
  
  @-webkit-keyframes rotation {
    from {
      -webkit-transform: rotate(0deg);
    }
  
    to {
      -webkit-transform: rotate(359deg);
    }
  }
  
  @-moz-keyframes rotation {
    from {
      -webkit-transform: rotate(0deg);
    }
  
    to {
      -webkit-transform: rotate(359deg);
    }
  }
  
  @-o-keyframes rotation {
    from {
      -webkit-transform: rotate(0deg);
    }
  
    to {
      -webkit-transform: rotate(359deg);
    }
  }

后端代码

import { Component, OnInit, HostListener, Optional, Inject, Input, Output, EventEmitter, OnChanges, SimpleChanges } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';

@Component({
  selector: 'app-image-viewer',
  templateUrl: './image-viewer.component.html',
  styleUrls: ['./image-viewer.component.less']
})
export class ImageViewerComponent implements OnInit, OnChanges {

  @Input() src: string[];//图片路径
  @Input() screenHeightOccupied: 0;// In Px
  @Input() index = 0; //当前显示的图片索引
  @Output() indexChange: EventEmitter<number> = new EventEmitter();
  styleHeight: string = '98vh';
  zoomFactor: number = 0.1;
  allowKeyboardNavigation: boolean = true;
  public style = { transform: '', msTransform: '', oTransform: '', webkitTransform: '' };//图片样式
  public fullscreen = false;
  private scale = 1;
  private rotation = 0;
  private translateX = 0;
  private translateY = 0;
  private prevX: number;
  private prevY: number;
  private hovered = false;

  constructor(

  ) { }

  ngOnInit() {
  }
  ngOnChanges(changes: SimpleChanges) {
    if (changes.screenHeightOccupied) {
      this.styleHeight = 'calc(98vh - ' + this.screenHeightOccupied + 'px)';
    }
  }
  //键盘右键
  @HostListener('window:keyup.ArrowRight', ['$event'])
  nextImage(event) {
    if (this.canNavigate(event) && this.index < this.src.length - 1) {
      this.index++;
      this.triggerIndexBinding();
      this.reset();
    }
  }
  //键盘左键
  @HostListener('window:keyup.ArrowLeft', ['$event'])
  prevImage(event) {
    if (this.canNavigate(event) && this.index > 0) {
      this.index--;
      this.triggerIndexBinding();
      this.reset();
    }
  }
  //mouse hover
  @HostListener('mouseover')
  onMouseOver() {
    this.hovered = true;
  }
  //mouse leave
  @HostListener('mouseleave')
  onMouseLeave() {
    this.hovered = false;
  }
  /*放大 */
  zoomIn() {
    this.scale *= (1 + this.zoomFactor);
    this.updateStyle();
  }
  /*缩小 */
  zoomOut() {
    if (this.scale > this.zoomFactor) {
      this.scale /= (1 + this.zoomFactor);
    }
    this.updateStyle();
  }
  /*鼠标滚动 放大、缩小 */
  scrollZoom(evt) {
    evt.deltaY > 0 ? this.zoomOut() : this.zoomIn();
    return false;
  }
  /*顺时针旋转 */
  rotateClockwise() {
    this.rotation += 90;
    this.updateStyle();
  }
  /*逆时针旋转 */
  rotateCounterClockwise() {
    this.rotation -= 90;
    this.updateStyle();
  }

  imageNotFound(url) {
    console.log('Image not found Url:', url);
  }
  /*拖拽 */
  onDragOver(evt) {
    this.translateX += (evt.clientX - this.prevX);
    this.translateY += (evt.clientY - this.prevY);
    this.prevX = evt.clientX;
    this.prevY = evt.clientY;
    this.updateStyle();
  }

  onDragStart(evt) {
    if (evt.dataTransfer && evt.dataTransfer.setDragImage) {
      evt.dataTransfer.setDragImage(evt.target.nextElementSibling, 0, 0);
    }
    this.prevX = evt.clientX;
    this.prevY = evt.clientY;
  }
  /*全屏切换 */
  toggleFullscreen() {
    this.fullscreen = !this.fullscreen;
    if (!this.fullscreen) {
      this.reset();
    }
  }
  /*图片翻页事件*/
  triggerIndexBinding() {
    this.indexChange.emit(this.index);
  }

  reset() {
    this.scale = 1;
    this.rotation = 0;
    this.translateX = 0;
    this.translateY = 0;
    this.updateStyle();
  }

  private canNavigate(event: any) {
    return event == null || (this.allowKeyboardNavigation && this.hovered);
  }
  /*样式变换 */
  private updateStyle() {
    this.style.transform = `translate(${this.translateX}px, ${this.translateY}px) rotate(${this.rotation}deg) scale(${this.scale})`;
    this.style.msTransform = this.style.transform;
    this.style.webkitTransform = this.style.transform;
    this.style.oTransform = this.style.transform;
  }

}

module代码

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ImageViewerComponent } from './image-viewer.component';
import { FullScreenDirective } from '../fullscreen/full-screen.directive';



@NgModule({
  declarations: [ImageViewerComponent, FullScreenDirective],
  imports: [
    CommonModule,
    FormsModule,
    BrowserModule,
    BrowserAnimationsModule
  ],
  exports: [ImageViewerComponent, FullScreenDirective]
})
export class ImageViewerModule { }

组件引用示例

1、在app.module中引入ImageViewerModule

2、在需要预览图片的页面,引入图片预览组件

前端

 后端

3、启动项目查看图片预览效果

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值