Browse Source

refactor: resizeObserver

pull/4060/head
tangjinzhou 4 years ago
parent
commit
11ffa8dfda
  1. 2
      components/_util/props-util/index.js
  2. 90
      components/vc-resize-observer/index.jsx
  3. 139
      components/vc-resize-observer/index.tsx
  4. 2
      v2-doc

2
components/_util/props-util/index.js

@ -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;
}

90
components/vc-resize-observer/index.jsx

@ -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;

139
components/vc-resize-observer/index.tsx

@ -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

@ -1 +1 @@
Subproject commit a7013ae87f69dcbcf547f4b023255b8a7a775557
Subproject commit 4da56dac85f43ad7a288951cbf09d860ecf93bb4
Loading…
Cancel
Save