写在前面
环境 :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、启动项目查看图片预览效果