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