diff --git a/src/assets/css/4-modules/preview.css b/src/assets/css/4-modules/preview.css index e83324a..3bc2a1c 100644 --- a/src/assets/css/4-modules/preview.css +++ b/src/assets/css/4-modules/preview.css @@ -49,6 +49,10 @@ content: none; } +.preview--flip { + transform: scaleX(-1); +} + .preview-visual { position: absolute; top: 0; diff --git a/src/services/camera.js b/src/services/camera.js new file mode 100644 index 0000000..8b2f2d8 --- /dev/null +++ b/src/services/camera.js @@ -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') +} diff --git a/src/services/capture.js b/src/services/capture.js index c507b96..97937f4 100644 --- a/src/services/capture.js +++ b/src/services/capture.js @@ -8,7 +8,7 @@ import { GIF_FRAME_RATE } from '/constants.js' -export function capture (mediaStream, duration) { +export function capture (mediaStream, duration, facingMode) { const emitter = new EventEmitter() Promise.resolve().then(async () => { @@ -33,9 +33,15 @@ export function capture (mediaStream, duration) { canvas.width = GIF_WIDTH canvas.height = GIF_HEIGHT - const canvasContext = canvas.getContext('2d') 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') video.setAttribute('playsinline', '') video.setAttribute('webkit-playsinline', '') diff --git a/src/store.js b/src/store.js index 361cd8f..c5a95cc 100644 --- a/src/store.js +++ b/src/store.js @@ -1,6 +1,7 @@ import Vue from 'vue' import Vuex from 'vuex' +import { getCamera } from '/services/camera.js' import { capture } from '/services/capture.js' import { encode } from '/services/encode.js' @@ -11,6 +12,7 @@ export default new Vuex.Store({ state: { welcomed: false, mediaStream: null, + facingMode: null, timer: { selected: 2, list: [2, 3, 5] @@ -34,8 +36,9 @@ export default new Vuex.Store({ updateWelcomed (state, welcome) { state.welcomed = welcome }, - startCamera (state, mediaStream) { + startCamera (state, { mediaStream, facingMode }) { state.mediaStream = mediaStream + state.facingMode = facingMode }, stopCamera (state) { if (state.mediaStream) { @@ -43,6 +46,10 @@ export default new Vuex.Store({ } state.mediaStream = null + state.facingMode = null + }, + updateFacingMode (state, facingMode) { + state.facingMode = facingMode }, inverseFacingMode (state) { state.capturing.shouldFaceUser = !state.capturing.shouldFaceUser @@ -84,37 +91,28 @@ export default new Vuex.Store({ } }, actions: { - requestCamera ({ state, commit }, inverseFacingMode) { + async requestCamera ({ state, commit }, inverseFacingMode) { + commit('stopCamera') + const shouldFaceUser = inverseFacingMode ? !state.capturing.shouldFaceUser : state.capturing.shouldFaceUser - const constraints = { - video: { - facingMode: shouldFaceUser ? 'user' : 'environment' - }, - audio: false + try { + commit('startCamera', await getCamera(shouldFaceUser)) + + 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) } - - 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 }) { 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)) diff --git a/src/views/screens/capture.vue b/src/views/screens/capture.vue index 7af007f..a4abc22 100644 --- a/src/views/screens/capture.vue +++ b/src/views/screens/capture.vue @@ -6,7 +6,7 @@
- +
@@ -34,10 +34,14 @@ export default { computed: { ...mapState([ 'mediaStream', + 'facingMode', 'capturing', 'timer', 'encoding' - ]) + ]), + flipActive () { + return this.facingMode === 'user' || this.facingMode === 'unknow' + } }, methods: { startCapture () {