| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- import { use, range } from '../utils';
- import { preventDefault } from '../utils/event';
- import { PopupMixin } from '../mixins/popup';
- import { TouchMixin } from '../mixins/touch';
- import Swipe from '../swipe';
- import SwipeItem from '../swipe-item';
- const [sfc, bem] = use('image-preview');
- function getDistance(touches) {
- return Math.sqrt(
- Math.abs(
- (touches[0].clientX - touches[1].clientX) *
- (touches[0].clientY - touches[1].clientY)
- )
- );
- }
- export default sfc({
- mixins: [PopupMixin, TouchMixin],
- props: {
- images: Array,
- className: null,
- lazyLoad: Boolean,
- asyncClose: Boolean,
- startPosition: Number,
- showIndicators: Boolean,
- loop: {
- type: Boolean,
- default: true
- },
- overlay: {
- type: Boolean,
- default: true
- },
- showIndex: {
- type: Boolean,
- default: true
- },
- minZoom: {
- type: Number,
- default: 1 / 3
- },
- maxZoom: {
- type: Number,
- default: 3
- },
- overlayClass: {
- type: String,
- default: 'van-image-preview__overlay'
- },
- closeOnClickOverlay: {
- type: Boolean,
- default: true
- }
- },
- data() {
- return {
- scale: 1,
- moveX: 0,
- moveY: 0,
- moving: false,
- zooming: false,
- active: 0
- };
- },
- computed: {
- imageStyle() {
- const { scale } = this;
- const style = {
- transition: this.zooming || this.moving ? '' : '.3s all'
- };
- if (scale !== 1) {
- style.transform = `scale3d(${scale}, ${scale}, 1) translate(${this.moveX /
- scale}px, ${this.moveY / scale}px)`;
- }
- return style;
- }
- },
- watch: {
- value() {
- this.active = this.startPosition;
- },
- startPosition(active) {
- this.active = active;
- }
- },
- methods: {
- onWrapperTouchStart() {
- this.touchStartTime = new Date();
- },
- onWrapperTouchEnd(event) {
- preventDefault(event);
- const deltaTime = new Date() - this.touchStartTime;
- const { offsetX = 0, offsetY = 0 } = this.$refs.swipe || {};
- // prevent long tap to close component
- if (deltaTime < 300 && offsetX < 10 && offsetY < 10) {
- const index = this.active;
- this.resetScale();
- this.$emit('close', {
- index,
- url: this.images[index]
- });
- if (!this.asyncClose) {
- this.$emit('input', false);
- }
- }
- },
- startMove(event) {
- const image = event.currentTarget;
- const rect = image.getBoundingClientRect();
- const winWidth = window.innerWidth;
- const winHeight = window.innerHeight;
- this.touchStart(event);
- this.moving = true;
- this.startMoveX = this.moveX;
- this.startMoveY = this.moveY;
- this.maxMoveX = Math.max(0, (rect.width - winWidth) / 2);
- this.maxMoveY = Math.max(0, (rect.height - winHeight) / 2);
- },
- startZoom(event) {
- this.moving = false;
- this.zooming = true;
- this.startScale = this.scale;
- this.startDistance = getDistance(event.touches);
- },
- onTouchStart(event) {
- const { touches } = event;
- const { offsetX = 0 } = this.$refs.swipe || {};
- if (touches.length === 1 && this.scale !== 1) {
- this.startMove(event);
- } /* istanbul ignore else */ else if (touches.length === 2 && !offsetX) {
- this.startZoom(event);
- }
- },
- onTouchMove(event) {
- const { touches } = event;
- if (this.moving || this.zooming) {
- preventDefault(event, true);
- }
- if (this.moving) {
- this.touchMove(event);
- const moveX = this.deltaX + this.startMoveX;
- const moveY = this.deltaY + this.startMoveY;
- this.moveX = range(moveX, -this.maxMoveX, this.maxMoveX);
- this.moveY = range(moveY, -this.maxMoveY, this.maxMoveY);
- }
- if (this.zooming && touches.length === 2) {
- const distance = getDistance(touches);
- const scale = (this.startScale * distance) / this.startDistance;
- this.scale = range(scale, this.minZoom, this.maxZoom);
- }
- },
- onTouchEnd(event) {
- /* istanbul ignore else */
- if (this.moving || this.zooming) {
- let stopPropagation = true;
- if (
- this.moving &&
- this.startMoveX === this.moveX &&
- this.startMoveY === this.moveY
- ) {
- stopPropagation = false;
- }
- if (!event.touches.length) {
- this.moving = false;
- this.zooming = false;
- this.startMoveX = 0;
- this.startMoveY = 0;
- this.startScale = 1;
- if (this.scale < 1) {
- this.resetScale();
- }
- }
- if (stopPropagation) {
- preventDefault(event, true);
- }
- }
- },
- onChange(active) {
- this.resetScale();
- this.active = active;
- this.$emit('change', active);
- },
- resetScale() {
- this.scale = 1;
- this.moveX = 0;
- this.moveY = 0;
- }
- },
- render(h) {
- if (!this.value) {
- return;
- }
- const { active, images } = this;
- const Index = this.showIndex && (
- <div class={bem('index')}>
- {this.slots('index') || `${active + 1}/${images.length}`}
- </div>
- );
- const Images = (
- <Swipe
- ref="swipe"
- loop={this.loop}
- indicatorColor="white"
- initialSwipe={this.startPosition}
- showIndicators={this.showIndicators}
- onChange={this.onChange}
- >
- {images.map((image, index) => {
- const props = {
- class: bem('image'),
- style: index === active ? this.imageStyle : null,
- on: {
- touchstart: this.onTouchStart,
- touchmove: this.onTouchMove,
- touchend: this.onTouchEnd,
- touchcancel: this.onTouchEnd
- }
- };
- return (
- <SwipeItem>
- {this.lazyLoad ? (
- <img vLazy={image} {...props} />
- ) : (
- <img src={image} {...props} />
- )}
- </SwipeItem>
- );
- })}
- </Swipe>
- );
- return (
- <transition name="van-fade">
- <div
- class={[bem(), this.className]}
- onTouchstart={this.onWrapperTouchStart}
- onTouchend={this.onWrapperTouchEnd}
- onTouchcancel={this.onWrapperTouchEnd}
- >
- {Index}
- {Images}
- </div>
- </transition>
- );
- }
- });
|