“Resize” 是一个 Quasar 指令,当它所应用的 DOM 元素(或组件)改变尺寸(宽度和/或高度)时,会调用一个方法。上报的尺寸是元素的外部尺寸(包含 padding 和 border),因此元素自身的 padding 变化也会被上报。
底层它使用 Resize Observer API,因此不涉及轮询。
它是 QResizeObserver 组件和 useElementSize composable 的模板侧形式(组件和指令都基于它构建):与组件不同,它不需要在你的模板中额外插入节点,而且可以放在任意元素上,包括那些你不太想去动它插槽的组件。
用法
指令的值就是 handler 函数。它接收一个参数——一个包含元素 width 和 height(单位为像素)的对象。它会在元素刚渲染出来时立即以初始尺寸被调用一次,因此你可以在元素一渲染出来就依赖这个尺寸,之后只在尺寸变化时才再被调用。
基础用法
下面的示例演示了以指令观察一个元素的尺寸变化:
<template>
<div class="q-pa-md">
<q-btn
color="primary"
push
@click="setRandomSize"
label="Set Random Size"
/>
<div
v-resize="onResize"
:style="style"
class="container bg-amber rounded-borders glossy q-my-md"
/>
<div v-if="report" class="q-gutter-sm row items-center">
<div>Reported:</div>
<q-badge :label="`width: ${report.width}`" />
<q-badge :label="`height: ${report.height}`" />
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const style = ref({ width: '200px', height: '200px' })
const report = ref(null)
function onResize(size) {
report.value = size
// {
// width: 20 // width of container (in px)
// height: 50 // height of container (in px)
// }
}
function setRandomSize() {
style.value = {
width: Math.floor(100 + Math.random() * 200) + 'px',
height: Math.floor(100 + Math.random() * 200) + 'px'
}
}
</script>
<style lang="sass" scoped>
.container
transition: width .3s, height .3s
</style>防抖(Debouncing)
指令的参数(argument)是一个以毫秒为单位的防抖时长(例如:v-resize:100)。有了它,元素在每个这样的时间窗口内最多只会被测量一次:连续的尺寸变化(拖动手柄、宽度动画)会周期性上报,而非每一帧都上报,并且最后一次变化永远不会被漏掉。如果需要在运行时改变防抖时长,请使用动态参数(v-resize:[debounce])。
下面的示例对比了立即上报与防抖上报:
<template>
<div class="q-pa-md">
<div class="q-mb-md">
Drag the bottom-right corner of the box. The box reports every frame, the
debounced one at most every 300ms.
</div>
<div class="row q-col-gutter-md">
<div class="col-6">
<div
v-resize="onImmediate"
class="container bg-amber rounded-borders q-pa-sm"
>
Immediate: {{ immediate }}
</div>
<div class="q-mt-sm">Reports: {{ immediateCount }}</div>
</div>
<div class="col-6">
<div
v-resize:300="onDebounced"
class="container bg-orange rounded-borders q-pa-sm"
>
Debounced: {{ debounced }}
</div>
<div class="q-mt-sm">Reports: {{ debouncedCount }}</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const immediate = ref('')
const immediateCount = ref(0)
const debounced = ref('')
const debouncedCount = ref(0)
function onImmediate({ width, height }) {
immediate.value = `${width} x ${height}`
immediateCount.value++
}
function onDebounced({ width, height }) {
debounced.value = `${width} x ${height}`
debouncedCount.value++
}
</script>
<style lang="sass" scoped>
.container
width: 100%
height: 120px
min-width: 100px
min-height: 60px
resize: both
overflow: auto
</style>只触发一次
该指令可以配合 once 修饰符使用(例如:v-resize.once)。handler 函数会以元素的初始尺寸被调用一次,随即停止观察,这使它成为一种无需模板 ref 就能读取元素渲染后尺寸的廉价方式。
禁用
传入 false 或 null(或 undefined)而非一个函数,即可禁用该指令:元素会停止被观察,直到再次提供 handler 为止;届时如果尺寸在此期间发生过变化,会上报当前尺寸。整个过程中 DOM 元素本身不会被触碰,因此它所包裹的内容会保持自身状态。
下面的示例演示了通过开关来启用/禁用观察:
<template>
<div class="q-pa-md">
<div class="row no-wrap items-center q-gutter-md">
<q-btn
color="primary"
push
@click="setRandomSize"
label="Set Random Size"
/>
<q-toggle v-model="enabled" label="Observe" dense />
</div>
<div
v-resize="enabled ? onResize : false"
:style="style"
class="container bg-amber rounded-borders glossy q-my-md"
/>
<div v-if="report" class="q-gutter-sm row items-center">
<div>Reported:</div>
<q-badge :label="`width: ${report.width}`" />
<q-badge :label="`height: ${report.height}`" />
<q-badge color="secondary" :label="`reports: ${count}`" />
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const enabled = ref(true)
const style = ref({ width: '200px', height: '200px' })
const report = ref(null)
const count = ref(0)
function onResize(size) {
report.value = size
count.value++
}
function setRandomSize() {
style.value = {
width: Math.floor(100 + Math.random() * 200) + 'px',
height: Math.floor(100 + Math.random() * 200) + 'px'
}
}
</script>
<style lang="sass" scoped>
.container
transition: width .3s, height .3s
</style>