parent
b7eaaea8e0
commit
78c811536d
4 changed files with 339 additions and 93 deletions
|
|
@ -613,13 +613,26 @@ $admin-fg: white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mic-button {
|
||||||
|
background-color: transparent !important;
|
||||||
|
&.waiting-for-long-tap {
|
||||||
|
transition: background-color 0.5s;
|
||||||
|
background-color: black !important;
|
||||||
|
.v-icon {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.voice-recorder {
|
.voice-recorder {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
//top: 0;
|
||||||
left: 0;
|
left: 10px;
|
||||||
bottom: 0;
|
bottom: 0px;
|
||||||
right: 0;
|
right: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
background-color: black;
|
background-color: black;
|
||||||
|
overflow: hidden;
|
||||||
.will-cancel {
|
.will-cancel {
|
||||||
background-color: #ff3300;
|
background-color: #ff3300;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -153,15 +153,14 @@
|
||||||
v-if="!currentInput || currentInput.length == 0"
|
v-if="!currentInput || currentInput.length == 0"
|
||||||
>
|
>
|
||||||
<v-btn
|
<v-btn
|
||||||
|
class="mic-button"
|
||||||
ref="mic_button"
|
ref="mic_button"
|
||||||
fab
|
fab
|
||||||
small
|
small
|
||||||
elevation="0"
|
elevation="0"
|
||||||
color="transparent"
|
|
||||||
v-blur
|
v-blur
|
||||||
style="z-index: 10"
|
style="z-index: 10"
|
||||||
@touchstart.native.stop="startRecording"
|
v-longTap:500="[showRecordingUI,startRecording]"
|
||||||
@mousedown.native.stop="startRecording"
|
|
||||||
>
|
>
|
||||||
<v-icon :color="showRecorder ? 'white' : 'black'">mic</v-icon>
|
<v-icon :color="showRecorder ? 'white' : 'black'">mic</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
|
@ -209,6 +208,7 @@
|
||||||
</v-row>
|
</v-row>
|
||||||
<VoiceRecorder
|
<VoiceRecorder
|
||||||
:micButtonRef="$refs.mic_button"
|
:micButtonRef="$refs.mic_button"
|
||||||
|
:ptt="showRecorderPTT"
|
||||||
:show="showRecorder"
|
:show="showRecorder"
|
||||||
v-on:close="showRecorder = false"
|
v-on:close="showRecorder = false"
|
||||||
v-on:file="onVoiceRecording"
|
v-on:file="onVoiceRecording"
|
||||||
|
|
@ -383,6 +383,7 @@ export default {
|
||||||
showContextMenuAnchor: null,
|
showContextMenuAnchor: null,
|
||||||
initialLoadDone: false,
|
initialLoadDone: false,
|
||||||
showRecorder: false,
|
showRecorder: false,
|
||||||
|
showRecorderPTT: false, // True to open the voice recorder in push-to-talk mode.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current chat container size. We need to keep track of this so that if and when
|
* Current chat container size. We need to keep track of this so that if and when
|
||||||
|
|
@ -668,7 +669,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggered when out "long tap" timer hits.
|
* Triggered when our "long tap" timer hits.
|
||||||
*/
|
*/
|
||||||
touchTimerElapsed() {
|
touchTimerElapsed() {
|
||||||
this.showContextMenu = true;
|
this.showContextMenu = true;
|
||||||
|
|
@ -1163,7 +1164,13 @@ export default {
|
||||||
return util.formatDay(event.getTs());
|
return util.formatDay(event.getTs());
|
||||||
},
|
},
|
||||||
|
|
||||||
|
showRecordingUI() {
|
||||||
|
this.showRecorderPTT = false;
|
||||||
|
this.showRecorder = true;
|
||||||
|
},
|
||||||
|
|
||||||
startRecording() {
|
startRecording() {
|
||||||
|
this.showRecorderPTT = true;
|
||||||
this.showRecorder = true;
|
this.showRecorder = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,80 +1,132 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-show="show" class="voice-recorder" ref="vrroot">
|
<transition name="grow" mode="out-in">
|
||||||
<v-container fluid fill-height>
|
<div v-show="show" class="voice-recorder" ref="vrroot">
|
||||||
<v-row align="center">
|
<!-- <div style="background-color:red;height:60px;width:100%"/> -->
|
||||||
<v-col cols="3">
|
<v-container v-if="!ptt" fluid fill-height>
|
||||||
<div class="recording-time">
|
|
||||||
{{ recordingTime }}
|
|
||||||
</div>
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="6">
|
|
||||||
<div class="swipe-info"><< Swipe to cancel</div>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-container>
|
|
||||||
<transition name="fade" mode="out-in">
|
|
||||||
<div
|
|
||||||
v-if="willCancel"
|
|
||||||
class="will-cancel"
|
|
||||||
style="position: absolute; top: 0; left: 0; right: 0; bottom: 0"
|
|
||||||
>
|
|
||||||
<v-container fluid fill-height>
|
|
||||||
<v-row align="center">
|
|
||||||
<v-col cols="3">
|
|
||||||
<v-icon color="white">delete_outline</v-icon>
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="6">
|
|
||||||
<div class="swipe-info">Release to cancel</div>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-container>
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
|
|
||||||
<transition name="fade" mode="out-in">
|
|
||||||
<div
|
|
||||||
v-if="recordingLocked"
|
|
||||||
class="locked"
|
|
||||||
style="position: absolute; top: 0; left: 0; right: 0; bottom: 0"
|
|
||||||
>
|
|
||||||
<v-container fluid fill-height>
|
|
||||||
<v-row align="center">
|
|
||||||
<v-col cols="3">
|
|
||||||
<div class="recording-time">
|
|
||||||
{{ recordingTime }}
|
|
||||||
</div>
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="3">
|
|
||||||
<v-btn @click.stop="cancelRecording" text class="swipe-info"
|
|
||||||
>Cancel</v-btn
|
|
||||||
>
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="3">
|
|
||||||
<v-btn @click.stop="stopRecording" icon class="swipe-info"
|
|
||||||
><v-icon color="white">stop</v-icon></v-btn
|
|
||||||
>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-container>
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
|
|
||||||
<div
|
|
||||||
v-if="state == states.ERROR"
|
|
||||||
class="error"
|
|
||||||
style="position: absolute; top: 0; left: 0; right: 0; bottom: 0"
|
|
||||||
>
|
|
||||||
<v-container fluid fill-height>
|
|
||||||
<v-row align="center">
|
<v-row align="center">
|
||||||
<v-col>
|
<v-col cols="4" align="center">
|
||||||
<div class="swipe-info">Failed to record audio</div>
|
<v-icon v-show="false" color="white">delete_outline</v-icon>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="4" align="center">
|
||||||
|
<v-btn
|
||||||
|
v-if="state == states.RECORDING"
|
||||||
|
style="background-color: white; padding: 30px"
|
||||||
|
icon
|
||||||
|
@click.stop="pauseRecording"
|
||||||
|
>
|
||||||
|
<v-icon color="black">stop</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
<v-btn
|
||||||
|
v-else-if="state == states.RECORDED"
|
||||||
|
style="background-color: green; padding: 30px"
|
||||||
|
icon
|
||||||
|
:disabled="!recordedFile"
|
||||||
|
@click.stop="send"
|
||||||
|
>
|
||||||
|
<v-icon color="white">arrow_upward</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
<v-btn
|
||||||
|
v-else
|
||||||
|
style="background-color: red; padding: 30px"
|
||||||
|
icon
|
||||||
|
@click.stop="startRecording"
|
||||||
|
>
|
||||||
|
<v-icon color="white">fiber_manual_record</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="4" align="center">
|
||||||
|
<v-btn icon @click.stop="cancelRecording">
|
||||||
|
<v-icon color="white">close</v-icon>
|
||||||
|
</v-btn>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-container>
|
</v-container>
|
||||||
</div>
|
|
||||||
|
|
||||||
<VoiceRecorderLock v-show="state == states.RECORDING" :style="lockButtonStyle" :isLocked="recordingLocked" />
|
<v-container fluid fill-height>
|
||||||
</div>
|
<v-row align="center">
|
||||||
|
<v-col cols="3">
|
||||||
|
<div class="recording-time">
|
||||||
|
{{ recordingTime }}
|
||||||
|
</div>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="6" v-if="ptt">
|
||||||
|
<div class="swipe-info"><< Swipe to cancel</div>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-container>
|
||||||
|
<transition name="fade" mode="out-in">
|
||||||
|
<div
|
||||||
|
v-if="willCancel"
|
||||||
|
class="will-cancel"
|
||||||
|
style="position: absolute; top: 0; left: 0; right: 0; bottom: 0"
|
||||||
|
>
|
||||||
|
<v-container fluid fill-height>
|
||||||
|
<v-row align="center">
|
||||||
|
<v-col cols="3">
|
||||||
|
<v-icon color="white">delete_outline</v-icon>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="6">
|
||||||
|
<div class="swipe-info">Release to cancel</div>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-container>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
|
||||||
|
<transition name="fade" mode="out-in">
|
||||||
|
<div
|
||||||
|
v-if="recordingLocked"
|
||||||
|
class="locked"
|
||||||
|
style="position: absolute; top: 0; left: 0; right: 0; bottom: 0"
|
||||||
|
>
|
||||||
|
<v-container fluid fill-height>
|
||||||
|
<v-row align="center">
|
||||||
|
<v-col cols="3">
|
||||||
|
<div class="recording-time">
|
||||||
|
{{ recordingTime }}
|
||||||
|
</div>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="3">
|
||||||
|
<v-btn @click.stop="cancelRecording" text class="swipe-info"
|
||||||
|
>Cancel</v-btn
|
||||||
|
>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="3">
|
||||||
|
<v-btn @click.stop="stopRecording" icon class="swipe-info"
|
||||||
|
><v-icon color="white">stop</v-icon></v-btn
|
||||||
|
>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-container>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="state == states.ERROR"
|
||||||
|
class="error"
|
||||||
|
style="position: absolute; top: 0; left: 0; right: 0; bottom: 0"
|
||||||
|
>
|
||||||
|
<v-container fluid fill-height>
|
||||||
|
<v-row align="center">
|
||||||
|
<v-col>
|
||||||
|
<div class="swipe-info">Failed to record audio</div>
|
||||||
|
</v-col>
|
||||||
|
<v-col align="right">
|
||||||
|
<v-btn icon @click.stop="cancelRecording">
|
||||||
|
<v-icon color="white">close</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-container>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<VoiceRecorderLock
|
||||||
|
v-show="state == states.RECORDING && ptt"
|
||||||
|
:style="lockButtonStyle"
|
||||||
|
:isLocked="recordingLocked"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
const State = {
|
const State = {
|
||||||
|
|
@ -98,6 +150,12 @@ export default {
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
ptt: {
|
||||||
|
type: Boolean,
|
||||||
|
default: function () {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
},
|
||||||
micButtonRef: {
|
micButtonRef: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: function () {
|
default: function () {
|
||||||
|
|
@ -114,21 +172,59 @@ export default {
|
||||||
states: State,
|
states: State,
|
||||||
state: State.INITIAL,
|
state: State.INITIAL,
|
||||||
recordStartedAt: null,
|
recordStartedAt: null,
|
||||||
recordingTime: null,
|
recordingTime: String.fromCharCode(160), // nbsp!
|
||||||
recordTimer: null,
|
recordTimer: null,
|
||||||
recordingLocked: false,
|
recordingLocked: false,
|
||||||
|
recordedFile: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
micButtonRef(buttonRef) {
|
||||||
|
if (buttonRef) {
|
||||||
|
var r = buttonRef.$el.getBoundingClientRect();
|
||||||
|
var left = r.left;
|
||||||
|
var width = r.right - r.left;
|
||||||
|
r = this.$refs.vrroot.parentElement.getBoundingClientRect();
|
||||||
|
var widthParent = r.right - r.left;
|
||||||
|
document.documentElement.style.setProperty(
|
||||||
|
"--v-mic-button-left",
|
||||||
|
left + "px"
|
||||||
|
);
|
||||||
|
document.documentElement.style.setProperty(
|
||||||
|
"--v-mic-button-width",
|
||||||
|
width + "px"
|
||||||
|
);
|
||||||
|
document.documentElement.style.setProperty(
|
||||||
|
"--v-mic-button-container-width",
|
||||||
|
widthParent + "px"
|
||||||
|
);
|
||||||
|
var initialScale = width / widthParent;
|
||||||
|
document.documentElement.style.setProperty(
|
||||||
|
"--v-mic-button-initial-scale",
|
||||||
|
initialScale
|
||||||
|
);
|
||||||
|
var initialTranslate = left + width / 2 - widthParent / 2;
|
||||||
|
document.documentElement.style.setProperty(
|
||||||
|
"--v-mic-button-initial-translate",
|
||||||
|
initialTranslate + "px"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
show(val) {
|
show(val) {
|
||||||
if (val) {
|
if (val) {
|
||||||
// Add listeners
|
// Add listeners
|
||||||
this.state = State.INITIAL;
|
this.state = State.INITIAL;
|
||||||
document.addEventListener("mouseup", this.mouseUp, false);
|
this.recordedFile = null;
|
||||||
document.addEventListener("mousemove", this.mouseMove, false);
|
if (this.ptt) {
|
||||||
document.addEventListener("touchend", this.mouseUp, false);
|
document.addEventListener("mouseup", this.mouseUp, false);
|
||||||
document.addEventListener("touchmove", this.mouseMove, false);
|
document.addEventListener("mousemove", this.mouseMove, false);
|
||||||
this.startRecording();
|
document.addEventListener("touchend", this.mouseUp, false);
|
||||||
|
document.addEventListener("touchmove", this.mouseMove, false);
|
||||||
|
this.startRecording();
|
||||||
|
} else {
|
||||||
|
console.log("Not PTT");
|
||||||
|
this.micButtonRef.$el.style.display = "none";
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Remove listeners
|
// Remove listeners
|
||||||
document.removeEventListener("mouseup", this.mouseUp, false);
|
document.removeEventListener("mouseup", this.mouseUp, false);
|
||||||
|
|
@ -141,6 +237,7 @@ export default {
|
||||||
this.startCoordinateX = null;
|
this.startCoordinateX = null;
|
||||||
this.startCoordinateY = null;
|
this.startCoordinateY = null;
|
||||||
this.recordingLocked = false;
|
this.recordingLocked = false;
|
||||||
|
this.micButtonRef.$el.style.display = "block";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -169,6 +266,7 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
close() {
|
close() {
|
||||||
this.stopRecordTimer();
|
this.stopRecordTimer();
|
||||||
|
this.recorder = null;
|
||||||
this.$emit("close");
|
this.$emit("close");
|
||||||
},
|
},
|
||||||
mouseUp(ignoredEvent) {
|
mouseUp(ignoredEvent) {
|
||||||
|
|
@ -230,21 +328,37 @@ export default {
|
||||||
},
|
},
|
||||||
cancelRecording() {
|
cancelRecording() {
|
||||||
this.state = State.INITIAL;
|
this.state = State.INITIAL;
|
||||||
this.recorder.stop();
|
if (this.recorder) {
|
||||||
|
this.recorder.stop();
|
||||||
|
}
|
||||||
this.stopRecordTimer();
|
this.stopRecordTimer();
|
||||||
this.close();
|
this.close();
|
||||||
},
|
},
|
||||||
|
pauseRecording() {
|
||||||
|
this.state = State.RECORDED;
|
||||||
|
this.stopRecordTimer();
|
||||||
|
this.getFile(false);
|
||||||
|
},
|
||||||
stopRecording() {
|
stopRecording() {
|
||||||
this.state = State.RECORDED;
|
this.state = State.RECORDED;
|
||||||
this.stopRecordTimer();
|
this.stopRecordTimer();
|
||||||
this.close();
|
this.close();
|
||||||
|
this.getFile(true);
|
||||||
|
},
|
||||||
|
send() {
|
||||||
|
console.log("Send:", this.recordedFile);
|
||||||
|
//this.$emit("file", {file: file});
|
||||||
|
// const player = new Audio(URL.createObjectURL(file));
|
||||||
|
// player.play();
|
||||||
|
},
|
||||||
|
getFile(send) {
|
||||||
this.recorder
|
this.recorder
|
||||||
.stop()
|
.stop()
|
||||||
.getMp3()
|
.getMp3()
|
||||||
.then(([buffer, blob]) => {
|
.then(([buffer, blob]) => {
|
||||||
// do what ever you want with buffer and blob
|
// do what ever you want with buffer and blob
|
||||||
// Example: Create a mp3 file and play
|
// Example: Create a mp3 file and play
|
||||||
const file = new File(
|
this.recordedFile = new File(
|
||||||
buffer,
|
buffer,
|
||||||
util.formatRecordStartTime(this.recordStartedAt) + ".mp3",
|
util.formatRecordStartTime(this.recordStartedAt) + ".mp3",
|
||||||
{
|
{
|
||||||
|
|
@ -252,10 +366,9 @@ export default {
|
||||||
lastModified: Date.now(),
|
lastModified: Date.now(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
//console.log(file);
|
if (send) {
|
||||||
this.$emit("file", {file: file});
|
this.send();
|
||||||
// const player = new Audio(URL.createObjectURL(file));
|
}
|
||||||
// player.play();
|
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
alert("We could not retrieve your message");
|
alert("We could not retrieve your message");
|
||||||
|
|
@ -275,7 +388,7 @@ export default {
|
||||||
if (this.recordTimer) {
|
if (this.recordTimer) {
|
||||||
clearInterval(this.recordTimer);
|
clearInterval(this.recordTimer);
|
||||||
this.recordTimer = null;
|
this.recordTimer = null;
|
||||||
this.recordingTime = null;
|
this.recordingTime = String.fromCharCode(160); // nbsp;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -286,6 +399,19 @@ export default {
|
||||||
@import "@/assets/css/chat.scss";
|
@import "@/assets/css/chat.scss";
|
||||||
</style>
|
</style>
|
||||||
<style>
|
<style>
|
||||||
|
.grow-enter-active,
|
||||||
|
.grow-leave-active {
|
||||||
|
transition-timing-function: ease-out;
|
||||||
|
transition: opacity 0.5s, border-radius 0.7s, transform 0.7s;
|
||||||
|
}
|
||||||
|
.grow-enter,
|
||||||
|
.grow-leave-to {
|
||||||
|
transform: translateX(var(--v-mic-button-initial-translate))
|
||||||
|
scaleX(var(--v-mic-button-initial-scale));
|
||||||
|
opacity: 0;
|
||||||
|
border-radius: 25px !important;
|
||||||
|
}
|
||||||
|
|
||||||
.fade-enter-active,
|
.fade-enter-active,
|
||||||
.fade-leave-active {
|
.fade-leave-active {
|
||||||
transition: opacity 0.5s;
|
transition: opacity 0.5s;
|
||||||
|
|
|
||||||
102
src/main.js
102
src/main.js
|
|
@ -22,7 +22,7 @@ Vue.config.productionTip = false
|
||||||
|
|
||||||
Vue.use(VueResize);
|
Vue.use(VueResize);
|
||||||
Vue.use(VEmojiPicker);
|
Vue.use(VEmojiPicker);
|
||||||
Vue.use(matrix, {store: store});
|
Vue.use(matrix, { store: store });
|
||||||
Vue.use(VueClipboard);
|
Vue.use(VueClipboard);
|
||||||
|
|
||||||
// Add bubble functionality to custom events.
|
// Add bubble functionality to custom events.
|
||||||
|
|
@ -45,6 +45,106 @@ Vue.directive('blur', {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle long taps. Call with the timeout as argument (default 500ms) and the value
|
||||||
|
* can be either one function called on long tap or two functions, the
|
||||||
|
* first called on "short tap" and the other on "long tap".
|
||||||
|
*
|
||||||
|
* Like this: v-linkTap:500="[tapped,longTapped]"
|
||||||
|
*/
|
||||||
|
Vue.directive('longTap', {
|
||||||
|
bind: function (el, binding, ignoredvnode) {
|
||||||
|
el.longTapTimeout = parseInt(binding.arg || "500");
|
||||||
|
el.longTapCallbacks = binding.value;
|
||||||
|
for (var i = el.longTapCallbacks.length; i < 2; i++) {
|
||||||
|
el.longTapCallbacks.splice(0, 0, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
const touchX = function (event) {
|
||||||
|
if (event.type.indexOf("mouse") !== -1) {
|
||||||
|
return event.clientX;
|
||||||
|
}
|
||||||
|
return event.touches[0].clientX;
|
||||||
|
};
|
||||||
|
const touchY = function (event) {
|
||||||
|
if (event.type.indexOf("mouse") !== -1) {
|
||||||
|
return event.clientY;
|
||||||
|
}
|
||||||
|
return event.touches[0].clientY;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered when our "long tap" timer hits.
|
||||||
|
*/
|
||||||
|
const touchTimerElapsed = function () {
|
||||||
|
el.longTapHandled = true;
|
||||||
|
el.longTapCallbacks[1] && el.longTapCallbacks[1].call();
|
||||||
|
el.longTapTimer = null;
|
||||||
|
el.classList.remove("waiting-for-long-tap");
|
||||||
|
};
|
||||||
|
|
||||||
|
const touchStart = function (e) {
|
||||||
|
el.longTapHandled = false;
|
||||||
|
el.longTapStartX = touchX(e);
|
||||||
|
el.longTapStartY = touchY(e);
|
||||||
|
el.longTapTimer = setTimeout(touchTimerElapsed, el.longTapTimeout);
|
||||||
|
el.classList.add("waiting-for-long-tap");
|
||||||
|
e.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
|
const touchCancel = function () {
|
||||||
|
el.longTapHandled = true;
|
||||||
|
el.longTapTimer && clearTimeout(el.longTapTimer);
|
||||||
|
el.longTapTimer = null;
|
||||||
|
el.classList.remove("waiting-for-long-tap");
|
||||||
|
};
|
||||||
|
|
||||||
|
const touchEnd = function () {
|
||||||
|
el.longTapTimer && clearTimeout(el.longTapTimer);
|
||||||
|
el.longTapTimer = null;
|
||||||
|
if (!el.longTapHandled) {
|
||||||
|
// Not canceled or long tapped. Just a single tap. Do we have a handler?
|
||||||
|
el.longTapCallbacks[0] && el.longTapCallbacks[0].call();
|
||||||
|
}
|
||||||
|
el.classList.remove("waiting-for-long-tap");
|
||||||
|
};
|
||||||
|
|
||||||
|
const touchMove = function (e) {
|
||||||
|
el.longTapCurrentX = touchX(e);
|
||||||
|
el.longTapCurrentY = touchY(e);
|
||||||
|
var tapTolerance = 4;
|
||||||
|
var touchMoved =
|
||||||
|
Math.abs(el.longTapStartX - el.longTapCurrentX) > tapTolerance ||
|
||||||
|
Math.abs(el.longTapStartY - el.longTapCurrentY) > tapTolerance;
|
||||||
|
if (touchMoved) {
|
||||||
|
touchCancel();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
el.longTapTouchStart = touchStart;
|
||||||
|
el.longTapTouchEnd = touchEnd;
|
||||||
|
el.longTapTouchCancel = touchCancel;
|
||||||
|
el.longTapTouchMove = touchMove;
|
||||||
|
el.addEventListener("touchstart", touchStart);
|
||||||
|
el.addEventListener("touchend", touchEnd);
|
||||||
|
el.addEventListener("touchcancel", touchCancel);
|
||||||
|
el.addEventListener("touchmove", touchMove);
|
||||||
|
el.addEventListener("mousedown", touchStart);
|
||||||
|
el.addEventListener("mouseup", touchEnd);
|
||||||
|
el.addEventListener("mousemove", touchMove);
|
||||||
|
},
|
||||||
|
unbind: function (el) {
|
||||||
|
el.longTapTimer && clearTimeout(el.longTapTimer);
|
||||||
|
el.removeEventListener("touchstart", el.longTapTouchStart);
|
||||||
|
el.removeEventListener("touchend", el.longTapTouchEnd);
|
||||||
|
el.removeEventListener("touchcancel", el.longTapTouchCancel);
|
||||||
|
el.removeEventListener("touchmove", el.longTapTouchMove);
|
||||||
|
el.removeEventListener("mousedown", el.longTapTouchStart);
|
||||||
|
el.removeEventListener("mouseup", el.longTapTouchEnd);
|
||||||
|
el.removeEventListener("mousemove", el.longTapTouchMove);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
Vue.use(navigation, router);
|
Vue.use(navigation, router);
|
||||||
|
|
||||||
new Vue({
|
new Vue({
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue