Skip to content

Commit

Permalink
fix(module:image): image fit content when drag released (#6262)
Browse files Browse the repository at this point in the history
  • Loading branch information
stygian-desolator authored Dec 30, 2020
1 parent 41696e3 commit 07ae66a
Show file tree
Hide file tree
Showing 4 changed files with 270 additions and 3 deletions.
36 changes: 34 additions & 2 deletions components/image/image-preview.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,26 @@
*/
import { AnimationEvent } from '@angular/animations';
import { OverlayRef } from '@angular/cdk/overlay';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, OnDestroy, ViewEncapsulation } from '@angular/core';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
EventEmitter,
OnDestroy,
ViewChild,
ViewEncapsulation
} from '@angular/core';
import { fadeMotion } from 'ng-zorro-antd/core/animation';
import { NzConfigService } from 'ng-zorro-antd/core/config';
import { NzSafeAny } from 'ng-zorro-antd/core/types';
import { isNotNil } from 'ng-zorro-antd/core/util';
import { Subject } from 'rxjs';

import { FADE_CLASS_NAME_MAP, NZ_CONFIG_MODULE_NAME } from './image-config';
import { NzImage, NzImagePreviewOptions } from './image-preview-options';
import { NzImagePreviewRef } from './image-preview-ref';
import { getClientSize, getFitContentPosition, getOffset } from './utils';

