點燈坊

失くすものさえない今が強くなるチャンスよ

當 Vue-web-cam 當配 v-if 顯示

Sam Xiao's Avatar 2020-08-22

Vue-web-cam 若正常使用沒問題,但若將 Component 包在 v-if 內時則表現不如預期,必須配合一些 Workaround 解決。

Version

macOS Catalina 10.15.6
WebStorm 2020.2
Vue 2.6.11
Vue-web-cam 1.9.0

Simple v-if

<template>
  <div>
    <div>
      <button type="button" @click="onToggle">Toggle Camera</button>
    </div>
    <div>
      <div v-if="isIfCamera">
        <web-cam ref="webcam" :device-id="deviceId" width="100%" @cameras="onCameras"/>
        <select v-model="deviceId">
          <option>-- Select Device --</option>
          <option v-for="device in devices" :key="device.deviceId" :value="device.deviceId">{{ device.label }}</option>
        </select>
      </div>
    </div>
  </div>
</template>

<script>
import { WebCam } from 'vue-web-cam'

let onCameras = function(cameras) {
  this.devices = cameras

  let first = this.devices[0]
  if (first) this.deviceId = first.deviceId
}

let onToggle = function() {
  this.isIfCamera = !this.isIfCamera
}

export default {
  name: 'App',
  components: {
    WebCam
  },
  data: () => ({
    deviceId: null,
    devices: [],
    isIfCamera: false
  }),
  methods: {
    onCameras,
    onToggle,
  }
}
</script>

第 7 行

<div v-if="isIfCamera">
  <web-cam ref="webcam" :device-id="deviceId" width="100%" @cameras="onCameras"/>
  <select v-model="deviceId">
    <option>-- Select Device --</option>
    <option v-for="device in devices" :key="device.deviceId" :value="device.deviceId">{{ device.label }}</option>
  </select>
</div>

若想將 vue-web-camera 放在 v-if 內控制,直覺會將 <web-cam> 放在 <div> 內搭配 v-if

40 行

isIfCamera: false

一開始不顯示。

28 行

let onToggle = function() {
  this.isIfCamera = !this.isIfCamera
}

onToggle() 內將 isIfCamera model 做 toggle。

這種做法雖然直覺,但會發現只有第一次會成功,第二次之後就無法 toggle,主要是因為 v-if 會摧毀 component,導致之後 toggle 失常

v-if && v-show

<template>
  <div>
    <div>
      <button type="button" @click="onToggle">Toggle Camera</button>
    </div>
    <div>
      <div v-if="isIfCamera">
        <div v-show="isShowCamera">
          <web-cam ref="webcam" :device-id="deviceId" width="100%" @cameras="onCameras"/>
          <select v-model="deviceId">
            <option>-- Select Device --</option>
            <option v-for="device in devices" :key="device.deviceId" :value="device.deviceId">{{ device.label }}</option>
          </select>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { WebCam } from 'vue-web-cam'

let onCameras = function(cameras) {
  this.devices = cameras

  let first = this.devices[0]
  if (first) this.deviceId = first.deviceId
}

let onToggle = function() {
  if (!this.isIfCamera) {
    this.isIfCamera = true
  } else if (this.isShowCamera) {
    this.isShowCamera = false
    this.$refs.webcam.stop()
  }
  else {
    this.isShowCamera = true
    this.$refs.webcam.start()
  }
}

export default {
  name: 'App',
  components: {
    WebCam
  },
  data: () => ({
    deviceId: null,
    devices: [],
    isIfCamera: false,
    isShowCamera: true
  }),
  methods: {
    onCameras,
    onToggle,
  }
}
</script>

第 7 行

<div v-if="isIfCamera">
  <div v-show="isShowCamera">
    <web-cam ref="webcam" :device-id="deviceId" width="100%" @cameras="onCameras"/>
    <select v-model="deviceId">
      <option>-- Select Device --</option>
      <option v-for="device in devices" :key="device.deviceId" :value="device.deviceId">{{ device.label }}</option>
    </select>
  </div>
</div>

因為 v-if 會摧毀 component,所以 v-if 只用來第一次建立 component,之後就用 v-show toggle,如此可保 component 不被摧毀。

51 行

isIfCamera: false,
isShowCamera: true

isIfCamera model 控制 component 第一次建立,isShowCamera model 控制 component toggle。

30 行

let onToggle = function() {
  if (!this.isIfCamera) {
    this.isIfCamera = true
  } else if (this.isShowCamera) {
    this.isShowCamera = false
    this.$refs.webcam.stop()
  }
  else {
    this.isShowCamera = true
    this.$refs.webcam.start()
  }
}
  • isIfCamerafalse,表示 component 尚未建立,將 isIfCamera 設定為 true 建立 component
  • isShowCameratrue,表示目前 compoent 正常顯示,將 isShowCamera 設定為 false 隱藏 camera,並搭配 this.$refs.webcam.stop() 關閉 camera
  • isShowCamerafalse,表示目前 compoent 隱藏中,將 isShowCamera 設定為 true 顯示 camera,並搭配 this.$refs.webcam.start() 顯示 camera

Conclusion

  • Vue-web-camera 再搭配 v-if 使用上頗為 tricky,必須同時搭配 v-ifv-show,且手動使用 stop()start() 控制 camera 關閉與啟動

Reference

VinceG, vue-web-cam