Skip to page content

v-resize 指令
v2.34+

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