refactor: resizeObserver
parent
7a2f28ac2d
commit
11ffa8dfda
|
@ -116,7 +116,7 @@ const getSlotOptions = () => {
|
|||
throw Error('使用 .type 直接取值');
|
||||
};
|
||||
const findDOMNode = instance => {
|
||||
let node = instance && (instance.$el || instance);
|
||||
let node = instance?.vnode?.el || (instance && (instance.$el || instance));
|
||||
while (node && !node.tagName) {
|
||||
node = node.nextSibling;
|
||||
}
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
// based on rc-resize-observer 0.1.3
|
||||
import { defineComponent } from 'vue';
|
||||
import BaseMixin from '../_util/BaseMixin';
|
||||
import { findDOMNode } from '../_util/props-util';
|
||||
|
||||
// Still need to be compatible with React 15, we use class component here
|
||||
const VueResizeObserver = defineComponent({
|
||||
name: 'ResizeObserver',
|
||||
mixins: [BaseMixin],
|
||||
props: {
|
||||
disabled: Boolean,
|
||||
onResize: Function,
|
||||
},
|
||||
data() {
|
||||
this.currentElement = null;
|
||||
this.resizeObserver = null;
|
||||
return {
|
||||
width: 0,
|
||||
height: 0,
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.onComponentUpdated();
|
||||
},
|
||||
|
||||
updated() {
|
||||
this.onComponentUpdated();
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.destroyObserver();
|
||||
},
|
||||
methods: {
|
||||
onComponentUpdated() {
|
||||
const { disabled } = this.$props;
|
||||
|
||||
// Unregister if disabled
|
||||
if (disabled) {
|
||||
this.destroyObserver();
|
||||
return;
|
||||
}
|
||||
|
||||
// Unregister if element changed
|
||||
const element = findDOMNode(this);
|
||||
const elementChanged = element !== this.currentElement;
|
||||
if (elementChanged) {
|
||||
this.destroyObserver();
|
||||
this.currentElement = element;
|
||||
}
|
||||
|
||||
if (!this.resizeObserver && element) {
|
||||
this.resizeObserver = new ResizeObserver(this.handleResize);
|
||||
this.resizeObserver.observe(element);
|
||||
}
|
||||
},
|
||||
|
||||
handleResize(entries) {
|
||||
const { target } = entries[0];
|
||||
const { width, height } = target.getBoundingClientRect();
|
||||
const { offsetWidth, offsetHeight } = target;
|
||||
/**
|
||||
* Resize observer trigger when content size changed.
|
||||
* In most case we just care about element size,
|
||||
* let's use `boundary` instead of `contentRect` here to avoid shaking.
|
||||
*/
|
||||
const fixedWidth = Math.floor(width);
|
||||
const fixedHeight = Math.floor(height);
|
||||
|
||||
if (this.width !== fixedWidth || this.height !== fixedHeight) {
|
||||
const size = { width: fixedWidth, height: fixedHeight, offsetWidth, offsetHeight };
|
||||
this.width = fixedWidth;
|
||||
this.height = fixedHeight;
|
||||
this.__emit('resize', size);
|
||||
}
|
||||
},
|
||||
|
||||
destroyObserver() {
|
||||
if (this.resizeObserver) {
|
||||
this.resizeObserver.disconnect();
|
||||
this.resizeObserver = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
render() {
|
||||
return this.$slots.default?.()[0];
|
||||
},
|
||||
});
|
||||
|
||||
export default VueResizeObserver;
|
|
@ -0,0 +1,139 @@
|
|||
// based on rc-resize-observer 1.0.0
|
||||
import {
|
||||
defineComponent,
|
||||
getCurrentInstance,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
onUpdated,
|
||||
PropType,
|
||||
reactive,
|
||||
watch,
|
||||
} from 'vue';
|
||||
import { findDOMNode } from '../_util/props-util';
|
||||
|
||||
interface ResizeObserverState {
|
||||
height: number;
|
||||
width: number;
|
||||
offsetHeight: number;
|
||||
offsetWidth: number;
|
||||
}
|
||||
|
||||
const VueResizeObserver = defineComponent({
|
||||
name: 'ResizeObserver',
|
||||
props: {
|
||||
disabled: Boolean,
|
||||
onResize: Function as PropType<
|
||||
(
|
||||
size: {
|
||||
width: number;
|
||||
height: number;
|
||||
offsetWidth: number;
|
||||
offsetHeight: number;
|
||||
},
|
||||
element: HTMLElement,
|
||||
) => void
|
||||
>,
|
||||
},
|
||||
emits: ['resize'],
|
||||
setup(props, { slots }) {
|
||||
const state = reactive<ResizeObserverState>({
|
||||
width: 0,
|
||||
height: 0,
|
||||
offsetHeight: 0,
|
||||
offsetWidth: 0,
|
||||
});
|
||||
let currentElement: Element | null = null;
|
||||
let resizeObserver: ResizeObserver | null = null;
|
||||
|
||||
const destroyObserver = () => {
|
||||
if (resizeObserver) {
|
||||
resizeObserver.disconnect();
|
||||
resizeObserver = null;
|
||||
}
|
||||
};
|
||||
|
||||
const onResize: ResizeObserverCallback = (entries: ResizeObserverEntry[]) => {
|
||||
const { onResize } = props;
|
||||
|
||||
const target = entries[0].target as HTMLElement;
|
||||
|
||||
const { width, height } = target.getBoundingClientRect();
|
||||
const { offsetWidth, offsetHeight } = target;
|
||||
|
||||
/**
|
||||
* Resize observer trigger when content size changed.
|
||||
* In most case we just care about element size,
|
||||
* let's use `boundary` instead of `contentRect` here to avoid shaking.
|
||||
*/
|
||||
const fixedWidth = Math.floor(width);
|
||||
const fixedHeight = Math.floor(height);
|
||||
|
||||
if (
|
||||
state.width !== fixedWidth ||
|
||||
state.height !== fixedHeight ||
|
||||
state.offsetWidth !== offsetWidth ||
|
||||
state.offsetHeight !== offsetHeight
|
||||
) {
|
||||
const size = { width: fixedWidth, height: fixedHeight, offsetWidth, offsetHeight };
|
||||
|
||||
Object.assign(state, size);
|
||||
if (onResize) {
|
||||
// defer the callback but not defer to next frame
|
||||
Promise.resolve().then(() => {
|
||||
onResize(
|
||||
{
|
||||
...size,
|
||||
offsetWidth,
|
||||
offsetHeight,
|
||||
},
|
||||
target,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
const instance = getCurrentInstance();
|
||||
const registerObserver = () => {
|
||||
const { disabled } = props;
|
||||
|
||||
// Unregister if disabled
|
||||
if (disabled) {
|
||||
destroyObserver();
|
||||
return;
|
||||
}
|
||||
// Unregister if element changed
|
||||
const element = findDOMNode(instance) as Element;
|
||||
const elementChanged = element !== currentElement;
|
||||
if (elementChanged) {
|
||||
destroyObserver();
|
||||
currentElement = element;
|
||||
}
|
||||
|
||||
if (!resizeObserver && element) {
|
||||
resizeObserver = new ResizeObserver(onResize);
|
||||
resizeObserver.observe(element);
|
||||
}
|
||||
};
|
||||
onMounted(() => {
|
||||
registerObserver();
|
||||
});
|
||||
onUpdated(() => {
|
||||
registerObserver();
|
||||
});
|
||||
onUnmounted(() => {
|
||||
destroyObserver();
|
||||
});
|
||||
watch(
|
||||
() => props.disabled,
|
||||
() => {
|
||||
registerObserver();
|
||||
},
|
||||
{ flush: 'post' },
|
||||
);
|
||||
return () => {
|
||||
return slots.default?.()[0];
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default VueResizeObserver;
|
2
v2-doc
2
v2-doc
|
@ -1 +1 @@
|
|||
Subproject commit a7013ae87f69dcbcf547f4b023255b8a7a775557
|
||||
Subproject commit 4da56dac85f43ad7a288951cbf09d860ecf93bb4
|
Loading…
Reference in New Issue