mirror of
https://github.com/GuerillaStudio/souvenir.git
synced 2025-01-20 22:10:20 +00:00
fix(preview/capture) flip with user facing camera
This commit is contained in:
parent
3c4652f6b0
commit
4b4c3bac1b
5 changed files with 69 additions and 28 deletions
|
@ -49,6 +49,10 @@
|
||||||
content: none;
|
content: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.preview--flip {
|
||||||
|
transform: scaleX(-1);
|
||||||
|
}
|
||||||
|
|
||||||
.preview-visual {
|
.preview-visual {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|
29
src/services/camera.js
Normal file
29
src/services/camera.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
export async function getCamera (shouldFaceUser) {
|
||||||
|
const facingModeList = shouldFaceUser
|
||||||
|
? ['user', 'environment']
|
||||||
|
: ['environment', 'user']
|
||||||
|
|
||||||
|
const [preferredFacingMode] = facingModeList
|
||||||
|
|
||||||
|
const constraintsList = [
|
||||||
|
...facingModeList.map(facingMode => ({ exact: facingMode })),
|
||||||
|
preferredFacingMode
|
||||||
|
].map(facingModeConstraint => ({ video: { facingMode: facingModeConstraint } }))
|
||||||
|
|
||||||
|
for (let constraints of constraintsList) {
|
||||||
|
try {
|
||||||
|
console.log(constraints)
|
||||||
|
return {
|
||||||
|
mediaStream: await navigator.mediaDevices.getUserMedia(constraints),
|
||||||
|
facingMode: constraints.video.facingMode.exact ? constraints.video.facingMode.exact : 'unknow'
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
if (error.name !== 'OverconstrainedError' && error.constraint === 'facingMode') {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Overconstrained')
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ import {
|
||||||
GIF_FRAME_RATE
|
GIF_FRAME_RATE
|
||||||
} from '/constants.js'
|
} from '/constants.js'
|
||||||
|
|
||||||
export function capture (mediaStream, duration) {
|
export function capture (mediaStream, duration, facingMode) {
|
||||||
const emitter = new EventEmitter()
|
const emitter = new EventEmitter()
|
||||||
|
|
||||||
Promise.resolve().then(async () => {
|
Promise.resolve().then(async () => {
|
||||||
|
@ -33,9 +33,15 @@ export function capture (mediaStream, duration) {
|
||||||
canvas.width = GIF_WIDTH
|
canvas.width = GIF_WIDTH
|
||||||
canvas.height = GIF_HEIGHT
|
canvas.height = GIF_HEIGHT
|
||||||
|
|
||||||
const canvasContext = canvas.getContext('2d')
|
|
||||||
const destinationRectangle = makeRectangle(0, 0, canvas.width, canvas.height)
|
const destinationRectangle = makeRectangle(0, 0, canvas.width, canvas.height)
|
||||||
|
|
||||||
|
const canvasContext = canvas.getContext('2d')
|
||||||
|
|
||||||
|
if (facingMode === 'user' || facingMode === 'unknow') {
|
||||||
|
canvasContext.translate(destinationRectangle.width, 0)
|
||||||
|
canvasContext.scale(-1, 1)
|
||||||
|
}
|
||||||
|
|
||||||
const video = document.createElement('video')
|
const video = document.createElement('video')
|
||||||
video.setAttribute('playsinline', '')
|
video.setAttribute('playsinline', '')
|
||||||
video.setAttribute('webkit-playsinline', '')
|
video.setAttribute('webkit-playsinline', '')
|
||||||
|
|
46
src/store.js
46
src/store.js
|
@ -1,6 +1,7 @@
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import Vuex from 'vuex'
|
import Vuex from 'vuex'
|
||||||
|
|
||||||
|
import { getCamera } from '/services/camera.js'
|
||||||
import { capture } from '/services/capture.js'
|
import { capture } from '/services/capture.js'
|
||||||
import { encode } from '/services/encode.js'
|
import { encode } from '/services/encode.js'
|
||||||
|
|
||||||
|
@ -11,6 +12,7 @@ export default new Vuex.Store({
|
||||||
state: {
|
state: {
|
||||||
welcomed: false,
|
welcomed: false,
|
||||||
mediaStream: null,
|
mediaStream: null,
|
||||||
|
facingMode: null,
|
||||||
timer: {
|
timer: {
|
||||||
selected: 2,
|
selected: 2,
|
||||||
list: [2, 3, 5]
|
list: [2, 3, 5]
|
||||||
|
@ -34,8 +36,9 @@ export default new Vuex.Store({
|
||||||
updateWelcomed (state, welcome) {
|
updateWelcomed (state, welcome) {
|
||||||
state.welcomed = welcome
|
state.welcomed = welcome
|
||||||
},
|
},
|
||||||
startCamera (state, mediaStream) {
|
startCamera (state, { mediaStream, facingMode }) {
|
||||||
state.mediaStream = mediaStream
|
state.mediaStream = mediaStream
|
||||||
|
state.facingMode = facingMode
|
||||||
},
|
},
|
||||||
stopCamera (state) {
|
stopCamera (state) {
|
||||||
if (state.mediaStream) {
|
if (state.mediaStream) {
|
||||||
|
@ -43,6 +46,10 @@ export default new Vuex.Store({
|
||||||
}
|
}
|
||||||
|
|
||||||
state.mediaStream = null
|
state.mediaStream = null
|
||||||
|
state.facingMode = null
|
||||||
|
},
|
||||||
|
updateFacingMode (state, facingMode) {
|
||||||
|
state.facingMode = facingMode
|
||||||
},
|
},
|
||||||
inverseFacingMode (state) {
|
inverseFacingMode (state) {
|
||||||
state.capturing.shouldFaceUser = !state.capturing.shouldFaceUser
|
state.capturing.shouldFaceUser = !state.capturing.shouldFaceUser
|
||||||
|
@ -84,37 +91,28 @@ export default new Vuex.Store({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
requestCamera ({ state, commit }, inverseFacingMode) {
|
async requestCamera ({ state, commit }, inverseFacingMode) {
|
||||||
|
commit('stopCamera')
|
||||||
|
|
||||||
const shouldFaceUser = inverseFacingMode
|
const shouldFaceUser = inverseFacingMode
|
||||||
? !state.capturing.shouldFaceUser
|
? !state.capturing.shouldFaceUser
|
||||||
: state.capturing.shouldFaceUser
|
: state.capturing.shouldFaceUser
|
||||||
|
|
||||||
const constraints = {
|
try {
|
||||||
video: {
|
commit('startCamera', await getCamera(shouldFaceUser))
|
||||||
facingMode: shouldFaceUser ? 'user' : 'environment'
|
|
||||||
},
|
if (inverseFacingMode) {
|
||||||
audio: false
|
commit('inverseFacingMode')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
window.alert('You haven\'t allowed to use your camera.\n\nOr maybe your browser is not compatible :(')
|
||||||
|
commit('updateWelcomed', false)
|
||||||
}
|
}
|
||||||
|
|
||||||
commit('stopCamera')
|
|
||||||
|
|
||||||
navigator.mediaDevices.getUserMedia(constraints)
|
|
||||||
.then(mediaStream => {
|
|
||||||
commit('startCamera', mediaStream)
|
|
||||||
|
|
||||||
if (inverseFacingMode) {
|
|
||||||
commit('inverseFacingMode')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error(error)
|
|
||||||
window.alert('You haven\'t allowed to use your camera.\n\nOr maybe your browser is not compatible :(')
|
|
||||||
commit('updateWelcomed', false)
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
capture ({ state, commit, dispatch }) {
|
capture ({ state, commit, dispatch }) {
|
||||||
commit('startCapture')
|
commit('startCapture')
|
||||||
const capturing = capture(state.mediaStream, state.timer.selected * 1000)
|
const capturing = capture(state.mediaStream, state.timer.selected * 1000, state.facingMode)
|
||||||
|
|
||||||
capturing.once('error', error => console.error(error))
|
capturing.once('error', error => console.error(error))
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<capture-options v-else></capture-options>
|
<capture-options v-else></capture-options>
|
||||||
|
|
||||||
<div class="preview">
|
<div class="preview">
|
||||||
<video ref="preview" class="preview-visual" preload="yes" autoplay muted playsinline webkit-playsinline></video>
|
<video ref="preview" class="preview-visual" :class="{ 'preview--flip': flipActive }" preload="yes" autoplay muted playsinline webkit-playsinline></video>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="capture-btn" :class="{ 'capture-btn--capturing': capturing.status }" :disabled="!mediaStream" @click.prevent="startCapture">Capture</button>
|
<button class="capture-btn" :class="{ 'capture-btn--capturing': capturing.status }" :disabled="!mediaStream" @click.prevent="startCapture">Capture</button>
|
||||||
|
@ -34,10 +34,14 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
...mapState([
|
...mapState([
|
||||||
'mediaStream',
|
'mediaStream',
|
||||||
|
'facingMode',
|
||||||
'capturing',
|
'capturing',
|
||||||
'timer',
|
'timer',
|
||||||
'encoding'
|
'encoding'
|
||||||
])
|
]),
|
||||||
|
flipActive () {
|
||||||
|
return this.facingMode === 'user' || this.facingMode === 'unknow'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
startCapture () {
|
startCapture () {
|
||||||
|
|
Loading…
Reference in a new issue