提交 e23bd269 编写于 作者: 无木

feat(preview): add more features

为Preview组件添加新的属性及事件
上级 98749ec6
### ✨ Features
- **Preview** 添加新的属性及事件
### 🐛 Bug Fixes
- **ApiTreeSelect** 修复未能正确监听`params`变化的问题
......
......@@ -38,13 +38,33 @@
type: Number as PropType<number>,
default: 0,
},
scaleStep: {
type: Number as PropType<number>,
},
defaultWidth: {
type: Number as PropType<number>,
},
maskClosable: {
type: Boolean as PropType<boolean>,
},
rememberState: {
type: Boolean as PropType<boolean>,
},
};
const prefixCls = 'img-preview';
export default defineComponent({
name: 'ImagePreview',
props,
setup(props: Props) {
emits: ['img-load', 'img-error'],
setup(props: Props, { expose, emit }) {
interface stateInfo {
scale: number;
rotate: number;
top: number;
left: number;
}
const stateMap = new Map<string, stateInfo>();
const imgState = reactive<ImgState>({
currentUrl: '',
imgScale: 1,
......@@ -96,6 +116,14 @@
};
}
const getScaleStep = computed(() => {
if (props.scaleStep > 0 && props.scaleStep < 100) {
return props.scaleStep / 100;
} else {
return imgState.imgScale / 10;
}
});
// 监听鼠标滚轮
function scrollFunc(e: any) {
e = e || window.event;
......@@ -104,11 +132,11 @@
e.preventDefault();
if (e.delta > 0) {
// 滑轮向上滚动
scaleFunc(0.015);
scaleFunc(getScaleStep.value);
}
if (e.delta < 0) {
// 滑轮向下滚动
scaleFunc(-0.015);
scaleFunc(-getScaleStep.value);
}
}
// 缩放函数
......@@ -134,11 +162,54 @@
imgState.status = StatueEnum.LOADING;
const img = new Image();
img.src = url;
img.onload = () => {
img.onload = (e: Event) => {
if (imgState.currentUrl !== url) {
const ele: HTMLElement[] = e.composedPath();
if (props.rememberState) {
// 保存当前图片的缩放信息
stateMap.set(imgState.currentUrl, {
scale: imgState.imgScale,
top: imgState.imgTop,
left: imgState.imgLeft,
rotate: imgState.imgRotate,
});
// 如果之前已存储缩放信息,就应用
const stateInfo = stateMap.get(url);
if (stateInfo) {
imgState.imgScale = stateInfo.scale;
imgState.imgTop = stateInfo.top;
imgState.imgRotate = stateInfo.rotate;
imgState.imgLeft = stateInfo.left;
} else {
initState();
if (props.defaultWidth) {
imgState.imgScale = props.defaultWidth / ele[0].naturalWidth;
}
}
} else {
if (props.defaultWidth) {
imgState.imgScale = props.defaultWidth / ele[0].naturalWidth;
}
}
ele &&
emit('img-load', {
index: imgState.currentIndex,
dom: ele[0] as HTMLImageElement,
url,
});
}
imgState.currentUrl = url;
imgState.status = StatueEnum.DONE;
};
img.onerror = () => {
img.onerror = (e: Event) => {
const ele: EventTarget[] = e.composedPath();
ele &&
emit('img-error', {
index: imgState.currentIndex,
dom: ele[0] as HTMLImageElement,
url,
});
imgState.status = StatueEnum.FAIL;
};
}
......@@ -146,6 +217,10 @@
// 关闭
function handleClose(e: MouseEvent) {
e && e.stopPropagation();
close();
}
function close() {
imgState.show = false;
// 移除火狐浏览器下的鼠标滚动事件
document.body.removeEventListener('DOMMouseScroll', scrollFunc);
......@@ -158,6 +233,19 @@
initState();
}
expose({
resume,
close,
prev: handleChange.bind(null, 'left'),
next: handleChange.bind(null, 'right'),
setScale: (scale: number) => {
if (scale > 0 && scale <= 10) imgState.imgScale = scale;
},
setRotate: (rotate: number) => {
imgState.imgRotate = rotate;
},
} as PreviewActions);
// 上一页下一页
function handleChange(direction: 'left' | 'right') {
const { currentIndex } = imgState;
......@@ -205,6 +293,7 @@
transform: `scale(${imgScale}) rotate(${imgRotate}deg)`,
marginTop: `${imgTop}px`,
marginLeft: `${imgLeft}px`,
maxWidth: props.defaultWidth ? 'unset' : '100%',
};
});
......@@ -222,6 +311,16 @@
}
});
const handleMaskClick = (e: MouseEvent) => {
if (
props.maskClosable &&
e.target &&
(e.target as HTMLDivElement).classList.contains(`${prefixCls}-content`)
) {
handleClose(e);
}
};
const renderClose = () => {
return (
<div class={`${prefixCls}__close`} onClick={handleClose}>
......@@ -246,10 +345,16 @@
const renderController = () => {
return (
<div class={`${prefixCls}__controller`}>
<div class={`${prefixCls}__controller-item`} onClick={() => scaleFunc(-0.15)}>
<div
class={`${prefixCls}__controller-item`}
onClick={() => scaleFunc(-getScaleStep.value)}
>
<img src={unScaleSvg} />
</div>
<div class={`${prefixCls}__controller-item`} onClick={() => scaleFunc(0.15)}>
<div
class={`${prefixCls}__controller-item`}
onClick={() => scaleFunc(getScaleStep.value)}
>
<img src={scaleSvg} />
</div>
<div class={`${prefixCls}__controller-item`} onClick={resume}>
......@@ -279,7 +384,12 @@
return () => {
return (
imgState.show && (
<div class={prefixCls} ref={wrapElRef} onMouseup={handleMouseUp}>
<div
class={prefixCls}
ref={wrapElRef}
onMouseup={handleMouseUp}
onClick={handleMaskClick}
>
<div class={`${prefixCls}-content`}>
{/*<Spin*/}
{/* indicator={<LoadingOutlined style="font-size: 24px" spin />}*/}
......
......@@ -6,15 +6,12 @@ import { createVNode, render } from 'vue';
let instance: ReturnType<typeof createVNode> | null = null;
export function createImgPreview(options: Options) {
if (!isClient) return;
const { imageList, show = true, index = 0 } = options;
const propsData: Partial<Props> = {};
const container = document.createElement('div');
propsData.imageList = imageList;
propsData.show = show;
propsData.index = index;
Object.assign(propsData, { show: true, index: 0, scaleStep: 100 }, options);
instance = createVNode(ImgPreview, propsData);
render(instance, container);
document.body.appendChild(container);
return instance.component?.exposed;
}
......@@ -2,6 +2,12 @@ export interface Options {
show?: boolean;
imageList: string[];
index?: number;
scaleStep?: number;
defaultWidth?: number;
maskClosable?: boolean;
rememberState?: boolean;
onImgLoad?: (img: HTMLImageElement) => void;
onImgError?: (img: HTMLImageElement) => void;
}
export interface Props {
......@@ -9,6 +15,19 @@ export interface Props {
instance: Props;
imageList: string[];
index: number;
scaleStep: number;
defaultWidth: number;
maskClosable: boolean;
rememberState: boolean;
}
export interface PreviewActions {
resume: () => void;
close: () => void;
prev: () => void;
next: () => void;
setScale: (scale: number) => void;
setRotate: (rotate: number) => void;
}
export interface ImageProps {
......
......@@ -8,6 +8,7 @@
import { defineComponent } from 'vue';
import { createImgPreview, ImagePreview } from '/@/components/Preview/index';
import { PageWrapper } from '/@/components/Page';
// import { PreviewActions } from '/@/components/Preview/src/typing';
const imgList: string[] = [
'https://picsum.photos/id/66/346/216',
......@@ -18,7 +19,11 @@
components: { PageWrapper, ImagePreview },
setup() {
function openImg() {
createImgPreview({ imageList: imgList });
const onImgLoad = ({ index, url, dom }) => {
console.log(`第${index + 1}张图片已加载,URL为:${url}`, dom);
};
// 可以使用createImgPreview返回的 PreviewActions 来控制预览逻辑,实现类似幻灯片、自动旋转之类的骚操作
createImgPreview({ imageList: imgList, defaultWidth: 700, rememberState: true, onImgLoad });
}
return { imgList, openImg };
},
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册