export interface NzImageContainerOperation {
icon: string;
Expand Down Expand Up @@ -57,6 +68,7 @@ const initialPosition = {
<img
cdkDragHandle
class="ant-image-preview-img"
#imgRef
*ngIf="index === imageIndex"
[attr.src]="image.src"
[attr.alt]="image.alt"
Expand Down Expand Up @@ -157,6 +169,8 @@ export class NzImagePreviewComponent implements OnDestroy {
containerClick = new EventEmitter<void>();
closeClick = new EventEmitter<void>();

@ViewChild('imgRef') imageRef!: ElementRef<HTMLImageElement>;

private zoom: number;
private rotate: number;
private destroy$ = new Subject();
Expand Down Expand Up @@ -228,13 +242,15 @@ export class NzImagePreviewComponent implements OnDestroy {
this.zoom += 1;
this.updatePreviewImageTransform();
this.updateZoomOutDisabled();
this.position = { ...initialPosition };
}

onZoomOut(): void {
if (this.zoom > 1) {
this.zoom -= 1;
this.updatePreviewImageTransform();
this.updateZoomOutDisabled();
this.position = { ...initialPosition };
}
}

Expand Down Expand Up @@ -296,7 +312,23 @@ export class NzImagePreviewComponent implements OnDestroy {

onDragReleased(): void {
this.isDragging = false;
this.position = { ...initialPosition };
const width = this.imageRef.nativeElement.offsetWidth * this.zoom;
const height = this.imageRef.nativeElement.offsetHeight * this.zoom;
const { left, top } = getOffset(this.imageRef.nativeElement);
const { width: clientWidth, height: clientHeight } = getClientSize();
const isRotate = this.rotate % 180 !== 0;
const fitContentParams = {
width: isRotate ? height : width,
height: isRotate ? width : height,
left,
top,
clientWidth,
clientHeight
};
const fitContentPos = getFitContentPosition(fitContentParams);
if (isNotNil(fitContentPos.x) || isNotNil(fitContentPos.y)) {
this.position = { ...this.position, ...fitContentPos };
}
}

ngOnDestroy(): void {
Expand Down
110 changes: 109 additions & 1 deletion components/image/image.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,15 @@ import {
} from '@ant-design/icons-angular/icons';
import { dispatchFakeEvent } from 'ng-zorro-antd/core/testing';
import { NzIconModule, NZ_ICONS } from 'ng-zorro-antd/icon';
import { NzImage, NzImageDirective, NzImageGroupComponent, NzImageModule, NzImagePreviewRef, NzImageService } from 'ng-zorro-antd/image';
import {
getFitContentPosition,
NzImage,
NzImageDirective,
NzImageGroupComponent,
NzImageModule,
NzImagePreviewRef,
NzImageService
} from 'ng-zorro-antd/image';

const SRC = 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png`';
const QUICK_SRC =
Expand Down Expand Up @@ -346,6 +354,106 @@ describe('Preview', () => {
flush();
}));
});

describe('Drag', () => {
it('should drag released work', fakeAsync(() => {
context.images = [{ src: QUICK_SRC }];
context.createUsingService();
const previewInstance = context.previewRef?.previewInstance!;
tickChanges();
previewInstance.onDragStarted();
previewInstance.onDragReleased();
expect(previewInstance.position).toEqual({ x: 0, y: 0 });
}));

it('should position calculate correct', () => {
let params = {
width: 200,
height: 200,
top: 0,
left: 0,
clientWidth: 1080,
clientHeight: 768
};
let pos = getFitContentPosition(params);
expect(pos.x).toBe(0);
expect(pos.y).toBe(0);

params = {
width: 2000,
height: 1000,
top: 0,
left: 0,
clientWidth: 1080,
clientHeight: 768
};
pos = getFitContentPosition(params);
expect(pos.x).toBeNull();
expect(pos.y).toBeNull();

params = {
width: 2000,
height: 1000,
top: 100,
left: 100,
clientWidth: 1080,
clientHeight: 768
};
pos = getFitContentPosition(params);
expect(pos.x).toBe(460);
expect(pos.y).toBe(116);

params = {
width: 2000,
height: 1000,
top: -200,
left: -200,
clientWidth: 1080,
clientHeight: 768
};
pos = getFitContentPosition(params);
expect(pos.x).toBeNull();
expect(pos.y).toBeNull();

params = {
width: 1000,
height: 500,
top: -200,
left: -200,
clientWidth: 1080,
clientHeight: 768
};
pos = getFitContentPosition(params);
expect(pos.x).toBe(0);
expect(pos.y).toBe(0);

params = {
width: 1200,
height: 600,
top: -200,
left: -200,
clientWidth: 1080,
clientHeight: 768
};

pos = getFitContentPosition(params);
expect(pos.x).toBe(-60);
expect(pos.y).toBe(-84);

params = {
width: 1000,
height: 900,
top: -200,
left: -200,
clientWidth: 1080,
clientHeight: 768
};

pos = getFitContentPosition(params);
expect(pos.x).toBe(-40);
expect(pos.y).toBe(-66);
});
});
});

@Component({
Expand Down
1 change: 1 addition & 0 deletions components/image/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export * from './image-preview.component';
export * from './image-preview-options';
export * from './image-preview-ref';
export * from './image.module';
export * from './utils';
126 changes: 126 additions & 0 deletions components/image/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/**
* 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
*/

/**
* fit content details: https://github.com/NG-ZORRO/ng-zorro-antd/pull/6154#issuecomment-745025554
*
* calc position x,y point
*
* CASE (width <= clientWidth && height <= clientHeight):
*
* ------------- clientWidth -------------
* | |
* | ------ width ------ |
* | | | |
* | | | |
* client height | |
* Height | | |
* | | | |
* | ------------------- |
* | |
* | |
* ---------------------------------------
* fixedPosition = { x: 0, y: 0 }
*
*
*
* CASE (width > clientWidth || height > clientHeight):
*
* ------------- clientWidth -------------
* | | |
* | top |
* | | |
* |--left--|--------------- width -----------------
* | | |
* client | |
* Height | |
* | | |
* | | |
* | height |
* | | |
* ---------| |
* | |
* | |
* | |
* ----------------------------------------
*
*
* - left || top > 0
* left -> 0 || top -> 0
*
* - (left + width) < clientWidth || (top + height) < clientHeight
* - left | top + width | height < clientWidth | clientHeight -> Back left | top + width | height === clientWidth | clientHeight
*
* DEFAULT:
* - hold position
*
*/
export function getFitContentPosition(params: {
width: number;
height: number;
left: number;
top: number;
clientWidth: number;
clientHeight: number;
}): { x?: number; y?: number } {
let fixPos = {};

if (params.width <= params.clientWidth && params.height <= params.clientHeight) {
fixPos = {
x: 0,
y: 0
};
}

if (params.width > params.clientWidth || params.height > params.clientHeight) {
fixPos = {
x: fitPoint(params.left, params.width, params.clientWidth),
y: fitPoint(params.top, params.height, params.clientHeight)
};
}

return fixPos;
}

export function getOffset(node: HTMLElement): { left: number; top: number } {
const box = node.getBoundingClientRect();
const docElem = document.documentElement;

// use docElem.scrollLeft to support IE
return {
left: box.left + (window.pageXOffset || docElem.scrollLeft) - (docElem.clientLeft || document.body.clientLeft || 0),
top: box.top + (window.pageYOffset || docElem.scrollTop) - (docElem.clientTop || document.body.clientTop || 0)
};
}

export function getClientSize(): { width: number; height: number } {
const width = document.documentElement.clientWidth;
const height = window.innerHeight || document.documentElement.clientHeight;
return {
width,
height
};
}

function fitPoint(start: number, size: number, clientSize: number): number | null {
const startAddSize = start + size;
const offsetStart = (size - clientSize) / 2;
let distance: number | null = null;

if (size > clientSize) {
if (start > 0) {
distance = offsetStart;
}
if (start < 0 && startAddSize < clientSize) {
distance = -offsetStart;
}
} else {
if (start < 0 || startAddSize > clientSize) {
distance = start < 0 ? offsetStart : -offsetStart;
}
}

return distance;
}

0 comments on commit 07ae66a

Please sign in to comment.