mirror of
https://gitee.com/ant-design-vue/ant-design-vue.git
synced 2024-11-29 18:48:32 +08:00
feat: add image (#3235)
* feat: 🆕 image组件 - 添加样式 * feat: 🆕 增加 AImage 组件 * feat: 🆕 参考 ant-design 增加 相关 Image 组件的less定义 * feat: 🆕 Image placeholder 的修改 * fix: 🐞 去除 onPreviewClose 相关 * test: 🧪 image width/height 以及相关测试 * fix: 🐞 fix lint no-setup-props-destructure * test: 🧪 image test snap file * refactor: 💡 去掉多加了的文件,重构文件以使逻辑清晰 * feat: 🆕 rc-image 相关内容 列入 vc-image 文件夹中 * feat: 🆕 antd 4.9.1 增加 image-preview-group * feat: 🆕 add ImagePropsType * feat: udpate image components * feat: update image * feat: update image Co-authored-by: tanjinzhou <415800467@qq.com>
This commit is contained in:
parent
c6b189b583
commit
5913cf9c5c
@ -1 +1 @@
|
|||||||
Subproject commit ab88ac19de8ce0d3c8a559ad7bcd5fc04532c184
|
Subproject commit db458a2276cd9156a7824f4e876de5702efd9ff7
|
25
components/image/PreviewGroup.tsx
Normal file
25
components/image/PreviewGroup.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import PreviewGroup from '../vc-image/src/PreviewGroup';
|
||||||
|
import { defineComponent, inject } from 'vue';
|
||||||
|
import { defaultConfigProvider } from '../config-provider';
|
||||||
|
import PropTypes from '../_util/vue-types';
|
||||||
|
|
||||||
|
const InternalPreviewGroup = defineComponent({
|
||||||
|
name: 'AImagePreviewGroup',
|
||||||
|
inheritAttrs: false,
|
||||||
|
props: { previewPrefixCls: PropTypes.string },
|
||||||
|
setup(props, { attrs, slots }) {
|
||||||
|
const configProvider = inject('configProvider', defaultConfigProvider);
|
||||||
|
return () => {
|
||||||
|
const { getPrefixCls } = configProvider;
|
||||||
|
const prefixCls = getPrefixCls('image-preview', props.previewPrefixCls);
|
||||||
|
return (
|
||||||
|
<PreviewGroup
|
||||||
|
previewPrefixCls={prefixCls}
|
||||||
|
{...{ ...attrs, ...props }}
|
||||||
|
v-slots={slots}
|
||||||
|
></PreviewGroup>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
export default InternalPreviewGroup;
|
3
components/image/__tests__/demo.test.js
Normal file
3
components/image/__tests__/demo.test.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import demoTest from '../../../tests/shared/demoTest';
|
||||||
|
|
||||||
|
demoTest('image');
|
32
components/image/__tests__/index.test.js
Normal file
32
components/image/__tests__/index.test.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import Image from '..';
|
||||||
|
import mountTest from '../../../tests/shared/mountTest';
|
||||||
|
import { mount } from '@vue/test-utils';
|
||||||
|
describe('Image', () => {
|
||||||
|
mountTest(Image);
|
||||||
|
it('image size', () => {
|
||||||
|
const wrapper = mount({
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
width="200px"
|
||||||
|
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(wrapper.find('.ant-image').element.style.width).toBe('200px');
|
||||||
|
});
|
||||||
|
it('image size number', () => {
|
||||||
|
const wrapper = mount({
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
width={200}
|
||||||
|
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(wrapper.find('.ant-image').element.style.width).toBe('200px');
|
||||||
|
});
|
||||||
|
});
|
35
components/image/index.tsx
Normal file
35
components/image/index.tsx
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { App, defineComponent, inject, Plugin } from 'vue';
|
||||||
|
import { defaultConfigProvider } from '../config-provider';
|
||||||
|
import ImageInternal from '../vc-image';
|
||||||
|
import { ImageProps, ImagePropsType } from '../vc-image/src/Image';
|
||||||
|
|
||||||
|
import PreviewGroup from './PreviewGroup';
|
||||||
|
const Image = defineComponent({
|
||||||
|
name: 'AImage',
|
||||||
|
inheritAttrs: false,
|
||||||
|
props: ImageProps,
|
||||||
|
setup(props, ctx) {
|
||||||
|
const { slots, attrs } = ctx;
|
||||||
|
const configProvider = inject('configProvider', defaultConfigProvider);
|
||||||
|
return () => {
|
||||||
|
const { getPrefixCls } = configProvider;
|
||||||
|
const prefixCls = getPrefixCls('image', props.prefixCls);
|
||||||
|
return <ImageInternal {...{ ...attrs, ...props, prefixCls }} v-slots={slots}></ImageInternal>;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export { ImageProps, ImagePropsType };
|
||||||
|
|
||||||
|
Image.PreviewGroup = PreviewGroup;
|
||||||
|
|
||||||
|
Image.install = function(app: App) {
|
||||||
|
app.component(Image.name, Image);
|
||||||
|
app.component(Image.PreviewGroup.name, Image.PreviewGroup);
|
||||||
|
return app;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Image as typeof Image &
|
||||||
|
Plugin & {
|
||||||
|
readonly PreviewGroup: typeof PreviewGroup;
|
||||||
|
};
|
141
components/image/style/index.less
Normal file
141
components/image/style/index.less
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
@import '../../style/themes/index';
|
||||||
|
@import '../../style/mixins/index';
|
||||||
|
|
||||||
|
@image-prefix-cls: ~'@{ant-prefix}-image';
|
||||||
|
@image-preview-prefix-cls: ~'@{image-prefix-cls}-preview';
|
||||||
|
|
||||||
|
.@{image-prefix-cls} {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
&-img {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
&-placeholder {
|
||||||
|
background-color: @image-bg;
|
||||||
|
background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMTQuNSAyLjVoLTEzQS41LjUgMCAwIDAgMSAzdjEwYS41LjUgMCAwIDAgLjUuNWgxM2EuNS41IDAgMCAwIC41LS41VjNhLjUuNSAwIDAgMC0uNS0uNXpNNS4yODEgNC43NWExIDEgMCAwIDEgMCAyIDEgMSAwIDAgMSAwLTJ6bTguMDMgNi44M2EuMTI3LjEyNyAwIDAgMS0uMDgxLjAzSDIuNzY5YS4xMjUuMTI1IDAgMCAxLS4wOTYtLjIwN2wyLjY2MS0zLjE1NmEuMTI2LjEyNiAwIDAgMSAuMTc3LS4wMTZsLjAxNi4wMTZMNy4wOCAxMC4wOWwyLjQ3LTIuOTNhLjEyNi4xMjYgMCAwIDEgLjE3Ny0uMDE2bC4wMTUuMDE2IDMuNTg4IDQuMjQ0YS4xMjcuMTI3IDAgMCAxLS4wMi4xNzV6IiBmaWxsPSIjOEM4QzhDIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48L3N2Zz4=);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center center;
|
||||||
|
background-size: 30%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-placeholder {
|
||||||
|
.box;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-preview {
|
||||||
|
.modal-mask;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&-body {
|
||||||
|
.box;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-img {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
vertical-align: middle;
|
||||||
|
transform: scale3d(1, 1, 1);
|
||||||
|
cursor: grab;
|
||||||
|
transition: transform 0.3s @ease-out 0s;
|
||||||
|
user-select: none;
|
||||||
|
pointer-events: auto;
|
||||||
|
&-wrapper {
|
||||||
|
.box;
|
||||||
|
transition: transform 0.3s @ease-out 0s;
|
||||||
|
&::before {
|
||||||
|
display: inline-block;
|
||||||
|
width: 1px;
|
||||||
|
height: 50%;
|
||||||
|
margin-right: -1px;
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-moving {
|
||||||
|
.@{image-prefix-cls}-preview-img {
|
||||||
|
cursor: grabbing;
|
||||||
|
&-wrapper {
|
||||||
|
transition-duration: 0s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-wrap {
|
||||||
|
z-index: @zindex-image;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-operations {
|
||||||
|
.reset-component;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
color: @image-preview-operation-color;
|
||||||
|
list-style: none;
|
||||||
|
background: fade(@modal-mask-bg, 10%);
|
||||||
|
pointer-events: auto;
|
||||||
|
|
||||||
|
&-operation {
|
||||||
|
margin-left: @control-padding-horizontal;
|
||||||
|
padding: @control-padding-horizontal;
|
||||||
|
cursor: pointer;
|
||||||
|
&-disabled {
|
||||||
|
color: @image-preview-operation-disabled-color;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
&:last-of-type {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-icon {
|
||||||
|
font-size: @image-preview-operation-size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-switch-left,
|
||||||
|
&-switch-right {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 10px;
|
||||||
|
z-index: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
margin-top: -22px;
|
||||||
|
color: @image-preview-operation-color;
|
||||||
|
background: fade(@modal-mask-bg, 10%);
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: pointer;
|
||||||
|
pointer-events: auto;
|
||||||
|
&-disabled {
|
||||||
|
color: @image-preview-operation-disabled-color;
|
||||||
|
cursor: not-allowed;
|
||||||
|
> .anticon {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> .anticon {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-switch-left {
|
||||||
|
left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-switch-right {
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2
components/image/style/index.ts
Normal file
2
components/image/style/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import '../../style/index.less';
|
||||||
|
import './index.less';
|
@ -133,7 +133,7 @@ import { default as Drawer } from './drawer';
|
|||||||
import { default as Skeleton } from './skeleton';
|
import { default as Skeleton } from './skeleton';
|
||||||
|
|
||||||
import { default as Comment } from './comment';
|
import { default as Comment } from './comment';
|
||||||
|
import { default as Image } from './image';
|
||||||
// import { default as ColorPicker } from './color-picker';
|
// import { default as ColorPicker } from './color-picker';
|
||||||
|
|
||||||
import { default as ConfigProvider } from './config-provider';
|
import { default as ConfigProvider } from './config-provider';
|
||||||
@ -209,6 +209,7 @@ const components = [
|
|||||||
Descriptions,
|
Descriptions,
|
||||||
PageHeader,
|
PageHeader,
|
||||||
Space,
|
Space,
|
||||||
|
Image,
|
||||||
];
|
];
|
||||||
|
|
||||||
const install = function(app: App) {
|
const install = function(app: App) {
|
||||||
@ -296,6 +297,7 @@ export {
|
|||||||
Descriptions,
|
Descriptions,
|
||||||
PageHeader,
|
PageHeader,
|
||||||
Space,
|
Space,
|
||||||
|
Image,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -60,4 +60,5 @@ import './descriptions/style';
|
|||||||
import './page-header/style';
|
import './page-header/style';
|
||||||
import './form/style';
|
import './form/style';
|
||||||
import './space/style';
|
import './space/style';
|
||||||
|
import './image/style';
|
||||||
// import './color-picker/style';
|
// import './color-picker/style';
|
||||||
|
7
components/style/mixins/box.less
Normal file
7
components/style/mixins/box.less
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.box(@position: absolute) {
|
||||||
|
position: @position;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
@ -8,3 +8,5 @@
|
|||||||
@import 'reset';
|
@import 'reset';
|
||||||
@import 'operation-unit';
|
@import 'operation-unit';
|
||||||
@import 'typography';
|
@import 'typography';
|
||||||
|
@import 'box';
|
||||||
|
@import 'modal-mask';
|
||||||
|
31
components/style/mixins/modal-mask.less
Normal file
31
components/style/mixins/modal-mask.less
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
@import 'box';
|
||||||
|
|
||||||
|
.modal-mask() {
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
&.zoom-enter,
|
||||||
|
&.zoom-appear {
|
||||||
|
transform: none; // reset scale avoid mousePosition bug
|
||||||
|
opacity: 0;
|
||||||
|
animation-duration: @animation-duration-slow;
|
||||||
|
user-select: none; // https://github.com/ant-design/ant-design/issues/11777
|
||||||
|
}
|
||||||
|
|
||||||
|
&-mask {
|
||||||
|
.box(fixed);
|
||||||
|
z-index: @zindex-modal-mask;
|
||||||
|
height: 100%;
|
||||||
|
background-color: @modal-mask-bg;
|
||||||
|
|
||||||
|
&-hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-wrap {
|
||||||
|
.box(fixed);
|
||||||
|
overflow: auto;
|
||||||
|
outline: 0;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
}
|
@ -288,6 +288,7 @@
|
|||||||
@zindex-dropdown: 1050;
|
@zindex-dropdown: 1050;
|
||||||
@zindex-picker: 1050;
|
@zindex-picker: 1050;
|
||||||
@zindex-tooltip: 1060;
|
@zindex-tooltip: 1060;
|
||||||
|
@zindex-image: 1080;
|
||||||
|
|
||||||
// Animation
|
// Animation
|
||||||
@animation-duration-slow: 0.3s; // Modal
|
@animation-duration-slow: 0.3s; // Modal
|
||||||
@ -720,3 +721,13 @@
|
|||||||
@typography-title-font-weight: 600;
|
@typography-title-font-weight: 600;
|
||||||
@typography-title-margin-top: 1.2em;
|
@typography-title-margin-top: 1.2em;
|
||||||
@typography-title-margin-bottom: 0.5em;
|
@typography-title-margin-bottom: 0.5em;
|
||||||
|
|
||||||
|
// Image
|
||||||
|
// ---
|
||||||
|
@image-size-base: 48px;
|
||||||
|
@image-font-size-base: 24px;
|
||||||
|
@image-bg: #f5f5f5;
|
||||||
|
@image-color: #fff;
|
||||||
|
@image-preview-operation-size: 18px;
|
||||||
|
@image-preview-operation-color: @text-color-dark;
|
||||||
|
@image-preview-operation-disabled-color: fade(@image-preview-operation-color, 45%);
|
||||||
|
313
components/vc-image/assets/index.less
Normal file
313
components/vc-image/assets/index.less
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
@import '../../style/themes/index';
|
||||||
|
@import '../../style/mixins/index';
|
||||||
|
|
||||||
|
@prefixCls: ~'@{ant-prefix}-image';
|
||||||
|
@zindex-preview-mask: 1000;
|
||||||
|
@preview-mask-bg: fade(#000, 40%);
|
||||||
|
@text-color: #bbb;
|
||||||
|
@text-color-disabled: darken(@text-color, 30%);
|
||||||
|
@background-color: #f3f3f3;
|
||||||
|
|
||||||
|
.reset() {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box() {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{prefixCls} {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
&-img {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
&-placeholder {
|
||||||
|
background-color: @background-color;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center center;
|
||||||
|
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjhweCIgaGVpZ2h0PSIyMnB4IiB2aWV3Qm94PSIwIDAgMjggMjIiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDU1LjIgKDc4MTgxKSAtIGh0dHBzOi8vc2tldGNoYXBwLmNvbSAtLT4KICAgIDx0aXRsZT5pbWFnZS1maWxs5aSH5Lu9PC90aXRsZT4KICAgIDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPgogICAgPGcgaWQ9Iuafpeeci+WbvueJh+S8mOWMljQuMCIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9IuWKoOi9veWbvueJhyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTU3Mi4wMDAwMDAsIC01MDYuMDAwMDAwKSI+CiAgICAgICAgICAgIDxnIGlkPSJpbWFnZS1maWxs5aSH5Lu9IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1NzAuMDAwMDAwLCA1MDEuMDAwMDAwKSI+CiAgICAgICAgICAgICAgICA8cmVjdCBpZD0iUmVjdGFuZ2xlIiBmaWxsPSIjMDAwMDAwIiBvcGFjaXR5PSIwIiB4PSIwIiB5PSIwIiB3aWR0aD0iMzIiIGhlaWdodD0iMzIiPjwvcmVjdD4KICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0yOSw1IEwzLDUgQzIuNDQ2ODc1LDUgMiw1LjQ0Njg3NSAyLDYgTDIsMjYgQzIsMjYuNTUzMTI1IDIuNDQ2ODc1LDI3IDMsMjcgTDI5LDI3IEMyOS41NTMxMjUsMjcgMzAsMjYuNTUzMTI1IDMwLDI2IEwzMCw2IEMzMCw1LjQ0Njg3NSAyOS41NTMxMjUsNSAyOSw1IFogTTEwLjU2MjUsOS41IEMxMS42NjU2MjUsOS41IDEyLjU2MjUsMTAuMzk2ODc1IDEyLjU2MjUsMTEuNSBDMTIuNTYyNSwxMi42MDMxMjUgMTEuNjY1NjI1LDEzLjUgMTAuNTYyNSwxMy41IEM5LjQ1OTM3NSwxMy41IDguNTYyNSwxMi42MDMxMjUgOC41NjI1LDExLjUgQzguNTYyNSwxMC4zOTY4NzUgOS40NTkzNzUsOS41IDEwLjU2MjUsOS41IFogTTI2LjYyMTg3NSwyMy4xNTkzNzUgQzI2LjU3ODEyNSwyMy4xOTY4NzUgMjYuNTE4NzUsMjMuMjE4NzUgMjYuNDU5Mzc1LDIzLjIxODc1IEw1LjUzNzUsMjMuMjE4NzUgQzUuNCwyMy4yMTg3NSA1LjI4NzUsMjMuMTA2MjUgNS4yODc1LDIyLjk2ODc1IEM1LjI4NzUsMjIuOTA5Mzc1IDUuMzA5Mzc1LDIyLjg1MzEyNSA1LjM0Njg3NSwyMi44MDYyNSBMMTAuNjY4NzUsMTYuNDkzNzUgQzEwLjc1NjI1LDE2LjM4NzUgMTAuOTE1NjI1LDE2LjM3NSAxMS4wMjE4NzUsMTYuNDYyNSBDMTEuMDMxMjUsMTYuNDcxODc1IDExLjA0Mzc1LDE2LjQ4MTI1IDExLjA1MzEyNSwxNi40OTM3NSBMMTQuMTU5Mzc1LDIwLjE4MTI1IEwxOS4xLDE0LjMyMTg3NSBDMTkuMTg3NSwxNC4yMTU2MjUgMTkuMzQ2ODc1LDE0LjIwMzEyNSAxOS40NTMxMjUsMTQuMjkwNjI1IEMxOS40NjI1LDE0LjMgMTkuNDc1LDE0LjMwOTM3NSAxOS40ODQzNzUsMTQuMzIxODc1IEwyNi42NTkzNzUsMjIuODA5Mzc1IEMyNi43NDA2MjUsMjIuOTEyNSAyNi43MjgxMjUsMjMuMDcxODc1IDI2LjYyMTg3NSwyMy4xNTkzNzUgWiIgaWQ9IlNoYXBlIiBmaWxsPSIjRThFOEU4Ij48L3BhdGg+CiAgICAgICAgICAgIDwvZz4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPg==);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-placeholder {
|
||||||
|
.box;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-preview {
|
||||||
|
text-align: center;
|
||||||
|
height: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
&-body {
|
||||||
|
.box;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.zoom-enter,
|
||||||
|
&.zoom-appear {
|
||||||
|
transform: none;
|
||||||
|
opacity: 0;
|
||||||
|
animation-duration: 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-mask {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: @zindex-preview-mask;
|
||||||
|
height: 100%;
|
||||||
|
background-color: @preview-mask-bg;
|
||||||
|
filter: ~'alpha(opacity=50)';
|
||||||
|
|
||||||
|
&-hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-img {
|
||||||
|
cursor: grab;
|
||||||
|
transform: scale3d(1, 1, 1);
|
||||||
|
transition: transform 0.3s cubic-bezier(0, 0, 0.25, 1) 0s;
|
||||||
|
user-select: none;
|
||||||
|
vertical-align: middle;
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
pointer-events: auto;
|
||||||
|
&-wrapper {
|
||||||
|
.box;
|
||||||
|
transition: transform 0.3s cubic-bezier(0, 0, 0.25, 1) 0s;
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
height: 50%;
|
||||||
|
width: 1px;
|
||||||
|
margin-right: -1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-moving {
|
||||||
|
.@{prefixCls}-preview-img {
|
||||||
|
cursor: grabbing;
|
||||||
|
&-wrapper {
|
||||||
|
transition-duration: 0s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-wrap {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: @zindex-preview-mask;
|
||||||
|
overflow: auto;
|
||||||
|
outline: 0;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-operations {
|
||||||
|
.reset;
|
||||||
|
pointer-events: auto;
|
||||||
|
list-style: none;
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 100%;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
z-index: 1;
|
||||||
|
color: @text-color;
|
||||||
|
background: fade(@preview-mask-bg, 45%);
|
||||||
|
|
||||||
|
&-operation {
|
||||||
|
padding: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-left: 10px;
|
||||||
|
&-disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
color: @text-color-disabled;
|
||||||
|
}
|
||||||
|
&:last-of-type {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-icon {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-switch-left {
|
||||||
|
position: absolute;
|
||||||
|
left: 10px;
|
||||||
|
top: 50%;
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
margin-top: -22px;
|
||||||
|
background: fade(@text-color, 45%);
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
color: @text-color;
|
||||||
|
&-disabled {
|
||||||
|
background: fade(@text-color, 30%);
|
||||||
|
color: @text-color-disabled;
|
||||||
|
cursor: not-allowed;
|
||||||
|
> .anticon {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> .anticon {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-switch-right {
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
top: 50%;
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
margin-top: -22px;
|
||||||
|
background: fade(@text-color, 45%);
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
color: @text-color;
|
||||||
|
&-disabled {
|
||||||
|
background: fade(@text-color, 20%);
|
||||||
|
color: @text-color-disabled;
|
||||||
|
cursor: not-allowed;
|
||||||
|
> .anticon {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> .anticon {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-enter,
|
||||||
|
.fade-appear {
|
||||||
|
animation-duration: 0.2s;
|
||||||
|
animation-fill-mode: both;
|
||||||
|
animation-play-state: paused;
|
||||||
|
opacity: 0;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
}
|
||||||
|
.fade-leave {
|
||||||
|
animation-duration: 0.2s;
|
||||||
|
animation-fill-mode: both;
|
||||||
|
animation-play-state: paused;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
}
|
||||||
|
.fade-enter.fade-enter-active,
|
||||||
|
.fade-appear.fade-appear-active {
|
||||||
|
animation-name: rcImageFadeIn;
|
||||||
|
animation-play-state: running;
|
||||||
|
}
|
||||||
|
.fade-leave.fade-leave-active {
|
||||||
|
animation-name: rcImageFadeOut;
|
||||||
|
animation-play-state: running;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rcImageFadeIn {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rcImageFadeOut {
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.zoom-enter,
|
||||||
|
.zoom-appear {
|
||||||
|
-webkit-animation-duration: 0.2s;
|
||||||
|
animation-duration: 0.2s;
|
||||||
|
-webkit-animation-fill-mode: both;
|
||||||
|
animation-fill-mode: both;
|
||||||
|
-webkit-animation-play-state: paused;
|
||||||
|
animation-play-state: paused;
|
||||||
|
-webkit-transform: scale(0);
|
||||||
|
transform: scale(0);
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1);
|
||||||
|
animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1);
|
||||||
|
}
|
||||||
|
.zoom-leave {
|
||||||
|
-webkit-animation-duration: 0.2s;
|
||||||
|
animation-duration: 0.2s;
|
||||||
|
-webkit-animation-fill-mode: both;
|
||||||
|
animation-fill-mode: both;
|
||||||
|
-webkit-animation-play-state: paused;
|
||||||
|
animation-play-state: paused;
|
||||||
|
-webkit-animation-timing-function: cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
||||||
|
animation-timing-function: cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
||||||
|
}
|
||||||
|
.zoom-enter.zoom-enter-active,
|
||||||
|
.zoom-appear.zoom-appear-active {
|
||||||
|
-webkit-animation-name: rcImageZoomIn;
|
||||||
|
animation-name: rcImageZoomIn;
|
||||||
|
-webkit-animation-play-state: running;
|
||||||
|
animation-play-state: running;
|
||||||
|
}
|
||||||
|
.zoom-leave.zoom-leave-active {
|
||||||
|
-webkit-animation-name: rcImageZoomOut;
|
||||||
|
animation-name: rcImageZoomOut;
|
||||||
|
-webkit-animation-play-state: running;
|
||||||
|
animation-play-state: running;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rcImageZoomIn {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: scale(0.2);
|
||||||
|
transform: scale(0.2);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes rcImageZoomOut {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-transform: scale(0.2);
|
||||||
|
transform: scale(0.2);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
4
components/vc-image/index.ts
Normal file
4
components/vc-image/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
// based on rc-image 4.3.2
|
||||||
|
import Image from './src/Image';
|
||||||
|
export * from './src/Image';
|
||||||
|
export default Image;
|
291
components/vc-image/src/Image.tsx
Normal file
291
components/vc-image/src/Image.tsx
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
import {
|
||||||
|
ImgHTMLAttributes,
|
||||||
|
CSSProperties,
|
||||||
|
ref,
|
||||||
|
watch,
|
||||||
|
defineComponent,
|
||||||
|
computed,
|
||||||
|
onMounted,
|
||||||
|
} from 'vue';
|
||||||
|
import { isNumber } from 'lodash-es';
|
||||||
|
|
||||||
|
import BaseMixin from '../../_util/BaseMixin';
|
||||||
|
import cn from '../../_util/classNames';
|
||||||
|
import PropTypes from '../../_util/vue-types';
|
||||||
|
import { getOffset } from '../../vc-util/Dom/css';
|
||||||
|
|
||||||
|
import Preview, { MouseEventHandler } from './Preview';
|
||||||
|
|
||||||
|
import PreviewGroup, { context } from './PreviewGroup';
|
||||||
|
|
||||||
|
export type GetContainer = string | HTMLElement | (() => HTMLElement);
|
||||||
|
export interface ImagePreviewType {
|
||||||
|
visible?: boolean;
|
||||||
|
onVisibleChange?: (value: boolean, prevValue: boolean) => void;
|
||||||
|
getContainer?: GetContainer | false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ImagePropsType extends Omit<ImgHTMLAttributes, 'placeholder' | 'onClick'> {
|
||||||
|
// Original
|
||||||
|
src?: string;
|
||||||
|
wrapperClassName?: string;
|
||||||
|
wrapperStyle?: CSSProperties;
|
||||||
|
prefixCls?: string;
|
||||||
|
previewPrefixCls?: string;
|
||||||
|
placeholder?: boolean;
|
||||||
|
fallback?: string;
|
||||||
|
preview?: boolean | ImagePreviewType;
|
||||||
|
}
|
||||||
|
export const ImageProps = {
|
||||||
|
src: PropTypes.string,
|
||||||
|
wrapperClassName: PropTypes.string,
|
||||||
|
wrapperStyle: PropTypes.style,
|
||||||
|
prefixCls: PropTypes.string,
|
||||||
|
previewPrefixCls: PropTypes.string,
|
||||||
|
placeholder: PropTypes.VNodeChild,
|
||||||
|
fallback: PropTypes.string,
|
||||||
|
preview: PropTypes.oneOfType([PropTypes.looseBool, PropTypes.shape<ImagePreviewType>({})]).def(
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
type ImageStatus = 'normal' | 'error' | 'loading';
|
||||||
|
|
||||||
|
const mergeDefaultValue = <T extends object>(obj: T, defaultValues: object): T => {
|
||||||
|
const res = { ...obj };
|
||||||
|
Object.keys(defaultValues).forEach(key => {
|
||||||
|
if (obj[key] === undefined) {
|
||||||
|
res[key] = defaultValues[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
let uuid = 0;
|
||||||
|
const ImageInternal = defineComponent({
|
||||||
|
name: 'Image',
|
||||||
|
mixins: [BaseMixin],
|
||||||
|
inheritAttrs: false,
|
||||||
|
props: ImageProps,
|
||||||
|
emits: ['click'],
|
||||||
|
setup(props, { attrs, slots, emit }) {
|
||||||
|
const prefixCls = computed(() => props.prefixCls);
|
||||||
|
const previewPrefixCls = computed(() => `${prefixCls.value}-preview`);
|
||||||
|
const preview = computed(() => {
|
||||||
|
const defaultValues = {
|
||||||
|
visible: undefined,
|
||||||
|
onVisibleChange: () => {},
|
||||||
|
getContainer: undefined,
|
||||||
|
};
|
||||||
|
return typeof props.preview === 'object'
|
||||||
|
? mergeDefaultValue(props.preview, defaultValues)
|
||||||
|
: defaultValues;
|
||||||
|
});
|
||||||
|
const isCustomPlaceholder = computed(
|
||||||
|
() => (props.placeholder && props.placeholder !== true) || slots.placeholder,
|
||||||
|
);
|
||||||
|
const previewVisible = computed(() => preview.value.visible);
|
||||||
|
const onPreviewVisibleChange = computed(() => preview.value.onVisibleChange);
|
||||||
|
const getPreviewContainer = computed(() => preview.value.getContainer);
|
||||||
|
|
||||||
|
const isControlled = computed(() => previewVisible.value !== undefined);
|
||||||
|
const isShowPreview = ref(!!previewVisible.value);
|
||||||
|
watch(previewVisible, () => {
|
||||||
|
isShowPreview.value = !!previewVisible.value;
|
||||||
|
});
|
||||||
|
watch(isShowPreview, (val, preVal) => {
|
||||||
|
onPreviewVisibleChange.value(val, preVal);
|
||||||
|
});
|
||||||
|
const status = ref<ImageStatus>(isCustomPlaceholder.value ? 'loading' : 'normal');
|
||||||
|
watch(
|
||||||
|
() => props.src,
|
||||||
|
() => {
|
||||||
|
status.value = isCustomPlaceholder.value ? 'loading' : 'normal';
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const mousePosition = ref<null | { x: number; y: number }>(null);
|
||||||
|
const isError = computed(() => status.value === 'error');
|
||||||
|
const groupContext = context.inject();
|
||||||
|
const {
|
||||||
|
isPreviewGroup,
|
||||||
|
setCurrent,
|
||||||
|
setShowPreview: setGroupShowPreview,
|
||||||
|
setMousePosition: setGroupMousePosition,
|
||||||
|
registerImage,
|
||||||
|
} = groupContext;
|
||||||
|
const currentId = ref(uuid++);
|
||||||
|
const canPreview = computed(() => props.preview && !isError.value);
|
||||||
|
const onLoad = () => {
|
||||||
|
status.value = 'normal';
|
||||||
|
};
|
||||||
|
const onError = () => {
|
||||||
|
status.value = 'error';
|
||||||
|
};
|
||||||
|
|
||||||
|
const onPreview: MouseEventHandler = e => {
|
||||||
|
if (!isControlled.value) {
|
||||||
|
const { left, top } = getOffset(e.target);
|
||||||
|
if (isPreviewGroup.value) {
|
||||||
|
setCurrent(currentId.value);
|
||||||
|
setGroupMousePosition({
|
||||||
|
x: left,
|
||||||
|
y: top,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
mousePosition.value = {
|
||||||
|
x: left,
|
||||||
|
y: top,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isPreviewGroup.value) {
|
||||||
|
setGroupShowPreview(true);
|
||||||
|
} else {
|
||||||
|
isShowPreview.value = true;
|
||||||
|
}
|
||||||
|
emit('click', e);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onPreviewClose = () => {
|
||||||
|
isShowPreview.value = false;
|
||||||
|
if (!isControlled.value) {
|
||||||
|
mousePosition.value = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const img = ref<HTMLImageElement>(null);
|
||||||
|
watch(
|
||||||
|
() => img,
|
||||||
|
() => {
|
||||||
|
if (status.value !== 'loading') return;
|
||||||
|
if (img.value.complete && (img.value.naturalWidth || img.value.naturalHeight)) {
|
||||||
|
onLoad();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let unRegister = () => {};
|
||||||
|
onMounted(() => {
|
||||||
|
watch(
|
||||||
|
[() => props.src, canPreview],
|
||||||
|
() => {
|
||||||
|
unRegister();
|
||||||
|
if (!isPreviewGroup.value) {
|
||||||
|
return () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
unRegister = registerImage(currentId.value, props.src);
|
||||||
|
|
||||||
|
if (!canPreview.value) {
|
||||||
|
unRegister();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ flush: 'post', immediate: true },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
const toSizePx = (l: number | string) => {
|
||||||
|
if (isNumber(l)) return l + 'px';
|
||||||
|
return l;
|
||||||
|
};
|
||||||
|
return () => {
|
||||||
|
const {
|
||||||
|
prefixCls,
|
||||||
|
wrapperClassName,
|
||||||
|
fallback,
|
||||||
|
src,
|
||||||
|
preview,
|
||||||
|
placeholder,
|
||||||
|
wrapperStyle,
|
||||||
|
} = props;
|
||||||
|
const {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
crossorigin,
|
||||||
|
decoding,
|
||||||
|
alt,
|
||||||
|
sizes,
|
||||||
|
srcset,
|
||||||
|
usemap,
|
||||||
|
class: cls,
|
||||||
|
style,
|
||||||
|
} = attrs as ImgHTMLAttributes;
|
||||||
|
const wrappperClass = cn(prefixCls, wrapperClassName, {
|
||||||
|
[`${prefixCls}-error`]: isError.value,
|
||||||
|
});
|
||||||
|
const mergedSrc = isError.value && fallback ? fallback : src;
|
||||||
|
const previewMask = slots.previewMask && slots.previewMask();
|
||||||
|
const imgCommonProps = {
|
||||||
|
crossorigin,
|
||||||
|
decoding,
|
||||||
|
alt,
|
||||||
|
sizes,
|
||||||
|
srcset,
|
||||||
|
usemap,
|
||||||
|
class: cn(
|
||||||
|
`${prefixCls}-img`,
|
||||||
|
{
|
||||||
|
[`${prefixCls}-img-placeholder`]: placeholder === true,
|
||||||
|
},
|
||||||
|
cls,
|
||||||
|
),
|
||||||
|
style: {
|
||||||
|
height,
|
||||||
|
...(style as CSSProperties),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
class={wrappperClass}
|
||||||
|
onClick={
|
||||||
|
preview && !isError.value
|
||||||
|
? onPreview
|
||||||
|
: e => {
|
||||||
|
emit('click', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
style={{
|
||||||
|
width: toSizePx(width),
|
||||||
|
height: toSizePx(height),
|
||||||
|
...wrapperStyle,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
{...imgCommonProps}
|
||||||
|
{...(isError.value && fallback
|
||||||
|
? {
|
||||||
|
src: fallback,
|
||||||
|
}
|
||||||
|
: { onLoad, onError, src })}
|
||||||
|
ref={img}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{status.value === 'loading' && (
|
||||||
|
<div aria-hidden="true" class={`${prefixCls}-placeholder`}>
|
||||||
|
{placeholder || (slots.placeholder && slots.placeholder())}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* Preview Click Mask */}
|
||||||
|
{previewMask && canPreview.value && (
|
||||||
|
<div class={`${prefixCls}-mask`}>{previewMask}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{!isPreviewGroup.value && canPreview.value && (
|
||||||
|
<Preview
|
||||||
|
aria-hidden={!isShowPreview.value}
|
||||||
|
visible={isShowPreview.value}
|
||||||
|
prefixCls={previewPrefixCls.value}
|
||||||
|
onClose={onPreviewClose}
|
||||||
|
mousePosition={mousePosition.value}
|
||||||
|
src={mergedSrc}
|
||||||
|
alt={alt}
|
||||||
|
getContainer={getPreviewContainer}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
ImageInternal.PreviewGroup = PreviewGroup;
|
||||||
|
|
||||||
|
export default ImageInternal as typeof ImageInternal & {
|
||||||
|
readonly PreviewGroup: typeof PreviewGroup;
|
||||||
|
};
|
314
components/vc-image/src/Preview.tsx
Normal file
314
components/vc-image/src/Preview.tsx
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
import { computed, defineComponent, onMounted, onUnmounted, reactive, ref, watch } from 'vue';
|
||||||
|
import {
|
||||||
|
RotateLeftOutlined,
|
||||||
|
RotateRightOutlined,
|
||||||
|
ZoomInOutlined,
|
||||||
|
ZoomOutOutlined,
|
||||||
|
CloseOutlined,
|
||||||
|
LeftOutlined,
|
||||||
|
RightOutlined,
|
||||||
|
} from '@ant-design/icons-vue';
|
||||||
|
|
||||||
|
import classnames from '../../_util/classNames';
|
||||||
|
import PropTypes from '../../_util/vue-types';
|
||||||
|
import Dialog from '../../vc-dialog';
|
||||||
|
import getIDialogPropTypes from '../../vc-dialog/IDialogPropTypes';
|
||||||
|
import { getOffset } from '../../vc-util/Dom/css';
|
||||||
|
import addEventListener from '../../vc-util/Dom/addEventListener';
|
||||||
|
import { warning } from '../../vc-util/warning';
|
||||||
|
import useFrameSetState from './hooks/useFrameSetState';
|
||||||
|
import getFixScaleEleTransPosition from './getFixScaleEleTransPosition';
|
||||||
|
|
||||||
|
import { context } from './PreviewGroup';
|
||||||
|
|
||||||
|
const IDialogPropTypes = getIDialogPropTypes();
|
||||||
|
export type MouseEventHandler = (payload: MouseEvent) => void;
|
||||||
|
|
||||||
|
export interface PreviewProps extends Omit<typeof IDialogPropTypes, 'onClose'> {
|
||||||
|
onClose?: (e: Element) => void;
|
||||||
|
src?: string;
|
||||||
|
alt?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialPosition = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
};
|
||||||
|
const PreviewType = {
|
||||||
|
src: PropTypes.string,
|
||||||
|
alt: PropTypes.string,
|
||||||
|
...IDialogPropTypes,
|
||||||
|
};
|
||||||
|
const Preview = defineComponent({
|
||||||
|
name: 'Preview',
|
||||||
|
inheritAttrs: false,
|
||||||
|
props: PreviewType,
|
||||||
|
emits: ['close', 'afterClose'],
|
||||||
|
setup(props, { emit, attrs }) {
|
||||||
|
const scale = ref(1);
|
||||||
|
const rotate = ref(0);
|
||||||
|
const [position, setPosition] = useFrameSetState<{
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}>(initialPosition);
|
||||||
|
|
||||||
|
const onClose = () => emit('close');
|
||||||
|
const imgRef = ref<HTMLImageElement>();
|
||||||
|
const originPositionRef = reactive<{
|
||||||
|
originX: number;
|
||||||
|
originY: number;
|
||||||
|
deltaX: number;
|
||||||
|
deltaY: number;
|
||||||
|
}>({
|
||||||
|
originX: 0,
|
||||||
|
originY: 0,
|
||||||
|
deltaX: 0,
|
||||||
|
deltaY: 0,
|
||||||
|
});
|
||||||
|
const isMoving = ref(false);
|
||||||
|
const groupContext = context.inject();
|
||||||
|
const { previewUrls, current, isPreviewGroup, setCurrent } = groupContext;
|
||||||
|
const previewGroupCount = computed(() => Object.keys(previewUrls).length);
|
||||||
|
const previewUrlsKeys = computed(() => Object.keys(previewUrls));
|
||||||
|
const currentPreviewIndex = computed(() =>
|
||||||
|
previewUrlsKeys.value.indexOf(String(current.value)),
|
||||||
|
);
|
||||||
|
const combinationSrc = computed(() =>
|
||||||
|
isPreviewGroup.value ? previewUrls[current.value] : props.src,
|
||||||
|
);
|
||||||
|
const showLeftOrRightSwitches = computed(
|
||||||
|
() => isPreviewGroup.value && previewGroupCount.value > 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
const onAfterClose = () => {
|
||||||
|
scale.value = 1;
|
||||||
|
rotate.value = 0;
|
||||||
|
setPosition(initialPosition);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onZoomIn = () => {
|
||||||
|
scale.value++;
|
||||||
|
setPosition(initialPosition);
|
||||||
|
};
|
||||||
|
const onZoomOut = () => {
|
||||||
|
if (scale.value > 1) {
|
||||||
|
scale.value--;
|
||||||
|
}
|
||||||
|
setPosition(initialPosition);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRotateRight = () => {
|
||||||
|
rotate.value += 90;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRotateLeft = () => {
|
||||||
|
rotate.value -= 90;
|
||||||
|
};
|
||||||
|
const onSwitchLeft: MouseEventHandler = event => {
|
||||||
|
event.preventDefault();
|
||||||
|
// Without this mask close will abnormal
|
||||||
|
event.stopPropagation();
|
||||||
|
if (currentPreviewIndex.value > 0) {
|
||||||
|
setCurrent(previewUrlsKeys.value[String(currentPreviewIndex.value - 1)]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSwitchRight: MouseEventHandler = event => {
|
||||||
|
event.preventDefault();
|
||||||
|
// Without this mask close will abnormal
|
||||||
|
event.stopPropagation();
|
||||||
|
if (currentPreviewIndex.value < previewGroupCount.value - 1) {
|
||||||
|
setCurrent(previewUrlsKeys.value[String(currentPreviewIndex.value + 1)]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapClassName = classnames({
|
||||||
|
[`${props.prefixCls}-moving`]: isMoving.value,
|
||||||
|
});
|
||||||
|
const toolClassName = `${props.prefixCls}-operations-operation`;
|
||||||
|
const iconClassName = `${props.prefixCls}-operations-icon`;
|
||||||
|
const tools = [
|
||||||
|
{
|
||||||
|
icon: CloseOutlined,
|
||||||
|
onClick: onClose,
|
||||||
|
type: 'close',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: ZoomInOutlined,
|
||||||
|
onClick: onZoomIn,
|
||||||
|
type: 'zoomIn',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: ZoomOutOutlined,
|
||||||
|
onClick: onZoomOut,
|
||||||
|
type: 'zoomOut',
|
||||||
|
disabled: computed(() => scale.value === 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: RotateRightOutlined,
|
||||||
|
onClick: onRotateRight,
|
||||||
|
type: 'rotateRight',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: RotateLeftOutlined,
|
||||||
|
onClick: onRotateLeft,
|
||||||
|
type: 'rotateLeft',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const onMouseUp: MouseEventHandler = () => {
|
||||||
|
if (props.visible && isMoving.value) {
|
||||||
|
const width = imgRef.value.offsetWidth * scale.value;
|
||||||
|
const height = imgRef.value.offsetHeight * scale.value;
|
||||||
|
const { left, top } = getOffset(imgRef.value);
|
||||||
|
const isRotate = rotate.value % 180 !== 0;
|
||||||
|
|
||||||
|
isMoving.value = false;
|
||||||
|
|
||||||
|
const fixState = getFixScaleEleTransPosition(
|
||||||
|
isRotate ? height : width,
|
||||||
|
isRotate ? width : height,
|
||||||
|
left,
|
||||||
|
top,
|
||||||
|
);
|
||||||
|
if (fixState) {
|
||||||
|
setPosition({ ...fixState });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseDown: MouseEventHandler = event => {
|
||||||
|
event.preventDefault();
|
||||||
|
// Without this mask close will abnormal
|
||||||
|
event.stopPropagation();
|
||||||
|
originPositionRef.deltaX = event.pageX - position.x;
|
||||||
|
originPositionRef.deltaY = event.pageY - position.y;
|
||||||
|
originPositionRef.originX = position.x;
|
||||||
|
originPositionRef.originY = position.y;
|
||||||
|
isMoving.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseMove: MouseEventHandler = event => {
|
||||||
|
if (props.visible && isMoving.value) {
|
||||||
|
setPosition({
|
||||||
|
x: event.pageX - originPositionRef.deltaX,
|
||||||
|
y: event.pageY - originPositionRef.deltaY,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let removeListeners = () => {};
|
||||||
|
onMounted(() => {
|
||||||
|
watch(
|
||||||
|
[() => props.visible, isMoving],
|
||||||
|
() => {
|
||||||
|
removeListeners();
|
||||||
|
let onTopMouseUpListener: { remove: any };
|
||||||
|
let onTopMouseMoveListener: { remove: any };
|
||||||
|
|
||||||
|
const onMouseUpListener = addEventListener(window, 'mouseup', onMouseUp, false);
|
||||||
|
const onMouseMoveListener = addEventListener(window, 'mousemove', onMouseMove, false);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Resolve if in iframe lost event
|
||||||
|
/* istanbul ignore next */
|
||||||
|
if (window.top !== window.self) {
|
||||||
|
onTopMouseUpListener = addEventListener(window.top, 'mouseup', onMouseUp, false);
|
||||||
|
onTopMouseMoveListener = addEventListener(
|
||||||
|
window.top,
|
||||||
|
'mousemove',
|
||||||
|
onMouseMove,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
/* istanbul ignore next */
|
||||||
|
warning(false, `[vc-image] ${error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeListeners = () => {
|
||||||
|
onMouseUpListener.remove();
|
||||||
|
onMouseMoveListener.remove();
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
|
if (onTopMouseUpListener) onTopMouseUpListener.remove();
|
||||||
|
/* istanbul ignore next */
|
||||||
|
if (onTopMouseMoveListener) onTopMouseMoveListener.remove();
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ flush: 'post', immediate: true },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
onUnmounted(() => {
|
||||||
|
removeListeners();
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => (
|
||||||
|
<Dialog
|
||||||
|
{...attrs}
|
||||||
|
transitionName="zoom"
|
||||||
|
maskTransitionName="fade"
|
||||||
|
closable={false}
|
||||||
|
keyboard
|
||||||
|
prefixCls={props.prefixCls}
|
||||||
|
onClose={onClose}
|
||||||
|
afterClose={onAfterClose}
|
||||||
|
visible={props.visible}
|
||||||
|
wrapClassName={wrapClassName}
|
||||||
|
>
|
||||||
|
<ul class={`${props.prefixCls}-operations`}>
|
||||||
|
{tools.map(({ icon: IconType, onClick, type, disabled }) => (
|
||||||
|
<li
|
||||||
|
class={classnames(toolClassName, {
|
||||||
|
[`${props.prefixCls}-operations-operation-disabled`]: disabled && disabled?.value,
|
||||||
|
})}
|
||||||
|
onClick={onClick}
|
||||||
|
key={type}
|
||||||
|
>
|
||||||
|
<IconType class={iconClassName} />
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
<div
|
||||||
|
class={`${props.prefixCls}-img-wrapper`}
|
||||||
|
style={{
|
||||||
|
transform: `translate3d(${position.x}px, ${position.y}px, 0)`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
onMousedown={onMouseDown}
|
||||||
|
ref={imgRef}
|
||||||
|
class={`${props.prefixCls}-img`}
|
||||||
|
src={combinationSrc.value}
|
||||||
|
alt={props.alt}
|
||||||
|
style={{
|
||||||
|
transform: `scale3d(${scale.value}, ${scale.value}, 1) rotate(${rotate.value}deg)`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{showLeftOrRightSwitches.value && (
|
||||||
|
<div
|
||||||
|
class={classnames(`${props.prefixCls}-switch-left`, {
|
||||||
|
[`${props.prefixCls}-switch-left-disabled`]: currentPreviewIndex.value <= 0,
|
||||||
|
})}
|
||||||
|
onClick={onSwitchLeft}
|
||||||
|
>
|
||||||
|
<LeftOutlined />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{showLeftOrRightSwitches.value && (
|
||||||
|
<div
|
||||||
|
class={classnames(`${props.prefixCls}-switch-right`, {
|
||||||
|
[`${props.prefixCls}-switch-right-disabled`]:
|
||||||
|
currentPreviewIndex.value >= previewGroupCount.value - 1,
|
||||||
|
})}
|
||||||
|
onClick={onSwitchRight}
|
||||||
|
>
|
||||||
|
<RightOutlined />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Preview;
|
97
components/vc-image/src/PreviewGroup.tsx
Normal file
97
components/vc-image/src/PreviewGroup.tsx
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import { ref, provide, defineComponent, inject, Ref, reactive } from 'vue';
|
||||||
|
import Preview from './Preview';
|
||||||
|
|
||||||
|
export interface GroupConsumerProps {
|
||||||
|
previewPrefixCls?: string;
|
||||||
|
}
|
||||||
|
export interface GroupConsumerValue extends GroupConsumerProps {
|
||||||
|
isPreviewGroup?: Ref<boolean | undefined>;
|
||||||
|
previewUrls: Record<number, string>;
|
||||||
|
setPreviewUrls: (previewUrls: Record<number, string>) => void;
|
||||||
|
current: Ref<number>;
|
||||||
|
setCurrent: (current: number) => void;
|
||||||
|
setShowPreview: (isShowPreview: boolean) => void;
|
||||||
|
setMousePosition: (mousePosition: null | { x: number; y: number }) => void;
|
||||||
|
registerImage: (id: number, url: string) => () => void;
|
||||||
|
}
|
||||||
|
const previewGroupContext = Symbol('previewGroupContext');
|
||||||
|
export const context = {
|
||||||
|
provide: (val: GroupConsumerValue) => {
|
||||||
|
provide(previewGroupContext, val);
|
||||||
|
},
|
||||||
|
inject: () => {
|
||||||
|
return inject<GroupConsumerValue>(previewGroupContext, {
|
||||||
|
isPreviewGroup: ref(false),
|
||||||
|
previewUrls: reactive({}),
|
||||||
|
setPreviewUrls: () => {},
|
||||||
|
current: ref(null),
|
||||||
|
setCurrent: () => {},
|
||||||
|
setShowPreview: () => {},
|
||||||
|
setMousePosition: () => {},
|
||||||
|
registerImage: null,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const Group = defineComponent({
|
||||||
|
name: 'PreviewGroup',
|
||||||
|
inheritAttrs: false,
|
||||||
|
props: { previewPrefixCls: String },
|
||||||
|
setup(props, { slots }) {
|
||||||
|
const previewUrls = reactive<Record<number, string>>({});
|
||||||
|
const current = ref<number>();
|
||||||
|
const isShowPreview = ref<boolean>(false);
|
||||||
|
const mousePosition = ref<{ x: number; y: number }>(null);
|
||||||
|
const setPreviewUrls = (val: Record<number, string>) => {
|
||||||
|
Object.assign(previewUrls, val);
|
||||||
|
};
|
||||||
|
const setCurrent = (val: number) => {
|
||||||
|
current.value = val;
|
||||||
|
};
|
||||||
|
const setMousePosition = (val: null | { x: number; y: number }) => {
|
||||||
|
mousePosition.value = val;
|
||||||
|
};
|
||||||
|
const setShowPreview = (val: boolean) => {
|
||||||
|
isShowPreview.value = val;
|
||||||
|
};
|
||||||
|
const registerImage = (id: number, url: string) => {
|
||||||
|
previewUrls[id] = url;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
delete previewUrls[id];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const onPreviewClose = (e: any) => {
|
||||||
|
e?.stopPropagation();
|
||||||
|
isShowPreview.value = false;
|
||||||
|
mousePosition.value = null;
|
||||||
|
};
|
||||||
|
context.provide({
|
||||||
|
isPreviewGroup: ref(true),
|
||||||
|
previewUrls,
|
||||||
|
setPreviewUrls,
|
||||||
|
current,
|
||||||
|
setCurrent,
|
||||||
|
setShowPreview,
|
||||||
|
setMousePosition,
|
||||||
|
registerImage,
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{slots.default && slots.default()}
|
||||||
|
<Preview
|
||||||
|
ria-hidden={!isShowPreview.value}
|
||||||
|
visible={isShowPreview.value}
|
||||||
|
prefixCls={props.previewPrefixCls}
|
||||||
|
onClose={onPreviewClose}
|
||||||
|
mousePosition={mousePosition.value}
|
||||||
|
src={previewUrls[current.value]}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Group;
|
61
components/vc-image/src/getFixScaleEleTransPosition.ts
Normal file
61
components/vc-image/src/getFixScaleEleTransPosition.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import { getClientSize } from '../../vc-util/Dom/css';
|
||||||
|
|
||||||
|
function fixPoint(key: 'x' | 'y', start: number, width: number, clientWidth: number) {
|
||||||
|
const startAddWidth = start + width;
|
||||||
|
const offsetStart = (width - clientWidth) / 2;
|
||||||
|
|
||||||
|
if (width > clientWidth) {
|
||||||
|
if (start > 0) {
|
||||||
|
return {
|
||||||
|
[key]: offsetStart,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (start < 0 && startAddWidth < clientWidth) {
|
||||||
|
return {
|
||||||
|
[key]: -offsetStart,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else if (start < 0 || startAddWidth > clientWidth) {
|
||||||
|
return {
|
||||||
|
[key]: start < 0 ? offsetStart : -offsetStart,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fix positon x,y point when
|
||||||
|
*
|
||||||
|
* Ele width && height < client
|
||||||
|
* - Back origin
|
||||||
|
*
|
||||||
|
* - Ele width | height > clientWidth | clientHeight
|
||||||
|
* - left | top > 0 -> Back 0
|
||||||
|
* - left | top + width | height < clientWidth | clientHeight -> Back left | top + width | height === clientWidth | clientHeight
|
||||||
|
*
|
||||||
|
* Regardless of other
|
||||||
|
*/
|
||||||
|
export default function getFixScaleEleTransPosition(
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
left: number,
|
||||||
|
top: number,
|
||||||
|
): null | { x: number; y: number } {
|
||||||
|
const { width: clientWidth, height: clientHeight } = getClientSize();
|
||||||
|
|
||||||
|
let fixPos = null;
|
||||||
|
|
||||||
|
if (width <= clientWidth && height <= clientHeight) {
|
||||||
|
fixPos = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
};
|
||||||
|
} else if (width > clientWidth || height > clientHeight) {
|
||||||
|
fixPos = {
|
||||||
|
...fixPoint('x', left, width, clientWidth),
|
||||||
|
...fixPoint('y', top, height, clientHeight),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return fixPos;
|
||||||
|
}
|
31
components/vc-image/src/hooks/useFrameSetState.ts
Normal file
31
components/vc-image/src/hooks/useFrameSetState.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import raf from '../../../_util/raf';
|
||||||
|
import { onMounted, reactive, ref } from 'vue';
|
||||||
|
|
||||||
|
type SetActionType<T> = Partial<T> | ((state: T) => Partial<T>);
|
||||||
|
export default function useFrameSetState<T extends object>(
|
||||||
|
initial: T,
|
||||||
|
): [Record<string, any>, (newState: SetActionType<T>) => void] {
|
||||||
|
const frame = ref(null);
|
||||||
|
const state = reactive({ ...initial });
|
||||||
|
const queue = ref<SetActionType<T>[]>([]);
|
||||||
|
|
||||||
|
const setFrameState = (newState: SetActionType<T>) => {
|
||||||
|
if (frame.value === null) {
|
||||||
|
queue.value = [];
|
||||||
|
frame.value = raf(() => {
|
||||||
|
let memoState: any;
|
||||||
|
queue.value.forEach((queueState: object) => {
|
||||||
|
memoState = { ...memoState, ...queueState };
|
||||||
|
});
|
||||||
|
Object.assign(state, memoState);
|
||||||
|
frame.value = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.value.push(newState as any);
|
||||||
|
};
|
||||||
|
onMounted(() => {
|
||||||
|
frame.value && raf.cancel(frame.value);
|
||||||
|
});
|
||||||
|
return [state, setFrameState];
|
||||||
|
}
|
115
components/vc-util/Dom/css.ts
Normal file
115
components/vc-util/Dom/css.ts
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
const PIXEL_PATTERN = /margin|padding|width|height|max|min|offset/;
|
||||||
|
|
||||||
|
const removePixel = {
|
||||||
|
left: true,
|
||||||
|
top: true,
|
||||||
|
};
|
||||||
|
const floatMap = {
|
||||||
|
cssFloat: 1,
|
||||||
|
styleFloat: 1,
|
||||||
|
float: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
function getComputedStyle(node: HTMLElement) {
|
||||||
|
return node.nodeType === 1 ? node.ownerDocument.defaultView.getComputedStyle(node, null) : {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStyleValue(node: HTMLElement, type: string, value: string) {
|
||||||
|
type = type.toLowerCase();
|
||||||
|
if (value === 'auto') {
|
||||||
|
if (type === 'height') {
|
||||||
|
return node.offsetHeight;
|
||||||
|
}
|
||||||
|
if (type === 'width') {
|
||||||
|
return node.offsetWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!(type in removePixel)) {
|
||||||
|
removePixel[type] = PIXEL_PATTERN.test(type);
|
||||||
|
}
|
||||||
|
return removePixel[type] ? parseFloat(value) || 0 : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function get(node: HTMLElement, name: any) {
|
||||||
|
const length = arguments.length;
|
||||||
|
const style = getComputedStyle(node);
|
||||||
|
|
||||||
|
name = floatMap[name] ? ('cssFloat' in node.style ? 'cssFloat' : 'styleFloat') : name;
|
||||||
|
|
||||||
|
return length === 1 ? style : getStyleValue(node, name, style[name] || node.style[name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function set(node: HTMLElement, name: any, value: string | number) {
|
||||||
|
const length = arguments.length;
|
||||||
|
name = floatMap[name] ? ('cssFloat' in node.style ? 'cssFloat' : 'styleFloat') : name;
|
||||||
|
if (length === 3) {
|
||||||
|
if (typeof value === 'number' && PIXEL_PATTERN.test(name)) {
|
||||||
|
value = `${value}px`;
|
||||||
|
}
|
||||||
|
node.style[name as string] = value; // Number
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
for (const x in name) {
|
||||||
|
if (name.hasOwnProperty(x)) {
|
||||||
|
set(node, x, name[x]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getComputedStyle(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getOuterWidth(el: HTMLElement) {
|
||||||
|
if (el === document.body) {
|
||||||
|
return document.documentElement.clientWidth;
|
||||||
|
}
|
||||||
|
return el.offsetWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getOuterHeight(el: HTMLElement) {
|
||||||
|
if (el === document.body) {
|
||||||
|
return window.innerHeight || document.documentElement.clientHeight;
|
||||||
|
}
|
||||||
|
return el.offsetHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDocSize() {
|
||||||
|
const width = Math.max(document.documentElement.scrollWidth, document.body.scrollWidth);
|
||||||
|
const height = Math.max(document.documentElement.scrollHeight, document.body.scrollHeight);
|
||||||
|
|
||||||
|
return {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getClientSize() {
|
||||||
|
const width = document.documentElement.clientWidth;
|
||||||
|
const height = window.innerHeight || document.documentElement.clientHeight;
|
||||||
|
return {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getScroll() {
|
||||||
|
return {
|
||||||
|
scrollLeft: Math.max(document.documentElement.scrollLeft, document.body.scrollLeft),
|
||||||
|
scrollTop: Math.max(document.documentElement.scrollTop, document.body.scrollTop),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getOffset(node: any) {
|
||||||
|
const box = node.getBoundingClientRect();
|
||||||
|
const docElem = document.documentElement;
|
||||||
|
|
||||||
|
// < ie8 不支持 win.pageXOffset, 则使用 docElem.scrollLeft
|
||||||
|
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),
|
||||||
|
};
|
||||||
|
}
|
@ -60,6 +60,7 @@ Array [
|
|||||||
"Drawer",
|
"Drawer",
|
||||||
"Skeleton",
|
"Skeleton",
|
||||||
"Comment",
|
"Comment",
|
||||||
|
"Image",
|
||||||
"ConfigProvider",
|
"ConfigProvider",
|
||||||
"Empty",
|
"Empty",
|
||||||
"Result",
|
"Result",
|
||||||
|
Loading…
Reference in New Issue
Block a user