tangjinzhou
4 years ago
4 changed files with 141 additions and 92 deletions
@ -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; |
Loading…
Reference in new issue