UNPKG

@nutui/nutui-react

Version:

京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序

217 lines (216 loc) 9.48 kB
import { _ as _object_spread } from "@swc/helpers/_/_object_spread"; import { _ as _object_spread_props } from "@swc/helpers/_/_object_spread_props"; import { _ as _sliced_to_array } from "@swc/helpers/_/_sliced_to_array"; import { _ as _to_consumable_array } from "@swc/helpers/_/_to_consumable_array"; import React, { useEffect, useRef, useState } from "react"; import classNames from "classnames"; import { Close } from "@nutui/icons-react"; import Popup from "../popup"; import Image from "../image"; import Video from "../video"; import Swiper from "../swiper"; import SwiperItem from "../swiperitem"; import { ComponentDefaults } from "../../utils/typings"; import { usePropsValue } from "../../hooks/use-props-value"; var defaultProps = _object_spread_props(_object_spread({}, ComponentDefaults), { images: [], videos: [], visible: false, autoPlay: 3000, defaultValue: 0, closeOnContentClick: false, pagination: true, indicator: false, indicatorColor: '#fff', closeIcon: false, closeIconPosition: 'top-right', onChange: function() {}, onClose: function() {} }); export var ImagePreview = function(props) { var _$_object_spread = _object_spread({}, defaultProps, props), value = _$_object_spread.value, className = _$_object_spread.className, style = _$_object_spread.style, images = _$_object_spread.images, videos = _$_object_spread.videos, visible = _$_object_spread.visible, defaultValue = _$_object_spread.defaultValue, indicatorColor = _$_object_spread.indicatorColor, pagination = _$_object_spread.pagination, indicator = _$_object_spread.indicator, autoPlay = _$_object_spread.autoPlay, closeOnContentClick = _$_object_spread.closeOnContentClick, closeIcon = _$_object_spread.closeIcon, closeIconPosition = _$_object_spread.closeIconPosition, onClose = _$_object_spread.onClose, onChange = _$_object_spread.onChange; var classPrefix = 'nut-imagepreview'; var ref = useRef(null); var _usePropsValue = _sliced_to_array(usePropsValue({ value: value, defaultValue: defaultValue, finalValue: defaultValue, onChange: onChange }), 2), innerNo = _usePropsValue[0], setInnerNo = _usePropsValue[1]; var _useState = _sliced_to_array(useState(visible), 2), showPop = _useState[0], setShowPop = _useState[1]; var _useState1 = _sliced_to_array(useState(0), 2), active = _useState1[0], setActive = _useState1[1]; var _useState2 = _sliced_to_array(useState(images.length + videos.length), 2), maxNo = _useState2[0], setMaxNo = _useState2[1]; var _useState3 = _sliced_to_array(useState({ scale: 1, moveable: false, oriDistance: 0, originScale: 1 }), 2), store = _useState3[0], setStore = _useState3[1]; var lastTouchEndTime = useRef(0) // 用来辅助监听双击 ; var onTouchStart = function(event) { var touches = event.touches; var events = touches[0]; var events2 = touches[1]; // 如果是原尺寸,双击放大;否则回到原尺寸。 var curTouchTime = Date.now(); if (curTouchTime - lastTouchEndTime.current < 100) { var store1 = store; store1.scale = store1.scale === 1 ? 2 : 1; scaleNow(); } var store11 = store; store11.moveable = true; if (events2) { // 如果开始两指操作,记录初始时刻两指间的距离 store11.oriDistance = getDistance(events, events2); } // 取到开始两指操作时的放大(缩小比例),store.scale 存储的是当前的放缩比(相对于标准大小 scale 为 1 的情况的放大缩小比) store11.originScale = store11.scale; }; var onTouchMove = function(event) { if (!store.moveable) return; var touches = event.touches; var events = touches[0]; var events2 = touches[1]; var store1 = store; // 双指移动 if (events2) { var curDistance = getDistance(events, events2); /** 此处计算倍数,距离放大(缩小) k 倍则 scale 也 扩大(缩小) k 倍。距离放大(缩小)倍数 = 结束时两点距离 除以 开始时两点距离 * 注意此处的 scale 变化是基于 store.scale 的。 * store.scale 是一个暂存值,比如第一次放大 2 倍,则 store.scale 为 2。 * 再次两指触碰的时候,store.originScale 就为 store.scale 的值,基于此时的 store.scale 继续放大缩小。 * */ var curScale = curDistance / store1.oriDistance; // 最大放大 3 倍,缩小后松手要弹回原比例 store1.scale = Math.min(store1.originScale * curScale, 3); scaleNow(); } }; var onTouchEnd = function() { lastTouchEndTime.current = Date.now(); var store1 = store; store1.moveable = false; if (store1.scale < 1.1 && store1.scale > 1 || store1.scale < 1) { store1.scale = 1; scaleNow(); } }; useEffect(function() { init(); }, []); var init = function() { document.addEventListener('touchmove', onTouchMove); document.addEventListener('touchend', onTouchEnd); document.addEventListener('touchcancel', onTouchEnd); return function() { document.removeEventListener('touchcancel', onTouchEnd); document.removeEventListener('touchmove', onTouchMove); document.removeEventListener('touchend', onTouchEnd); }; }; useEffect(function() { setShowPop(visible); }, [ visible ]); useEffect(function() { setInnerNo(defaultValue || 1); }, [ defaultValue ]); useEffect(function() { setActive(innerNo); }, [ innerNo ]); useEffect(function() { setMaxNo(images.length + videos.length); }, [ images, videos ]); var scaleNow = function() { if (ref.current) { ref.current.style.transform = "scale(".concat(store.scale, ")"); } }; // 用于查找给定数字的斜边。起止两点间距离。 var getDistance = function(first, second) { return Math.hypot(Math.abs(second.pageX - first.pageX), Math.abs(second.pageY - first.pageY)); }; var slideChangeEnd = function(page) { setActive(page + 1); onChange === null || onChange === void 0 ? void 0 : onChange(page + 1); }; var onCloseInner = function(e) { e.stopPropagation(); setShowPop(false); setActive(innerNo); scaleNow(); onClose === null || onClose === void 0 ? void 0 : onClose(); setStore(_object_spread_props(_object_spread({}, store), { scale: 1 })); }; var closeOnImg = function(e) { e.stopPropagation(); // 点击内容区域的图片是否可以关闭弹层(视频区域由于nut-video做了限制,无法关闭弹层) if (closeOnContentClick) onCloseInner(e); }; var duration = typeof autoPlay === 'string' ? parseInt(autoPlay) : autoPlay; return /*#__PURE__*/ React.createElement(Popup, { visible: showPop, className: "".concat(classPrefix, "-pop"), onClick: onCloseInner }, /*#__PURE__*/ React.createElement("div", { className: classNames(classPrefix, className), style: style, ref: ref, onTouchStart: onTouchStart }, showPop && /*#__PURE__*/ React.createElement(Swiper, { autoPlay: !!duration, duration: duration, className: "".concat(classPrefix, "-swiper"), loop: true, style: { '--nutui-indicator-color': indicatorColor }, direction: "horizontal", onChange: function(page) { return slideChangeEnd(page); }, defaultValue: innerNo > maxNo ? maxNo - 1 : innerNo - 1, indicator: indicator }, _to_consumable_array(videos.map(function(item) { return { type: 'video', data: item }; })).concat(_to_consumable_array(images.map(function(item) { return { type: 'image', data: item }; }))).sort(function(a, b) { var _a_data_index, _b_data_index; return ((_a_data_index = a.data.index) !== null && _a_data_index !== void 0 ? _a_data_index : 0) - ((_b_data_index = b.data.index) !== null && _b_data_index !== void 0 ? _b_data_index : 0); }).map(function(item, index) { return /*#__PURE__*/ React.createElement(SwiperItem, { key: index }, item.type === 'video' ? /*#__PURE__*/ React.createElement(Video, { source: item.data.source, options: item.data.options, onClick: closeOnImg }) : /*#__PURE__*/ React.createElement(Image, { src: item.data.src, draggable: false, onClick: closeOnImg })); }))), pagination && /*#__PURE__*/ React.createElement("div", { className: "".concat(classPrefix, "-index") }, active, "/", maxNo), closeIcon !== false && /*#__PURE__*/ React.createElement("div", { className: "".concat(classPrefix, "-close ").concat(closeIconPosition), onClick: onCloseInner }, closeIcon === true ? /*#__PURE__*/ React.createElement(Close, null) : closeIcon)); }; ImagePreview.displayName = 'NutImagePreview';